blob: aeff9284ec827b31d04700e5333337bde89e0dde [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
Simon Ogorodnik0fccc3f2017-03-23 16:35:10 +03007import com.intellij.rt.execution.junit.FileComparisonFailure
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +02008import org.jetbrains.dokka.*
Dmitry Jemerov2bd8bdf2017-02-22 17:29:39 +01009import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +020010import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
11import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
12import org.jetbrains.kotlin.cli.common.messages.MessageCollector
13import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
14import org.jetbrains.kotlin.config.ContentRoot
15import org.jetbrains.kotlin.config.KotlinSourceRoot
Dmitry Jemerovedb0d902017-02-24 10:51:22 +010016import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
Dmitry Jemerov7fbff242015-01-09 18:54:06 +010017import org.junit.Assert
Dmitry Jemerov74fa7632016-01-25 13:56:01 +010018import org.junit.Assert.fail
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +020019import java.io.File
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040020
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +020021fun verifyModel(vararg roots: ContentRoot,
22 withJdk: Boolean = false,
23 withKotlinRuntime: Boolean = false,
24 format: String = "html",
25 includeNonPublic: Boolean = true,
Chris Rankinf941d6a2017-10-06 14:14:09 +010026 perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
Simon Ogorodnik9e65c3d2018-01-11 22:22:36 +030027 noStdlibLink: Boolean = true,
Simon Ogorodnikbf2945d2018-01-17 20:01:20 +030028 collectInheritedExtensionsFromLibraries: Boolean = false,
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +020029 verifier: (DocumentationModule) -> Unit) {
Dmitry Jemerova39c7a12017-02-23 15:21:03 +010030 val documentation = DocumentationModule("test")
Dmitry Jemerovff8fdb02017-02-23 19:08:31 +010031
Simon Ogorodnik13c4cb92017-11-27 16:05:59 +030032 val options = DocumentationOptions(
Simon Ogorodnikbf2945d2018-01-17 20:01:20 +030033 "",
34 format,
35 includeNonPublic = includeNonPublic,
36 skipEmptyPackages = false,
37 includeRootPackage = true,
38 sourceLinks = listOf(),
39 perPackageOptions = perPackageOptions,
40 generateIndexPages = false,
41 noStdlibLink = noStdlibLink,
42 cacheRoot = "default",
43 languageVersion = null,
44 apiVersion = null,
45 collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
Simon Ogorodnik13c4cb92017-11-27 16:05:59 +030046 )
Dmitry Jemerovff8fdb02017-02-23 19:08:31 +010047
Dmitry Jemerova39c7a12017-02-23 15:21:03 +010048 appendDocumentation(documentation, *roots,
49 withJdk = withJdk,
50 withKotlinRuntime = withKotlinRuntime,
Dmitry Jemerovff8fdb02017-02-23 19:08:31 +010051 options = options)
52 documentation.prepareForGeneration(options)
53
Dmitry Jemerova39c7a12017-02-23 15:21:03 +010054 verifier(documentation)
55}
56
57fun appendDocumentation(documentation: DocumentationModule,
58 vararg roots: ContentRoot,
59 withJdk: Boolean = false,
60 withKotlinRuntime: Boolean = false,
Dmitry Jemerovff8fdb02017-02-23 19:08:31 +010061 options: DocumentationOptions,
Dmitry Jemerov64a86842017-02-23 17:05:32 +010062 defaultPlatforms: List<String> = emptyList()) {
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040063 val messageCollector = object : MessageCollector {
Simon Ogorodnikc4cf0012016-11-03 19:25:09 +030064 override fun clear() {
65
66 }
67
Simon Ogorodnik4810d072017-06-26 15:25:09 +030068 override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040069 when (severity) {
Simon Ogorodnik4810d072017-06-26 15:25:09 +030070 CompilerMessageSeverity.STRONG_WARNING,
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040071 CompilerMessageSeverity.WARNING,
72 CompilerMessageSeverity.LOGGING,
73 CompilerMessageSeverity.OUTPUT,
74 CompilerMessageSeverity.INFO,
75 CompilerMessageSeverity.ERROR -> {
76 println("$severity: $message at $location")
77 }
78 CompilerMessageSeverity.EXCEPTION -> {
79 fail("$severity: $message at $location")
80 }
81 }
82 }
Dmitry Jemerov95a606f2016-07-08 12:55:26 +020083
84 override fun hasErrors() = false
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +040085 }
86
Dmitry Jemerov3655b702015-11-04 14:09:53 +010087 val environment = AnalysisEnvironment(messageCollector)
88 environment.apply {
Dmitry Jemerov47ccfb02015-10-28 12:15:40 +010089 if (withJdk || withKotlinRuntime) {
90 val stringRoot = PathManager.getResourceRoot(String::class.java, "/java/lang/String.class")
91 addClasspath(File(stringRoot))
92 }
93 if (withKotlinRuntime) {
Dmitry Jemerov39fefdc2016-05-10 18:06:35 +020094 val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class")
95 addClasspath(File(kotlinStrictfpRoot))
Dmitry Jemerov47ccfb02015-10-28 12:15:40 +010096 }
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +020097 addRoots(roots.toList())
Simon Ogorodnik13c4cb92017-11-27 16:05:59 +030098
99 loadLanguageVersionSettings(options.languageVersion, options.apiVersion)
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +0400100 }
Dmitry Jemerovedb0d902017-02-24 10:51:22 +0100101 val defaultPlatformsProvider = object : DefaultPlatformsProvider {
102 override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = defaultPlatforms
103 }
Dmitry Jemerov99689ad2017-02-23 16:35:45 +0100104 val injector = Guice.createInjector(
Dmitry Jemerovedb0d902017-02-24 10:51:22 +0100105 DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentation.nodeRefGraph, DokkaConsoleLogger))
Dmitry Jemerov2bd8bdf2017-02-22 17:29:39 +0100106 buildDocumentationModule(injector, documentation)
Ilya Ryzhenkov044e1b82014-07-11 17:11:35 +0400107 Disposer.dispose(environment)
108}
109
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200110fun verifyModel(source: String,
111 withJdk: Boolean = false,
112 withKotlinRuntime: Boolean = false,
113 format: String = "html",
114 includeNonPublic: Boolean = true,
115 verifier: (DocumentationModule) -> Unit) {
Dmitry Jemerovb643bf62015-11-05 15:51:23 +0100116 if (!File(source).exists()) {
117 throw IllegalArgumentException("Can't find test data file $source")
118 }
Dmitry Jemerove4b2ae92015-10-30 18:43:48 +0100119 verifyModel(contentRootFromPath(source),
120 withJdk = withJdk,
121 withKotlinRuntime = withKotlinRuntime,
Dmitry Jemerov84ea5c62015-11-04 16:26:07 +0100122 format = format,
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200123 includeNonPublic = includeNonPublic,
Dmitry Jemerove4b2ae92015-10-30 18:43:48 +0100124 verifier = verifier)
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +0200125}
126
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200127fun verifyPackageMember(source: String,
128 withJdk: Boolean = false,
129 withKotlinRuntime: Boolean = false,
130 verifier: (DocumentationNode) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100131 verifyModel(source, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime) { model ->
132 val pkg = model.members.single()
133 verifier(pkg.members.single())
134 }
135}
136
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200137fun verifyJavaModel(source: String,
138 withKotlinRuntime: Boolean = false,
139 verifier: (DocumentationModule) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100140 val tempDir = FileUtil.createTempDirectory("dokka", "")
141 try {
142 val sourceFile = File(source)
143 FileUtil.copy(sourceFile, File(tempDir, sourceFile.name))
Dmitry Jemerov64946ea2015-11-16 19:45:45 +0100144 verifyModel(JavaSourceRoot(tempDir, null), withJdk = true, withKotlinRuntime = withKotlinRuntime, verifier = verifier)
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100145 }
146 finally {
147 FileUtil.delete(tempDir)
148 }
149}
150
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200151fun verifyJavaPackageMember(source: String,
152 withKotlinRuntime: Boolean = false,
153 verifier: (DocumentationNode) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100154 verifyJavaModel(source, withKotlinRuntime) { model ->
Dmitry Jemerovaa3f0512015-02-13 17:04:58 +0100155 val pkg = model.members.single()
156 verifier(pkg.members.single())
157 }
158}
159
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200160fun verifyOutput(roots: Array<ContentRoot>,
161 outputExtension: String,
162 withJdk: Boolean = false,
163 withKotlinRuntime: Boolean = false,
Simon Ogorodnik47790d12016-11-14 18:08:17 +0300164 format: String = "html",
Simon Ogorodnik46af59c2017-11-21 18:58:06 +0300165 includeNonPublic: Boolean = true,
Simon Ogorodnik9e65c3d2018-01-11 22:22:36 +0300166 noStdlibLink: Boolean = true,
Simon Ogorodnikbf2945d2018-01-17 20:01:20 +0300167 collectInheritedExtensionsFromLibraries: Boolean = false,
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200168 outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
Simon Ogorodnik46af59c2017-11-21 18:58:06 +0300169 verifyModel(
Simon Ogorodnik9e65c3d2018-01-11 22:22:36 +0300170 *roots,
171 withJdk = withJdk,
172 withKotlinRuntime = withKotlinRuntime,
173 format = format,
174 includeNonPublic = includeNonPublic,
Simon Ogorodnikbf2945d2018-01-17 20:01:20 +0300175 noStdlibLink = noStdlibLink,
176 collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
Simon Ogorodnik46af59c2017-11-21 18:58:06 +0300177 ) {
Dmitry Jemerova39c7a12017-02-23 15:21:03 +0100178 verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
Dmitry Jemerovc43a4372014-12-29 20:22:43 +0100179 }
180}
181
Dmitry Jemerova39c7a12017-02-23 15:21:03 +0100182fun verifyModelOutput(it: DocumentationModule,
183 outputExtension: String,
184 sourcePath: String,
185 outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100186 val output = StringBuilder()
187 outputGenerator(it, output)
188 val ext = outputExtension.removePrefix(".")
Simon Ogorodnik0fccc3f2017-03-23 16:35:10 +0300189 val expectedFile = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext))
190 assertEqualsIgnoringSeparators(expectedFile, output.toString())
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100191}
192
Simon Ogorodnik9e65c3d2018-01-11 22:22:36 +0300193fun verifyOutput(
194 path: String,
195 outputExtension: String,
196 withJdk: Boolean = false,
197 withKotlinRuntime: Boolean = false,
198 format: String = "html",
199 includeNonPublic: Boolean = true,
200 noStdlibLink: Boolean = true,
Simon Ogorodnikbf2945d2018-01-17 20:01:20 +0300201 collectInheritedExtensionsFromLibraries: Boolean = false,
Simon Ogorodnik9e65c3d2018-01-11 22:22:36 +0300202 outputGenerator: (DocumentationModule, StringBuilder) -> Unit
203) {
Simon Ogorodnik46af59c2017-11-21 18:58:06 +0300204 verifyOutput(
Simon Ogorodnik9e65c3d2018-01-11 22:22:36 +0300205 arrayOf(contentRootFromPath(path)),
206 outputExtension,
207 withJdk,
208 withKotlinRuntime,
209 format,
210 includeNonPublic,
211 noStdlibLink,
Simon Ogorodnikbf2945d2018-01-17 20:01:20 +0300212 collectInheritedExtensionsFromLibraries,
Simon Ogorodnik9e65c3d2018-01-11 22:22:36 +0300213 outputGenerator
Simon Ogorodnik46af59c2017-11-21 18:58:06 +0300214 )
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +0200215}
216
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200217fun verifyJavaOutput(path: String,
218 outputExtension: String,
219 withKotlinRuntime: Boolean = false,
220 outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100221 verifyJavaModel(path, withKotlinRuntime) { model ->
Dmitry Jemerova39c7a12017-02-23 15:21:03 +0100222 verifyModelOutput(model, outputExtension, path, outputGenerator)
Dmitry Jemerovf724ba62015-11-03 19:36:48 +0100223 }
224}
225
Simon Ogorodnik0fccc3f2017-03-23 16:35:10 +0300226fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) {
Simon Ogorodnikfff865c2017-06-05 18:28:49 +0300227 if (!expectedFile.exists()) expectedFile.createNewFile()
Simon Ogorodnik0fccc3f2017-03-23 16:35:10 +0300228 val expectedText = expectedFile.readText().replace("\r\n", "\n")
229 val actualText = output.replace("\r\n", "\n")
230
231 if(expectedText != actualText)
232 throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath)
233}
234
Dmitry Jemerovb96aaa52016-06-30 17:36:22 +0200235fun assertEqualsIgnoringSeparators(expectedOutput: String, output: String) {
Dmitry Jemerov599f32d2015-01-22 11:30:57 +0100236 Assert.assertEquals(expectedOutput.replace("\r\n", "\n"), output.replace("\r\n", "\n"))
237}
238
Dmitry Jemerov0d0fc1f2015-02-10 18:32:12 +0100239fun StringBuilder.appendChildren(node: ContentBlock): StringBuilder {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400240 for (child in node.children) {
241 val childText = child.toTestString()
242 append(childText)
243 }
244 return this
245}
246
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400247fun StringBuilder.appendNode(node: ContentNode): StringBuilder {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400248 when (node) {
249 is ContentText -> {
250 append(node.text)
251 }
252 is ContentEmphasis -> append("*").appendChildren(node).append("*")
Ilya Ryzhenkov1b5f12b2014-12-22 20:01:01 +0300253 is ContentBlockCode -> {
Simon Ogorodnik7922e2a2016-11-01 20:31:16 +0300254 if (node.language.isNotBlank())
255 appendln("[code lang=${node.language}]")
256 else
257 appendln("[code]")
Ilya Ryzhenkov1b5f12b2014-12-22 20:01:01 +0300258 appendChildren(node)
259 appendln()
260 appendln("[/code]")
261 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300262 is ContentNodeLink -> {
263 append("[")
264 appendChildren(node)
265 append(" -> ")
266 append(node.node.toString())
267 append("]")
268 }
Dmitry Jemerov0d0fc1f2015-02-10 18:32:12 +0100269 is ContentBlock -> {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400270 appendChildren(node)
271 }
Dmitry Jemerovded56962015-10-30 19:33:29 +0100272 is ContentEmpty -> { /* nothing */ }
Dmitry Jemerov0d0fc1f2015-02-10 18:32:12 +0100273 else -> throw IllegalStateException("Don't know how to format node $node")
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400274 }
275 return this
276}
277
278fun ContentNode.toTestString(): String {
279 val node = this
Ilya Ryzhenkova76e5a62015-10-22 19:18:07 +0300280 return StringBuilder().apply {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +0400281 appendNode(node)
282 }.toString()
283}
Dmitry Jemerovc43a4372014-12-29 20:22:43 +0100284
Dmitry Jemerovbc0654e2015-05-27 19:26:13 +0200285val ContentRoot.path: String
286 get() = when(this) {
287 is KotlinSourceRoot -> path
288 is JavaSourceRoot -> file.path
289 else -> throw UnsupportedOperationException()
290 }