Skip to content

Commit 9a47067

Browse files
authored
Merge pull request #10170 from Kordyjan/scala3doc/tests-refactor
Cleaning up tests in scala3doc
2 parents 24f018d + a5c9ce9 commit 9a47067

File tree

9 files changed

+336
-321
lines changed

9 files changed

+336
-321
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package tests
2+
3+
package complexNames
4+
5+
abstract class A:
6+
def ++(other: A): A
7+
def +:(other: Int): A
8+
def :+(other: Int): A
9+
10+
// scala3doc has problems with names in backticks
11+
// def `multi word name`: Int
12+
// def `*** name with arbitrary chars ^%`: Int
13+
// def `mischievous(param:Int)`(otherParam: Int): String
14+
// def withMischievousParams(`param: String, param2`: String): String
15+
16+
def complexName_^*(param: String): A
17+
18+
def `completelyUnnecessaryBackticks`: Int //expected: def completelyUnnecessaryBackticks: Int
19+
def `+++:`(other: Int): A //expected: def +++:(other: Int): A
20+
def `:+++`(other: Int): A //expected: def :+++(other: Int): A
21+
22+
def `abc_^^_&&`: A //expected: def abc_^^_&&: A
23+
def `abc_def`: A //expected: def abc_def: A
24+
def `abc_def_++`: A //expected: def abc_def_++: A
25+
// def `++_abc`: A
26+
// def `abc_++_--`: A

scala3doc/README.md

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,36 @@ You can also find the result of building the same sites for latest `master` at:
7272

7373
### Testing
7474

