blob: 108c5bbf6f6ced29927bf76f922fb3e5341914ee [file] [log] [blame]
Ilya Ryzhenkov8a4dad42014-07-11 20:41:53 +04001package org.jetbrains.dokka.tests
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +04002
Dmitry Jemerov84ea5c62015-11-04 16:26:07 +01003import com.google.inject.Guice
Dmitry Jemerov69dd2982014-12-30 18:47:03 +01004import com.intellij.openapi.application.PathManager
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +02005import com.intellij.openapi.util.Disposer
Dmitry Jemerovf724ba62015-11-03 19:36:48 +01006import com.intellij.openapi.util.io.FileUtil
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +02007import org.jetbrains.dokka.*
Dmitry Jemerov2bd8bdf2017-02-22 17:29:39 +01008import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +02009import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
10import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
11import org.jetbrains.kotlin.cli.common.messages.MessageCollector
12import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
13import org.jetbrains.kotlin.config.ContentRoot
14import org.jetbrains.kotlin.config.KotlinSourceRoot
Dmitry Jemerov7fbff242015-01-09 18:54:06 +010015import org.junit.Assert
Dmitry Jemerov74fa7632016-01-25 13:56:01 +010016import org.junit.Assert.fail
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +020017import java.io.File
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040018
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +020019fun verifyModel(vararg roots: ContentRoot,
20 withJdk: Boolean = false,
21 withKotlinRuntime: Boolean = false,
22 format: String = "html",
23 includeNonPublic: Boolean = true,
24 verifier: (DocumentationModule) -> Unit) {
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040025 val messageCollector = object : MessageCollector {
Simon Ogorodnikc4cf0012016-11-03 19:25:09 +030026 override fun clear() {
27
28 }
29
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040030 override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) {
31 when (severity) {
32 CompilerMessageSeverity.WARNING,
33 CompilerMessageSeverity.LOGGING,
34 CompilerMessageSeverity.OUTPUT,
35 CompilerMessageSeverity.INFO,
36 CompilerMessageSeverity.ERROR -> {
37 println("$severity: $message at $location")
38 }
39 CompilerMessageSeverity.EXCEPTION -> {
40 fail("$severity: $message at $location")
41 }
42 }
43 }
Dmitry Jemerov95a606f2016-07-08 12:55:26 +020044
45 override fun hasErrors() = false
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040046 }
47
Dmitry Jemerov3655b702015-11-04 14:09:53 +010048 val environment = AnalysisEnvironment(messageCollector)
49 environment.apply {
Dmitry Jemerov47ccfb02015-10-28 12:15:40 +010050 if (withJdk || withKotlinRuntime) {
51 val stringRoot = PathManager.getResourceRoot(String::class.java, "/java/lang/String.class")
52 addClasspath(File(stringRoot))
53 }
54 if (withKotlinRuntime) {
55 val kotlinPairRoot = PathManager.getResourceRoot(Pair::class.java, "/kotlin/Pair.class")
56 addClasspath(File(kotlinPairRoot))
Dmitry Jemerov39fefdc2016-05-10 18:06:35 +020057
58 val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class")
59 addClasspath(File(kotlinStrictfpRoot))
Dmitry Jemerov47ccfb02015-10-28 12:15:40 +010060 }
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +020061 addRoots(roots.toList())
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040062 }
Dmitry Jemerov8640e512016-01-12 19:34:48 +010063 val options = DocumentationOptions("", format,
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +020064 includeNonPublic = includeNonPublic,
Dmitry Jemerov8640e512016-01-12 19:34:48 +010065 skipEmptyPackages = false,
66 sourceLinks = listOf<SourceLinkDefinition>(),
67 generateIndexPages = false)
Dmitry Jemerov2bd8bdf2017-02-22 17:29:39 +010068 val injector = Guice.createInjector(DokkaAnalysisModule(environment, options, DokkaConsoleLogger))
69 val documentation = DocumentationModule("test")
70 buildDocumentationModule(injector, documentation)
Ilya Ryzhenkov08e69002014-07-14 15:44:32 +040071 verifier(documentation)
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040072 Disposer.dispose(environment)
73}
74
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +020075fun verifyModel(source: String,
76 withJdk: Boolean = false,
77 withKotlinRuntime: Boolean = false,
78 format: String = "html",
79 includeNonPublic: Boolean = true,
80 verifier: (DocumentationModule) -> Unit) {
Dmitry Jemerovb643bf62015-11-05 15:51:23 +010081 if (!File(source).exists()) {
82 throw IllegalArgumentException("Can't find test data file $source")
83 }
Dmitry Jemerove4b2ae92015-10-30 18:43:48 +010084 verifyModel(contentRootFromPath(source),
85 withJdk = withJdk,
86 withKotlinRuntime = withKotlinRuntime,
Dmitry Jemerov84ea5c62015-11-04 16:26:07 +010087 format = format,
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +020088 includeNonPublic = includeNonPublic,
Dmitry Jemerove4b2ae92015-10-30 18:43:48 +010089 verifier = verifier)
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +020090}
91
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +020092fun verifyPackageMember(source: String,
93 withJdk: Boolean = false,
94 withKotlinRuntime: Boolean = false,
95 verifier: (DocumentationNode) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +010096 verifyModel(source, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime) { model ->
97 val pkg = model.members.single()
98 verifier(pkg.members.single())
99 }
100}
101
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200102fun verifyJavaModel(source: String,
103 withKotlinRuntime: Boolean = false,
104 verifier: (DocumentationModule) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100105 val tempDir = FileUtil.createTempDirectory("dokka", "")
106 try {
107 val sourceFile = File(source)
108 FileUtil.copy(sourceFile, File(tempDir, sourceFile.name))
Dmitry Jemerov64946ea2015-11-16 19:45:45 +0100109 verifyModel(JavaSourceRoot(tempDir, null), withJdk = true, withKotlinRuntime = withKotlinRuntime, verifier = verifier)
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100110 }
111 finally {
112 FileUtil.delete(tempDir)
113 }
114}
115
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200116fun verifyJavaPackageMember(source: String,
117 withKotlinRuntime: Boolean = false,
118 verifier: (DocumentationNode) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100119 verifyJavaModel(source, withKotlinRuntime) { model ->
Dmitry Jemerovaa3f0512015-02-13 17:04:58 +0100120 val pkg = model.members.single()
121 verifier(pkg.members.single())
122 }
123}
124
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200125fun verifyOutput(roots: Array<ContentRoot>,
126 outputExtension: String,
127 withJdk: Boolean = false,
128 withKotlinRuntime: Boolean = false,
Simon Ogorodnik47790d12016-11-14 18:08:17 +0300129 format: String = "html",
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200130 outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
Simon Ogorodnik47790d12016-11-14 18:08:17 +0300131 verifyModel(*roots, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, format = format) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100132 verifyModelOutput(it, outputExtension, outputGenerator, roots.first().path)
Dmitry Jemerovc43a4372014-12-29 20:22:43 +0100133 }
134}
135
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100136private fun verifyModelOutput(it: DocumentationModule,
137 outputExtension: String,
138 outputGenerator: (DocumentationModule, StringBuilder) -> Unit,
139 sourcePath: String) {
140 val output = StringBuilder()
141 outputGenerator(it, output)
142 val ext = outputExtension.removePrefix(".")
143 val path = sourcePath
144 val expectedOutput = File(path.replaceAfterLast(".", ext, path + "." + ext)).readText()
145 assertEqualsIgnoringSeparators(expectedOutput, output.toString())
146}
147
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200148fun verifyOutput(path: String,
149 outputExtension: String,
150 withJdk: Boolean = false,
151 withKotlinRuntime: Boolean = false,
Simon Ogorodnik47790d12016-11-14 18:08:17 +0300152 format: String = "html",
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200153 outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
Simon Ogorodnik47790d12016-11-14 18:08:17 +0300154 verifyOutput(arrayOf(contentRootFromPath(path)), outputExtension, withJdk, withKotlinRuntime, format, outputGenerator)
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +0200155}
156
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200157fun verifyJavaOutput(path: String,
158 outputExtension: String,
159 withKotlinRuntime: Boolean = false,
160 outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100161 verifyJavaModel(path, withKotlinRuntime) { model ->
162 verifyModelOutput(model, outputExtension, outputGenerator, path)
163 }
164}
165
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200166fun assertEqualsIgnoringSeparators(expectedOutput: String, output: String) {
Dmitry Jemerov599f32d2015-01-22 11:30:57 +0100167 Assert.assertEquals(expectedOutput.replace("\r\n", "\n"), output.replace("\r\n", "\n"))
168}
169
Dmitry Jemerov0d0fc1f2015-02-10 18:32:12 +0100170fun StringBuilder.appendChildren(node: ContentBlock): StringBuilder {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400171 for (child in node.children) {
172 val childText = child.toTestString()
173 append(childText)
174 }
175 return this
176}
177
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400178fun StringBuilder.appendNode(node: ContentNode): StringBuilder {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400179 when (node) {
180 is ContentText -> {
181 append(node.text)
182 }
183 is ContentEmphasis -> append("*").appendChildren(node).append("*")
Ilya Ryzhenkov1b5f12b2014-12-22 20:01:01 +0300184 is ContentBlockCode -> {
Simon Ogorodnik7922e2a2016-11-01 20:31:16 +0300185 if (node.language.isNotBlank())
186 appendln("[code lang=${node.language}]")
187 else
188 appendln("[code]")
Ilya Ryzhenkov1b5f12b2014-12-22 20:01:01 +0300189 appendChildren(node)
190 appendln()
191 appendln("[/code]")
192 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300193 is ContentNodeLink -> {
194 append("[")
195 appendChildren(node)
196 append(" -> ")
197 append(node.node.toString())
198 append("]")
199 }
Dmitry Jemerov0d0fc1f2015-02-10 18:32:12 +0100200 is ContentBlock -> {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400201 appendChildren(node)
202 }
Dmitry Jemerovded56962015-10-30 19:33:29 +0100203 is ContentEmpty -> { /* nothing */ }
Dmitry Jemerov0d0fc1f2015-02-10 18:32:12 +0100204 else -> throw IllegalStateException("Don't know how to format node $node")
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400205 }
206 return this
207}
208
209fun ContentNode.toTestString(): String {
210 val node = this
Ilya Ryzhenkova76e5a62015-10-22 19:18:07 +0300211 return StringBuilder().apply {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400212 appendNode(node)
213 }.toString()
214}
Dmitry Jemerovc43a4372014-12-29 20:22:43 +0100215
Dmitry Jemerovd9bfa022015-02-19 18:59:00 +0100216class InMemoryLocation(override val path: String): Location {
Dmitry Jemerov85a3ae72015-02-20 14:08:30 +0100217 override fun relativePathTo(other: Location, anchor: String?): String =
218 if (anchor != null) other.path + "#" + anchor else other.path
Dmitry Jemerovd9bfa022015-02-19 18:59:00 +0100219}
Dmitry Jemerovc43a4372014-12-29 20:22:43 +0100220
221object InMemoryLocationService: LocationService {
Dmitry Jemerovecadf402015-02-20 14:44:30 +0100222 override fun location(qualifiedName: List<String>, hasMembers: Boolean) =
223 InMemoryLocation(relativePathToNode(qualifiedName, hasMembers))
Dmitry Jemerov39631052015-12-03 16:22:11 +0100224
225 override val root: Location
226 get() = InMemoryLocation("")
Dmitry Jemerovc43a4372014-12-29 20:22:43 +0100227}
Dmitry Jemerovd9bfa022015-02-19 18:59:00 +0100228
229val tempLocation = InMemoryLocation("")
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +0200230
231val ContentRoot.path: String
232 get() = when(this) {
233 is KotlinSourceRoot -> path
234 is JavaSourceRoot -> file.path
235 else -> throw UnsupportedOperationException()
236 }