Skip to content

Commit 1c86072

Browse files
authored
Merge pull request #1612 from Gedochao/tweak-sclicheck
Improve docs coverage with `sclicheck`
2 parents c96750b + bd9f46a commit 1c86072

File tree

7 files changed

+127
-77
lines changed

7 files changed

+127
-77
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,13 @@ To just check the documents, run:
197197
./mill -i docs-tests.test 'sclicheck.DocTests.*'
198198
```
199199

200-
You can also check all commands, guides or cookbooks:
200+
You can also check all root docs, commands, reference docs, guides or cookbooks:
201201
```bash
202+
./mill -i docs-tests.test 'sclicheck.DocTests.root*'
202203
./mill -i docs-tests.test 'sclicheck.DocTests.guide*'
203204
./mill -i docs-tests.test 'sclicheck.DocTests.command*'
204205
./mill -i docs-tests.test 'sclicheck.DocTests.cookbook*'
206+
./mill -i docs-tests.test 'sclicheck.DocTests.reference*'
205207
```
206208

207209
Similarly, you can check single files:

modules/docs-tests/src/main/scala/sclicheck/sclicheck.scala

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ import fansi.Color.{Blue, Green, Red}
55
import java.io.File
66
import java.security.SecureRandom
77

8+
import scala.annotation.tailrec
89
import scala.io.StdIn.readLine
910
import scala.util.Random
1011
import scala.util.matching.Regex
1112

12-
val SnippetBlock = """ *```[^ ]+ title=([\w\d\.\-\/_]+) *""".r
13-
val CompileBlock = """ *``` *(\w+) +(compile|fail) *(?:title=([\w\d\.\-\/_]+))? *""".r
14-
val CodeBlockEnds = """ *``` *""".r
15-
val BashCommand = """ *```bash *(fail)? *""".r
16-
val CheckBlock = """ *\<\!-- Expected(-regex)?: *""".r
17-
val CheckBlockEnd = """ *\--> *""".r
18-
val Clear = """ *<!--+ *clear *-+-> *""".r
13+
val SnippetBlock = """ *(`{2}`+)[^ ]+ title=([\w\d.\-/_]+) *""".r
14+
val CompileBlock = """ *(`{2}`+) *(\w+) +(compile|fail) *(?:title=([\w\d.\-/_]+))? *""".r
15+
def compileBlockEnds(backticks: String) = s""" *$backticks *""".r
16+
val BashCommand = """ *```bash *(fail)? *""".r
17+
val CheckBlock = """ *\<\!-- Expected(-regex)?: *""".r
18+
val CheckBlockEnd = """ *\--> *""".r
19+
val Clear = """ *<!--+ *clear *-+-> *""".r
1920

2021
case class Options(
2122
scalaCliCommand: Seq[String],
@@ -28,8 +29,8 @@ case class Options(
2829

2930
enum Commands:
3031
def context: Context
31-
def name = toString.takeWhile(_ != '(')
32-
def log = this match {
32+
def name: String = toString.takeWhile(_ != '(')
33+
def log: Any = this match {
3334
case _: Clear => ""
3435
case Check(patterns, regex, _) =>
3536
val kind = if regex then "regexes" else "patterns"
@@ -51,13 +52,13 @@ enum Commands:
5152
case Clear(context: Context)
5253

5354
case class Context(file: os.RelPath, line: Int):
54-
def proceed(linesToSkip: Int = 1) = copy(line = line + linesToSkip)
55-
override def toString = s"$file:$line"
55+
def proceed(linesToSkip: Int = 1): Context = copy(line = line + linesToSkip)
56+
override def toString = s"$file:$line"
5657

5758
case class FailedCheck(line: Int, file: os.RelPath, txt: String)
5859
extends RuntimeException(s"[$file:$line] $txt")
5960

60-
def check(cond: Boolean, msg: => String)(using c: Context) =
61+
def check(cond: Boolean, msg: => String)(using c: Context): Unit =
6162
if !cond then throw FailedCheck(c.line, c.file, msg)
6263

6364
@annotation.tailrec
@@ -67,7 +68,7 @@ def parse(content: Seq[String], currentCommands: Seq[Commands], context: Context
6768
inline def parseMultiline(
6869
lines: Seq[String],
6970
newCommand: Seq[String] => Commands,
70-
endMarker: Regex = CodeBlockEnds
71+
endMarker: Regex = compileBlockEnds("```")
7172
) =
7273
val codeLines = lines.takeWhile(l => !endMarker.matches(l))
7374
check(codeLines.size > 0, "Block cannot be empty!")
@@ -81,12 +82,17 @@ def parse(content: Seq[String], currentCommands: Seq[Commands], context: Context
8182
content match
8283
case Nil => currentCommands
8384

84-
case SnippetBlock(name) :: tail =>
85-
parseMultiline(tail, Commands.Write(name, _, context))
85+
case SnippetBlock(backticks, name) :: tail =>
86+
parseMultiline(tail, Commands.Write(name, _, context), compileBlockEnds(backticks))
8687

87-
case CompileBlock(name, status, fileName) :: tail =>
88-
val file = Option(fileName).getOrElse("snippet_" + Random.nextInt(1000) + "." + name)
89-
parseMultiline(tail, Commands.Compile(file, _, context, status == "fail"))
88+
case CompileBlock(backticks, name, status, fileName) :: tail =>
89+
val fileSuffix = if name == "markdown" then ".md" else s".$name"
90+
val file = Option(fileName).getOrElse("snippet_" + Random.nextInt(1000) + fileSuffix)
91+
parseMultiline(
92+
tail,
93+
Commands.Compile(file, _, context, status == "fail"),
94+
compileBlockEnds(backticks)
95+
)
9096

9197
case BashCommand(failGroup) :: tail =>
9298
parseMultiline(tail, Commands.Run(_, failGroup != null, context))
@@ -192,10 +198,10 @@ def checkFile(file: os.Path, options: Options): Unit =
192198
var lastOutput: String = null
193199
val allSources = Set.newBuilder[os.Path]
194200

195-
def runCommand(cmd: Commands, log: String => Unit) =
201+
def runCommand(cmd: Commands, log: String => Unit): Unit =
196202
given Context = cmd.context
197203

198-
def writeFile(file: os.Path, code: Seq[String], c: Context) =
204+
def writeFile(file: os.Path, code: Seq[String], c: Context): Unit =
199205
val (prefixLines, codeLines) =
200206
code match
201207
case shbang :: tail if shbang.startsWith("#!") =>
@@ -249,7 +255,7 @@ def checkFile(file: os.Path, options: Options): Unit =
249255
else
250256
check(exitCode == 0, s"Compilation failed.")
251257

252-
case Commands.Check(patterns, regex, line) =>
258+
case Commands.Check(patterns, regex, _) =>
253259
check(lastOutput != null, "No output stored from previous commands")
254260
val lines = lastOutput.linesIterator.toList
255261

@@ -277,7 +283,7 @@ def checkFile(file: os.Path, options: Options): Unit =
277283
commands.foreach { cmd =>
278284
val logs = List.newBuilder[String]
279285

280-
def printResult(success: Boolean, startTime: Long) =
286+
def printResult(success: Boolean, startTime: Long): Unit =
281287
val duration = System.currentTimeMillis - startTime
282288
val commandName = s"[${cmd.name} in $duration ms]"
283289
val cmdLog =
@@ -336,8 +342,8 @@ def checkFile(file: os.Path, options: Options): Unit =
336342

337343
os.list(out).filter(_.last.endsWith(".scala")).foreach(p => os.copy.into(p, exampleDir))
338344

339-
@main def check(args: String*) =
340-
def processFiles(options: Options) =
345+
@main def check(args: String*): Unit =
346+
def processFiles(options: Options): Unit =
341347
val paths = options.files.map { str =>
342348
val path = os.Path(str, os.pwd)
343349
assert(os.exists(path), s"Provided path $str does not exists in ${os.pwd}")
@@ -377,6 +383,7 @@ def checkFile(file: os.Path, options: Options): Unit =
377383
val Dest = PathParameter("--dest")
378384
val StatusFile = PathParameter("--status-file")
379385

386+
@tailrec
380387
def parseArgs(args: Seq[String], options: Options): Options = args match
381388
case Nil => options
382389
case "--step" :: rest =>

modules/docs-tests/src/test/scala/sclicheck/DocTests.scala

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
package sclicheck
22

33
class DocTests extends munit.FunSuite {
4+
case class DocTestEntry(name: String, path: os.Path, depth: Int = Int.MaxValue)
5+
46
val docsRootPath: os.Path = os.pwd / "website" / "docs"
5-
val dirs: Seq[(String, os.Path)] = Seq(
6-
"cookbook" -> docsRootPath / "cookbooks",
7-
"command" -> docsRootPath / "commands",
8-
"guide" -> docsRootPath / "guides"
7+
val entries: Seq[DocTestEntry] = Seq(
8+
DocTestEntry("root", docsRootPath, depth = 1),
9+
DocTestEntry("cookbook", docsRootPath / "cookbooks"),
10+
DocTestEntry("command", docsRootPath / "commands"),
11+
DocTestEntry("guide", docsRootPath / "guides"),
12+
DocTestEntry("reference", docsRootPath / "reference")
913
)
1014

1115
val options: Options = Options(scalaCliCommand = Seq(TestUtil.scalaCliPath))
1216

13-
private def containsCheck(f: os.Path): Boolean =
14-
os.read.lines(f)
15-
.exists(line => line.startsWith("```md") || line.startsWith("```bash"))
17+
private def lineContainsAnyChecks(l: String): Boolean =
18+
l.startsWith("```md") || l.startsWith("```bash") ||
19+
l.startsWith("```scala compile") || l.startsWith("```scala fail") ||
20+
l.startsWith("````markdown compile") || l.startsWith("````markdown fail") ||
21+
l.startsWith("```java compile") || l.startsWith("````java fail")
22+
private def fileContainsAnyChecks(f: os.Path): Boolean =
23+
os.read.lines(f).exists(lineContainsAnyChecks)
1624

1725
for {
18-
(tpe, dir) <- dirs
19-
inputs = os.walk(dir)
26+
DocTestEntry(tpe, dir, depth) <- entries
27+
inputs = os.walk(dir, maxDepth = depth)
2028
.filter(_.last.endsWith(".md"))
2129
.filter(os.isFile(_))
22-
.filter(containsCheck)
30+
.filter(fileContainsAnyChecks)
2331
.map(_.relativeTo(dir))
2432
.sortBy(_.toString)
2533
md <- inputs

modules/docs-tests/src/test/scala/sclicheck/SclicheckTests.scala

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,26 @@ package sclicheck
22

33
class SclicheckTests extends munit.FunSuite:
44
test("Run regex") {
5+
assert(clue(CompileBlock.unapplySeq("``scala compile")).isEmpty)
56
assert(
6-
Some(Seq("scala", "compile", null)) == clue(CompileBlock.unapplySeq("```scala compile"))
7+
clue(CompileBlock.unapplySeq("```scala compile"))
8+
.contains(Seq("```", "scala", "compile", null))
79
)
810
assert(
9-
Some(Seq("scala", "fail", null)) == clue(CompileBlock.unapplySeq("```scala fail"))
11+
clue(CompileBlock.unapplySeq("```scala fail"))
12+
.contains(Seq("```", "scala", "fail", null))
1013
)
1114
assert(
12-
Some(Seq("scala", "fail", "a.sc")) == clue(
13-
CompileBlock.unapplySeq("```scala fail title=a.sc")
14-
)
15+
clue(CompileBlock.unapplySeq("````markdown compile"))
16+
.contains(Seq("````", "markdown", "compile", null))
17+
)
18+
assert(
19+
clue(CompileBlock.unapplySeq("````markdown fail title=a.md"))
20+
.contains(Seq("````", "markdown", "fail", "a.md"))
21+
)
22+
assert(clue(CompileBlock.unapplySeq("``scala fail title=a.sc")).isEmpty)
23+
assert(
24+
clue(CompileBlock.unapplySeq("```scala fail title=a.sc"))
25+
.contains(Seq("```", "scala", "fail", "a.sc"))
1526
)
1627
}

0 commit comments

Comments
 (0)