75-
Most tests rely on comparing signatures (of classes, methods, objects etc.) extracted from the generated documentation
76-
to signatures found in source files. Such tests are defined using [MultipleFileTest](src/test/scala/dotty/dokka/MultipleFileTest.scala) class
77-
and its subtypes (such as [SingleFileTest](src/test/scala/dotty/dokka/SingleFileTest.scala))
78-
79-
WARNING: As the classes mentioned above are likely to evolve, the description below might easily get out of date.
80-
In case of any discrepancies rely on the source files instead.
81-
82-
`MultipleFileTest` requires that you specify the names of the files used to extract signatures,
83-
the names of directories containing corresponding TASTY files
84-
and the kinds of signatures from source files (corresponding to keywords used to declare them like `def`, `class`, `object` etc.)
85-
whose presence in the generated documentation will be checked (other signatures, when missing, will be ignored).
86-
The mentioned source files should be located directly inside `src/main/scala/tests` directory
87-
but the file names passed as parameters should contain neither this path prefix nor `.scala` suffix.
88-
The TASTY folders are expected to be located in `target/${dottyVersion}/classes/tests` (after successful compilation of the project)
89-
and similarly only their names relative to this path should be provided as tests' parameters.
90-
For `SingleFileTest` the name of the source file and the TASTY folder are expected to be the same.
75+
To implement integration tests that inspects state of the model on different stages of
76+
documentation generation one can use [ScaladocTest](src/test/scala/dotty/dokka/ScaladocTest.scala#L15).
77+
This class requires providing the name of the test and a the list of assertions.
78+
79+
Name of the test is used to extract symbols that should be included in given test run from
80+
the TASTY files. All test data is located in [scala3doc-testcases module](../scala3doc-testcases/src).
81+
It is compiled together with the rest of modules. This solves lots of potential problems with
82+
invoking the compiler in a separate process such as mismatching versions. Every test is using
83+
only symbols defined in the package with the same name as the test located inside top level `tests`
84+
package (including subpackages). This restriction may be relaxed in the future.
85+
86+
Assertions of each test are defined as list of [Assertion enum](src/test/scala/dotty/dokka/ScaladocTest.scala#L63)
87+
instances. Each of them represents a callback that is invoked in corresponding phase of
88+
documentation generation. Those callback can inspect the model, log information and raise errors
89+
using `reportError` method.
90+
91+
#### Signature tests
92+
93+
Some of the tests rely on comparing signatures (of classes, methods, objects etc.) extracted from
94+
the generated documentation to signatures found in source files. Such tests are defined using
95+
[SignatureTest](src/test/scala/dotty/dokka/SignatureTest.scala#L16) class, that is a subclass of
96+
`ScaladocTest` and uses exactly tha same mechanism to find input symbols in the TASTY files.
97+
98+
Signature tests by default assume that sources that were used to generate used TASTY files are
99+
located in the file with the same name as the name of the test. If this is not the case optional
100+
parameter `sourceFiles` can be used to pass list of source file names (without `.scala` extension).
101+
102+
Those tests also requires specifying kinds of ignatures from source files (corresponding to
103+
keywords used to declare them like `def`, `class`, `object` etc.) whose presence in the generated
104+
documentation will be checked (other signatures, when missing, will be ignored).
91105

92106
By default it's expected that all signatures from the source files will be present in the documentation
93107
but not vice versa (because the documentation can contain also inherited signatures).

scala3doc/test/dotty/dokka/DottyTestRunner.scala

Lines changed: 0 additions & 126 deletions
This file was deleted.

scala3doc/test/dotty/dokka/MultipleFileTest.scala

Lines changed: 0 additions & 92 deletions
This file was deleted.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package dotty.dokka
2+
3+
import org.jetbrains.dokka.plugability.DokkaContext
4+
import org.jetbrains.dokka.model.{DModule, WithChildren}
5+
import org.jetbrains.dokka.pages.RootPageNode
6+
import org.jetbrains.dokka.testApi.testRunner.{DokkaTestGenerator, TestMethods}
7+
import org.jetbrains.dokka.testApi.logger.TestLogger
8+
import org.jetbrains.dokka.utilities.DokkaConsoleLogger
9+
import org.jetbrains.dokka.DokkaConfiguration
10+
import scala.jdk.CollectionConverters.{ListHasAsScala, SeqHasAsJava}
11+
import org.junit.{Test, Rule}
12+
import org.junit.rules.{TemporaryFolder, ErrorCollector}
13+
import java.io.File
14+
15+
abstract class ScaladocTest(val name: String):
16+
def assertions: Seq[Assertion]
17+
18+
private def getTempDir() : TemporaryFolder =
19+
val folder = new TemporaryFolder()
20+
folder.create()
21+
folder
22+
23+
private def args = Args(
24+
name = "test",
25+
tastyRoots = Nil ,
26+
classpath = System.getProperty("java.class.path"),
27+
None,
28+
output = getTempDir().getRoot,
29+
projectVersion = "1.0",
30+
projectTitle = None,
31+
projectLogo = None,
32+
defaultSyntax = None,
33+
sourceLinks = Nil
34+
)
35+
36+
private def tastyFiles =
37+
def listFilesSafe(dir: File) = Option(dir.listFiles).getOrElse {
38+
throw AssertionError(s"$dir not found. The test name is incorrect or scala3doc-testcases were not recompiled.")
39+
}
40+
def collectFiles(dir: File): List[String] = listFilesSafe(dir).toList.flatMap {
41+
case f if f.isDirectory => collectFiles(f)
42+
case f if f.getName endsWith ".tasty" => f.getAbsolutePath :: Nil
43+
case _ => Nil
44+
}
45+
collectFiles(File(s"${BuildInfo.test_testcasesOutputDir}/tests/$name"))
46+
47+
@Rule
48+
def collector = _collector
49+
private val _collector = new ErrorCollector();
50+
def reportError(msg: String) = collector.addError(new AssertionError(msg))
51+
52+
@Test
53+
def executeTest =
54+
DokkaTestGenerator(
55+
DottyDokkaConfig(DocConfiguration.Standalone(args, tastyFiles, Nil)),
56+
TestLogger(DokkaConsoleLogger.INSTANCE),
57+
assertions.asTestMethods,
58+
Nil.asJava
59+
).generate()
60+
61+
end ScaladocTest
62+
63+
type Validator = () => Unit
64+
65+
/**
66+
* Those assertions map 1-1 to their dokka counterparts. Some of them may be irrelevant in scala3doc.
67+
*/
68+
enum Assertion:
69+
case AfterPluginSetup(fn: DokkaContext => Unit)
70+
case DuringValidation(fn: Validator => Unit)
71+
case AfterDocumentablesCreation(fn: Seq[DModule] => Unit)
72+
case AfterPreMergeDocumentablesTransformation(fn: Seq[DModule] => Unit)
73+
case AfterDocumentablesMerge(fn: DModule => Unit)
74+
case AfterDocumentablesTransformation(fn: DModule => Unit)
75+
case AfterPagesGeneration(fn: RootPageNode => Unit)
76+
case AfterPagesTransformation(fn: RootPageNode => Unit)
77+
case AfterRendering(fn: (RootPageNode, DokkaContext) => Unit)
78+
79+
extension (s: Seq[Assertion]):
80+
def asTestMethods: TestMethods =
81+
import Assertion._
82+
TestMethods(
83+
(context => s.collect { case AfterPluginSetup(fn) => fn(context) }.kUnit),
84+
(validator => s.collect { case DuringValidation(fn) => fn(() => validator.invoke()) }.kUnit),
85+
(modules => s.collect { case AfterDocumentablesCreation(fn) => fn(modules.asScala.toSeq) }.kUnit),
86+
(modules => s.collect { case AfterPreMergeDocumentablesTransformation(fn) => fn(modules.asScala.toSeq) }.kUnit),
87+
(module => s.collect { case AfterDocumentablesMerge(fn) => fn(module)}.kUnit),
88+
(module => s.collect { case AfterDocumentablesTransformation(fn) => fn(module) }.kUnit),
89+
(root => s.collect { case AfterPagesGeneration(fn) => fn(root) }.kUnit),
90+
(root => s.collect { case AfterPagesTransformation(fn) => fn(root) }.kUnit),
91+
((root, context) => s.collect { case AfterRendering(fn) => fn(root, context)}.kUnit)
92+
)
93+
94+
extension [T] (s: T):
95+
private def kUnit = kotlin.Unit.INSTANCE

0 commit comments

Comments
 (0)