Ilya Ryzhenkov | 8a4dad4 | 2014-07-11 20:41:53 +0400 | [diff] [blame] | 1 | package org.jetbrains.dokka.tests |
Ilya Ryzhenkov | 044e1b8 | 2014-07-11 17:11:35 +0400 | [diff] [blame] | 2 | |
Dmitry Jemerov | 84ea5c6 | 2015-11-04 16:26:07 +0100 | [diff] [blame] | 3 | import com.google.inject.Guice |
Dmitry Jemerov | 69dd298 | 2014-12-30 18:47:03 +0100 | [diff] [blame] | 4 | import com.intellij.openapi.application.PathManager |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 5 | import com.intellij.openapi.util.Disposer |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 6 | import com.intellij.openapi.util.io.FileUtil |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 7 | import org.jetbrains.dokka.* |
Dmitry Jemerov | 2bd8bdf | 2017-02-22 17:29:39 +0100 | [diff] [blame^] | 8 | import org.jetbrains.dokka.Utilities.DokkaAnalysisModule |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 9 | import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation |
| 10 | import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity |
| 11 | import org.jetbrains.kotlin.cli.common.messages.MessageCollector |
| 12 | import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot |
| 13 | import org.jetbrains.kotlin.config.ContentRoot |
| 14 | import org.jetbrains.kotlin.config.KotlinSourceRoot |
Dmitry Jemerov | 7fbff24 | 2015-01-09 18:54:06 +0100 | [diff] [blame] | 15 | import org.junit.Assert |
Dmitry Jemerov | 74fa763 | 2016-01-25 13:56:01 +0100 | [diff] [blame] | 16 | import org.junit.Assert.fail |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 17 | import java.io.File |
Ilya Ryzhenkov | 044e1b8 | 2014-07-11 17:11:35 +0400 | [diff] [blame] | 18 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 19 | fun 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 Ryzhenkov | 044e1b8 | 2014-07-11 17:11:35 +0400 | [diff] [blame] | 25 | val messageCollector = object : MessageCollector { |
Simon Ogorodnik | c4cf001 | 2016-11-03 19:25:09 +0300 | [diff] [blame] | 26 | override fun clear() { |
| 27 | |
| 28 | } |
| 29 | |
Ilya Ryzhenkov | 044e1b8 | 2014-07-11 17:11:35 +0400 | [diff] [blame] | 30 | 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 Jemerov | 95a606f | 2016-07-08 12:55:26 +0200 | [diff] [blame] | 44 | |
| 45 | override fun hasErrors() = false |
Ilya Ryzhenkov | 044e1b8 | 2014-07-11 17:11:35 +0400 | [diff] [blame] | 46 | } |
| 47 | |
Dmitry Jemerov | 3655b70 | 2015-11-04 14:09:53 +0100 | [diff] [blame] | 48 | val environment = AnalysisEnvironment(messageCollector) |
| 49 | environment.apply { |
Dmitry Jemerov | 47ccfb0 | 2015-10-28 12:15:40 +0100 | [diff] [blame] | 50 | 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 Jemerov | 39fefdc | 2016-05-10 18:06:35 +0200 | [diff] [blame] | 57 | |
| 58 | val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class") |
| 59 | addClasspath(File(kotlinStrictfpRoot)) |
Dmitry Jemerov | 47ccfb0 | 2015-10-28 12:15:40 +0100 | [diff] [blame] | 60 | } |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 61 | addRoots(roots.toList()) |
Ilya Ryzhenkov | 044e1b8 | 2014-07-11 17:11:35 +0400 | [diff] [blame] | 62 | } |
Dmitry Jemerov | 8640e51 | 2016-01-12 19:34:48 +0100 | [diff] [blame] | 63 | val options = DocumentationOptions("", format, |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 64 | includeNonPublic = includeNonPublic, |
Dmitry Jemerov | 8640e51 | 2016-01-12 19:34:48 +0100 | [diff] [blame] | 65 | skipEmptyPackages = false, |
| 66 | sourceLinks = listOf<SourceLinkDefinition>(), |
| 67 | generateIndexPages = false) |
Dmitry Jemerov | 2bd8bdf | 2017-02-22 17:29:39 +0100 | [diff] [blame^] | 68 | val injector = Guice.createInjector(DokkaAnalysisModule(environment, options, DokkaConsoleLogger)) |
| 69 | val documentation = DocumentationModule("test") |
| 70 | buildDocumentationModule(injector, documentation) |
Ilya Ryzhenkov | 08e6900 | 2014-07-14 15:44:32 +0400 | [diff] [blame] | 71 | verifier(documentation) |
Ilya Ryzhenkov | 044e1b8 | 2014-07-11 17:11:35 +0400 | [diff] [blame] | 72 | Disposer.dispose(environment) |
| 73 | } |
| 74 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 75 | fun verifyModel(source: String, |
| 76 | withJdk: Boolean = false, |
| 77 | withKotlinRuntime: Boolean = false, |
| 78 | format: String = "html", |
| 79 | includeNonPublic: Boolean = true, |
| 80 | verifier: (DocumentationModule) -> Unit) { |
Dmitry Jemerov | b643bf6 | 2015-11-05 15:51:23 +0100 | [diff] [blame] | 81 | if (!File(source).exists()) { |
| 82 | throw IllegalArgumentException("Can't find test data file $source") |
| 83 | } |
Dmitry Jemerov | e4b2ae9 | 2015-10-30 18:43:48 +0100 | [diff] [blame] | 84 | verifyModel(contentRootFromPath(source), |
| 85 | withJdk = withJdk, |
| 86 | withKotlinRuntime = withKotlinRuntime, |
Dmitry Jemerov | 84ea5c6 | 2015-11-04 16:26:07 +0100 | [diff] [blame] | 87 | format = format, |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 88 | includeNonPublic = includeNonPublic, |
Dmitry Jemerov | e4b2ae9 | 2015-10-30 18:43:48 +0100 | [diff] [blame] | 89 | verifier = verifier) |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 90 | } |
| 91 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 92 | fun verifyPackageMember(source: String, |
| 93 | withJdk: Boolean = false, |
| 94 | withKotlinRuntime: Boolean = false, |
| 95 | verifier: (DocumentationNode) -> Unit) { |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 96 | verifyModel(source, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime) { model -> |
| 97 | val pkg = model.members.single() |
| 98 | verifier(pkg.members.single()) |
| 99 | } |
| 100 | } |
| 101 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 102 | fun verifyJavaModel(source: String, |
| 103 | withKotlinRuntime: Boolean = false, |
| 104 | verifier: (DocumentationModule) -> Unit) { |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 105 | val tempDir = FileUtil.createTempDirectory("dokka", "") |
| 106 | try { |
| 107 | val sourceFile = File(source) |
| 108 | FileUtil.copy(sourceFile, File(tempDir, sourceFile.name)) |
Dmitry Jemerov | 64946ea | 2015-11-16 19:45:45 +0100 | [diff] [blame] | 109 | verifyModel(JavaSourceRoot(tempDir, null), withJdk = true, withKotlinRuntime = withKotlinRuntime, verifier = verifier) |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 110 | } |
| 111 | finally { |
| 112 | FileUtil.delete(tempDir) |
| 113 | } |
| 114 | } |
| 115 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 116 | fun verifyJavaPackageMember(source: String, |
| 117 | withKotlinRuntime: Boolean = false, |
| 118 | verifier: (DocumentationNode) -> Unit) { |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 119 | verifyJavaModel(source, withKotlinRuntime) { model -> |
Dmitry Jemerov | aa3f051 | 2015-02-13 17:04:58 +0100 | [diff] [blame] | 120 | val pkg = model.members.single() |
| 121 | verifier(pkg.members.single()) |
| 122 | } |
| 123 | } |
| 124 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 125 | fun verifyOutput(roots: Array<ContentRoot>, |
| 126 | outputExtension: String, |
| 127 | withJdk: Boolean = false, |
| 128 | withKotlinRuntime: Boolean = false, |
Simon Ogorodnik | 47790d1 | 2016-11-14 18:08:17 +0300 | [diff] [blame] | 129 | format: String = "html", |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 130 | outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { |
Simon Ogorodnik | 47790d1 | 2016-11-14 18:08:17 +0300 | [diff] [blame] | 131 | verifyModel(*roots, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, format = format) { |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 132 | verifyModelOutput(it, outputExtension, outputGenerator, roots.first().path) |
Dmitry Jemerov | c43a437 | 2014-12-29 20:22:43 +0100 | [diff] [blame] | 133 | } |
| 134 | } |
| 135 | |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 136 | private 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 Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 148 | fun verifyOutput(path: String, |
| 149 | outputExtension: String, |
| 150 | withJdk: Boolean = false, |
| 151 | withKotlinRuntime: Boolean = false, |
Simon Ogorodnik | 47790d1 | 2016-11-14 18:08:17 +0300 | [diff] [blame] | 152 | format: String = "html", |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 153 | outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { |
Simon Ogorodnik | 47790d1 | 2016-11-14 18:08:17 +0300 | [diff] [blame] | 154 | verifyOutput(arrayOf(contentRootFromPath(path)), outputExtension, withJdk, withKotlinRuntime, format, outputGenerator) |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 155 | } |
| 156 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 157 | fun verifyJavaOutput(path: String, |
| 158 | outputExtension: String, |
| 159 | withKotlinRuntime: Boolean = false, |
| 160 | outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { |
Dmitry Jemerov | f724ba6 | 2015-11-03 19:36:48 +0100 | [diff] [blame] | 161 | verifyJavaModel(path, withKotlinRuntime) { model -> |
| 162 | verifyModelOutput(model, outputExtension, outputGenerator, path) |
| 163 | } |
| 164 | } |
| 165 | |
Dmitry Jemerov | b96aaa5 | 2016-06-30 17:36:22 +0200 | [diff] [blame] | 166 | fun assertEqualsIgnoringSeparators(expectedOutput: String, output: String) { |
Dmitry Jemerov | 599f32d | 2015-01-22 11:30:57 +0100 | [diff] [blame] | 167 | Assert.assertEquals(expectedOutput.replace("\r\n", "\n"), output.replace("\r\n", "\n")) |
| 168 | } |
| 169 | |
Dmitry Jemerov | 0d0fc1f | 2015-02-10 18:32:12 +0100 | [diff] [blame] | 170 | fun StringBuilder.appendChildren(node: ContentBlock): StringBuilder { |
Ilya Ryzhenkov | 778e2b3 | 2014-09-29 20:54:59 +0400 | [diff] [blame] | 171 | for (child in node.children) { |
| 172 | val childText = child.toTestString() |
| 173 | append(childText) |
| 174 | } |
| 175 | return this |
| 176 | } |
| 177 | |
Ilya Ryzhenkov | 11355ce | 2014-10-12 22:35:47 +0400 | [diff] [blame] | 178 | fun StringBuilder.appendNode(node: ContentNode): StringBuilder { |
Ilya Ryzhenkov | 778e2b3 | 2014-09-29 20:54:59 +0400 | [diff] [blame] | 179 | when (node) { |
| 180 | is ContentText -> { |
| 181 | append(node.text) |
| 182 | } |
| 183 | is ContentEmphasis -> append("*").appendChildren(node).append("*") |
Ilya Ryzhenkov | 1b5f12b | 2014-12-22 20:01:01 +0300 | [diff] [blame] | 184 | is ContentBlockCode -> { |
Simon Ogorodnik | 7922e2a | 2016-11-01 20:31:16 +0300 | [diff] [blame] | 185 | if (node.language.isNotBlank()) |
| 186 | appendln("[code lang=${node.language}]") |
| 187 | else |
| 188 | appendln("[code]") |
Ilya Ryzhenkov | 1b5f12b | 2014-12-22 20:01:01 +0300 | [diff] [blame] | 189 | appendChildren(node) |
| 190 | appendln() |
| 191 | appendln("[/code]") |
| 192 | } |
Ilya Ryzhenkov | bd6cddd | 2014-12-16 21:41:32 +0300 | [diff] [blame] | 193 | is ContentNodeLink -> { |
| 194 | append("[") |
| 195 | appendChildren(node) |
| 196 | append(" -> ") |
| 197 | append(node.node.toString()) |
| 198 | append("]") |
| 199 | } |
Dmitry Jemerov | 0d0fc1f | 2015-02-10 18:32:12 +0100 | [diff] [blame] | 200 | is ContentBlock -> { |
Ilya Ryzhenkov | 778e2b3 | 2014-09-29 20:54:59 +0400 | [diff] [blame] | 201 | appendChildren(node) |
| 202 | } |
Dmitry Jemerov | ded5696 | 2015-10-30 19:33:29 +0100 | [diff] [blame] | 203 | is ContentEmpty -> { /* nothing */ } |
Dmitry Jemerov | 0d0fc1f | 2015-02-10 18:32:12 +0100 | [diff] [blame] | 204 | else -> throw IllegalStateException("Don't know how to format node $node") |
Ilya Ryzhenkov | 778e2b3 | 2014-09-29 20:54:59 +0400 | [diff] [blame] | 205 | } |
| 206 | return this |
| 207 | } |
| 208 | |
| 209 | fun ContentNode.toTestString(): String { |
| 210 | val node = this |
Ilya Ryzhenkov | a76e5a6 | 2015-10-22 19:18:07 +0300 | [diff] [blame] | 211 | return StringBuilder().apply { |
Ilya Ryzhenkov | 778e2b3 | 2014-09-29 20:54:59 +0400 | [diff] [blame] | 212 | appendNode(node) |
| 213 | }.toString() |
| 214 | } |
Dmitry Jemerov | c43a437 | 2014-12-29 20:22:43 +0100 | [diff] [blame] | 215 | |
Dmitry Jemerov | d9bfa02 | 2015-02-19 18:59:00 +0100 | [diff] [blame] | 216 | class InMemoryLocation(override val path: String): Location { |
Dmitry Jemerov | 85a3ae7 | 2015-02-20 14:08:30 +0100 | [diff] [blame] | 217 | override fun relativePathTo(other: Location, anchor: String?): String = |
| 218 | if (anchor != null) other.path + "#" + anchor else other.path |
Dmitry Jemerov | d9bfa02 | 2015-02-19 18:59:00 +0100 | [diff] [blame] | 219 | } |
Dmitry Jemerov | c43a437 | 2014-12-29 20:22:43 +0100 | [diff] [blame] | 220 | |
| 221 | object InMemoryLocationService: LocationService { |
Dmitry Jemerov | ecadf40 | 2015-02-20 14:44:30 +0100 | [diff] [blame] | 222 | override fun location(qualifiedName: List<String>, hasMembers: Boolean) = |
| 223 | InMemoryLocation(relativePathToNode(qualifiedName, hasMembers)) |
Dmitry Jemerov | 3963105 | 2015-12-03 16:22:11 +0100 | [diff] [blame] | 224 | |
| 225 | override val root: Location |
| 226 | get() = InMemoryLocation("") |
Dmitry Jemerov | c43a437 | 2014-12-29 20:22:43 +0100 | [diff] [blame] | 227 | } |
Dmitry Jemerov | d9bfa02 | 2015-02-19 18:59:00 +0100 | [diff] [blame] | 228 | |
| 229 | val tempLocation = InMemoryLocation("") |
Dmitry Jemerov | bc0654e | 2015-05-27 19:26:13 +0200 | [diff] [blame] | 230 | |
| 231 | val ContentRoot.path: String |
| 232 | get() = when(this) { |
| 233 | is KotlinSourceRoot -> path |
| 234 | is JavaSourceRoot -> file.path |
| 235 | else -> throw UnsupportedOperationException() |
| 236 | } |