From 5632167f6cacec16d82b1961d233d7e761cf6036 Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Tue, 27 Mar 2012 10:35:40 +0200 Subject: Fixes SI-5373 And adds basic support for scaladoc model tests (class partest.ScaladocModelTest) --- build.xml | 3 +- src/compiler/scala/tools/nsc/doc/DocFactory.scala | 18 +- .../scala/tools/nsc/doc/html/page/Template.scala | 2 +- .../scala/tools/nsc/doc/model/ModelFactory.scala | 5 +- .../scala/tools/partest/ScaladocModelTest.scala | 124 ++++ test/scaladoc/run/SI-5373.check | 1 + test/scaladoc/run/SI-5373.scala | 34 + test/scaladoc/scala/IndexScriptTest.scala | 52 -- test/scaladoc/scala/IndexTest.scala | 82 --- test/scaladoc/scala/html.flags | 1 - test/scaladoc/scala/html/HtmlFactoryTest.flags | 1 - test/scaladoc/scala/html/HtmlFactoryTest.scala | 730 --------------------- test/scaladoc/scala/model/CommentFactoryTest.scala | 155 ----- test/scaladoc/scalacheck/CommentFactoryTest.scala | 155 +++++ test/scaladoc/scalacheck/HtmlFactoryTest.flags | 1 + test/scaladoc/scalacheck/HtmlFactoryTest.scala | 730 +++++++++++++++++++++ test/scaladoc/scalacheck/IndexScriptTest.scala | 52 ++ test/scaladoc/scalacheck/IndexTest.scala | 82 +++ 18 files changed, 1198 insertions(+), 1030 deletions(-) create mode 100644 src/partest/scala/tools/partest/ScaladocModelTest.scala create mode 100644 test/scaladoc/run/SI-5373.check create mode 100644 test/scaladoc/run/SI-5373.scala delete mode 100644 test/scaladoc/scala/IndexScriptTest.scala delete mode 100644 test/scaladoc/scala/IndexTest.scala delete mode 100644 test/scaladoc/scala/html.flags delete mode 100644 test/scaladoc/scala/html/HtmlFactoryTest.flags delete mode 100644 test/scaladoc/scala/html/HtmlFactoryTest.scala delete mode 100644 test/scaladoc/scala/model/CommentFactoryTest.scala create mode 100644 test/scaladoc/scalacheck/CommentFactoryTest.scala create mode 100644 test/scaladoc/scalacheck/HtmlFactoryTest.flags create mode 100644 test/scaladoc/scalacheck/HtmlFactoryTest.scala create mode 100644 test/scaladoc/scalacheck/IndexScriptTest.scala create mode 100644 test/scaladoc/scalacheck/IndexTest.scala diff --git a/build.xml b/build.xml index 2f655c2077..b5fead5bec 100644 --- a/build.xml +++ b/build.xml @@ -1916,7 +1916,8 @@ BOOTRAPING TEST AND TEST SUITE - + + diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index 9a025b0d14..f32564f097 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -8,7 +8,7 @@ package doc import scala.util.control.ControlThrowable import reporters.Reporter -import util.NoPosition +import util.{ NoPosition, BatchSourceFile} import io.{ File, Directory } import DocParser.Parsed @@ -46,13 +46,19 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor override def forScaladoc = true } - /** Creates a scaladoc site for all symbols defined in this call's `files`, - * as well as those defined in `files` of previous calls to the same processor. + /** Creates a scaladoc site for all symbols defined in this call's `source`, + * as well as those defined in `sources` of previous calls to the same processor. * @param files The list of paths (relative to the compiler's source path, * or absolute) of files to document. */ - def makeUniverse(files: List[String]): Option[Universe] = { + def makeUniverse(source: Either[List[String], String]): Option[Universe] = { assert(settings.docformat.value == "html") - new compiler.Run() compile files + source match { + case Left(files) => + new compiler.Run() compile files + case Right(sourceCode) => + new compiler.Run() compileSources List(new BatchSourceFile("newSource", sourceCode)) + } + if (reporter.hasErrors) return None @@ -111,7 +117,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor docletInstance match { case universer: Universer => - val universe = makeUniverse(files) getOrElse { throw NoCompilerRunException } + val universe = makeUniverse(Left(files)) getOrElse { throw NoCompilerRunException } universer setUniverse universe docletInstance match { diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index c8c5f1ec11..f059b5c0cb 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -35,7 +35,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage val valueMembers = - tpl.methods.filterNot(_.isBridge) ++ tpl.values ++ tpl.templates.filter(x => x.isObject || x.isPackage) sorted + tpl.methods ++ tpl.values ++ tpl.templates.filter(x => x.isObject || x.isPackage) sorted val (absValueMembers, nonAbsValueMembers) = valueMembers partition (_.isAbstract) diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 496d004fd8..dd1c75c322 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -247,7 +247,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { protected lazy val memberSyms = // Only this class's constructors are part of its members, inherited constructors are not. - sym.info.members.filter(s => localShouldDocument(s) && (!s.isConstructor || s.owner == sym)) + sym.info.members.filter(s => localShouldDocument(s) && (!s.isConstructor || s.owner == sym) && !isPureBridge(sym) ) val members = memberSyms flatMap (makeMember(_, this)) val templates = members collect { case c: DocTemplateEntity => c } @@ -705,4 +705,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def localShouldDocument(aSym: Symbol): Boolean = { !aSym.isPrivate && (aSym.isProtected || aSym.privateWithin == NoSymbol) && !aSym.isSynthetic } + + /** Filter '@bridge' methods only if *they don't override non-bridge methods*. See SI-5373 for details */ + def isPureBridge(sym: Symbol) = sym.isBridge && sym.allOverriddenSymbols.forall(_.isBridge) } diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala new file mode 100644 index 0000000000..2eb026ceee --- /dev/null +++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala @@ -0,0 +1,124 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Vlad Ureche + */ + +package scala.tools.partest + +import scala.tools.partest._ +import java.io._ +import scala.tools.nsc._ +import scala.tools.nsc.util.CommandLineParser +import scala.tools.nsc.doc.{Settings, DocFactory, Universe} +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.reporters.ConsoleReporter + +/** A class for testing scaladoc model generation + * - you need to specify the code in the `code` method + * - you need to override the testModel method to test the model + * - you may specify extra parameters to send to scaladoc in `scaladocSettings` + * {{{ + import scala.tools.nsc.doc.model._ + import scala.tools.partest.ScaladocModelTest + + object Test extends ScaladocModelTest { + + def code = """ ... """ + def scaladocSettings = "" + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + rootPackage._package("scala")._package("test")._class("C")._method("foo") + } + } + * }}} + */ +abstract class ScaladocModelTest extends DirectTest { + + /** Override this to give scaladoc command line parameters */ + def scaladocSettings: String + + /** Override this to test the model */ + def testModel(root: Package): Unit + + // Implementation follows: + override def extraSettings: String = "-usejavacp" + + override def show(): Unit = { + // redirect err to out, for logging + val prevErr = System.err + System.setErr(System.out) + + try { + // 1 - compile with scaladoc and get the model out + val args = scaladocSettings.split(" ") + val universe = model(args:_*).getOrElse({sys.error("Scaladoc Model Test ERROR: No universe generated!")}) + // 2 - check the model generated + testModel(universe.rootPackage) + } catch { + case e => + println(e) + e.printStackTrace + } + // set err back to the real err handler + System.setErr(prevErr) + } + + // create a new scaladoc compiler + def newDocFactory(args: String*): DocFactory = { + val settings = new Settings(_ => ()) + val command = new ScalaDoc.Command((CommandLineParser tokenize extraSettings) ++ args.toList, settings) + val docFact = new DocFactory(new ConsoleReporter(settings), settings) + docFact + } + + // compile with scaladoc and output the result + def model(args: String*): Option[Universe] = newDocFactory(args: _*).makeUniverse(Right(code)) + + // so we don't get the newSettings warning + override def isDebug = false + + + // finally, enable easy navigation inside the entities + object access { + + // Make it easy to access things + class TemplateAccess(tpl: DocTemplateEntity) { + + def _class(name: String): DocTemplateEntity = getTheFirst(_classes(name), tpl.qualifiedName + ".class(" + name + ")") + def _classes(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).flatMap({ case c: Class => List(c)}) + + def _trait(name: String): DocTemplateEntity = getTheFirst(_traits(name), tpl.qualifiedName + ".trait(" + name + ")") + def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).flatMap({ case t: Trait => List(t)}) + + def _object(name: String): DocTemplateEntity = getTheFirst(_objects(name), tpl.qualifiedName + ".object(" + name + ")") + def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).flatMap({ case o: Object => List(o)}) + + def _method(name: String): Def = getTheFirst(_methods(name), tpl.qualifiedName + ".method(" + name + ")") + def _methods(name: String): List[Def] = tpl.methods.filter(_.name == name) + + def _value(name: String): Val = getTheFirst(_values(name), tpl.qualifiedName + ".value(" + name + ")") + def _values(name: String): List[Val] = tpl.values.filter(_.name == name) + + def getTheFirst[T](list: List[T], expl: String): T = { + if (list.length == 1) + list.head + else if (list.length == 0) + sys.error("Error getting " + expl + ": No such element. All elements in list: [" + list.mkString(", ") + "]") + else + sys.error("Error getting " + expl + ": " + list.length + " elements with this name. " + + "All elements in list: [" + list.mkString(", ") + "]") + } + } + + class PackageAccess(pack: Package) extends TemplateAccess(pack) { + def _package(name: String): Package = getTheFirst(_packages(name), pack.qualifiedName + ".package(" + name + ")") + def _packages(name: String): List[Package] = pack.packages.filter(_.name == name) + } + + implicit def templateAccess(tpl: DocTemplateEntity) = new TemplateAccess(tpl) + implicit def packageAccess(pack: Package) = new PackageAccess(pack) + } +} diff --git a/test/scaladoc/run/SI-5373.check b/test/scaladoc/run/SI-5373.check new file mode 100644 index 0000000000..c55eb001cf --- /dev/null +++ b/test/scaladoc/run/SI-5373.check @@ -0,0 +1 @@ +model contains 6 documentable templates diff --git a/test/scaladoc/run/SI-5373.scala b/test/scaladoc/run/SI-5373.scala new file mode 100644 index 0000000000..af433a1844 --- /dev/null +++ b/test/scaladoc/run/SI-5373.scala @@ -0,0 +1,34 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + def code = """ + import scala.annotation.bridge + + package scala.test { + + trait A { + def foo = () + } + + trait B { + @bridge() + def foo = () + } + + class C extends A with B + } + """ + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + rootPackage._package("scala")._package("test")._class("C")._method("foo") + } +} \ No newline at end of file diff --git a/test/scaladoc/scala/IndexScriptTest.scala b/test/scaladoc/scala/IndexScriptTest.scala deleted file mode 100644 index e0372020fd..0000000000 --- a/test/scaladoc/scala/IndexScriptTest.scala +++ /dev/null @@ -1,52 +0,0 @@ -import org.scalacheck._ -import org.scalacheck.Prop._ - -import scala.tools.nsc.doc -import scala.tools.nsc.doc.html.page.IndexScript -import java.net.{URLClassLoader, URLDecoder} - -object Test extends Properties("IndexScript") { - - def getClasspath = { - val loader = Thread.currentThread.getContextClassLoader - val paths = loader.asInstanceOf[URLClassLoader].getURLs - val morepaths = loader.getParent.asInstanceOf[URLClassLoader].getURLs - (paths ++ morepaths).map(u => URLDecoder.decode(u.getPath)).mkString(java.io.File.pathSeparator) - } - - val docFactory = { - val settings = new doc.Settings({Console.err.println(_)}) - settings.classpath.value = getClasspath - val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) - new doc.DocFactory(reporter, settings) - } - - val indexModelFactory = doc.model.IndexModelFactory - - def createIndexScript(path: String) = - docFactory.makeUniverse(List(path)) match { - case Some(universe) => { - val index = new IndexScript(universe, - indexModelFactory.makeIndex(universe)) - Some(index) - } - case _ => - None - } - - property("allPackages") = { - createIndexScript("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { - case Some(index) => - index.allPackages.map(_.toString) == List( - "scala", - "scala.tools", - "scala.tools.nsc", - "scala.tools.nsc.doc", - "scala.tools.nsc.doc.html", - "scala.tools.nsc.doc.html.page" - ) - case None => - false - } - } -} diff --git a/test/scaladoc/scala/IndexTest.scala b/test/scaladoc/scala/IndexTest.scala deleted file mode 100644 index c14fd98297..0000000000 --- a/test/scaladoc/scala/IndexTest.scala +++ /dev/null @@ -1,82 +0,0 @@ -import org.scalacheck._ -import org.scalacheck.Prop._ - -import scala.tools.nsc.doc -import scala.tools.nsc.doc.html.page.Index -import java.net.{URLClassLoader, URLDecoder} - -object Test extends Properties("Index") { - - def getClasspath = { - // these things can be tricky - // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths - // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. - // this test _will_ fail again some time in the future. - val paths = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - (paths ++ morepaths).mkString(java.io.File.pathSeparator) - } - - val docFactory = { - val settings = new doc.Settings({Console.err.println(_)}) - - settings.classpath.value = getClasspath - println(settings.classpath.value) - - val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) - - new doc.DocFactory(reporter, settings) - } - - val indexModelFactory = doc.model.IndexModelFactory - - def createIndex(path: String): Option[Index] = { - - val maybeUniverse = { - //val stream = new java.io.ByteArrayOutputStream - //val original = Console.out - //Console.setOut(stream) - - val result = docFactory.makeUniverse(List(path)) - - // assert(stream.toString == "model contains 2 documentable templates\n") - //Console.setOut(original) - - result - } - - maybeUniverse match { - case Some(universe) => { - val index = new Index(universe, indexModelFactory.makeIndex(universe)) - return Some(index) - } - case _ => return None - } - - } - - property("path") = { - createIndex("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { - case Some(index) => - index.path == List("index.html") - case None => false - } - } - - property("title") = { - createIndex("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { - case Some(index) => - index.title == "" - - case None => false - } - } - property("browser contants a script element") = { - createIndex("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { - case Some(index) => - (index.browser \ "script").size == 1 - - case None => false - } - } -} diff --git a/test/scaladoc/scala/html.flags b/test/scaladoc/scala/html.flags deleted file mode 100644 index b2264ec4f4..0000000000 --- a/test/scaladoc/scala/html.flags +++ /dev/null @@ -1 +0,0 @@ --encoding UTF-8 \ No newline at end of file diff --git a/test/scaladoc/scala/html/HtmlFactoryTest.flags b/test/scaladoc/scala/html/HtmlFactoryTest.flags deleted file mode 100644 index b2264ec4f4..0000000000 --- a/test/scaladoc/scala/html/HtmlFactoryTest.flags +++ /dev/null @@ -1 +0,0 @@ --encoding UTF-8 \ No newline at end of file diff --git a/test/scaladoc/scala/html/HtmlFactoryTest.scala b/test/scaladoc/scala/html/HtmlFactoryTest.scala deleted file mode 100644 index 28c7a4b94f..0000000000 --- a/test/scaladoc/scala/html/HtmlFactoryTest.scala +++ /dev/null @@ -1,730 +0,0 @@ -import org.scalacheck._ -import org.scalacheck.Prop._ - -import java.net.{URLClassLoader, URLDecoder} - -object XMLUtil { - import scala.xml._ - - def stripGroup(seq: Node): Node = { - seq match { - case group: Group => { -
{ group.nodes.map(stripGroup _) }
- } - case e: Elem => { - val child = e.child.map(stripGroup _) - Elem(e.prefix, e.label, e.attributes, e.scope, child : _*) - } - case _ => seq - } - } -} - -object Test extends Properties("HtmlFactory") { - - final val RESOURCES = "test/scaladoc/resources/" - - import scala.tools.nsc.doc.{DocFactory, Settings} - import scala.tools.nsc.doc.model.IndexModelFactory - import scala.tools.nsc.doc.html.HtmlFactory - import scala.tools.nsc.doc.html.page.ReferenceIndex - - def getClasspath = { - // these things can be tricky - // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths - // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. - // this test _will_ fail again some time in the future. - val paths = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - (paths ++ morepaths).mkString(java.io.File.pathSeparator) - } - - def createFactory = { - val settings = new Settings({Console.err.println(_)}) - settings.classpath.value = getClasspath - - val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) - new DocFactory(reporter, settings) - } - - def createTemplates(basename: String) = { - val result = scala.collection.mutable.Map[String, scala.xml.NodeSeq]() - - createFactory.makeUniverse(List(RESOURCES+basename)) match { - case Some(universe) => { - val index = IndexModelFactory.makeIndex(universe) - (new HtmlFactory(universe, index)).writeTemplates((page) => { - result += (page.absoluteLinkTo(page.path) -> page.body) - }) - } - case _ => ; - } - - result - } - - def createReferenceIndex(basename: String) = { - createFactory.makeUniverse(List(RESOURCES+basename)) match { - case Some(universe) => { - val index = IndexModelFactory.makeIndex(universe) - val pages = index.firstLetterIndex.map({ - case (key, value) => { - val page = new ReferenceIndex(key, index, universe) - page.absoluteLinkTo(page.path) -> page.body - } - }) - Some(pages) - } - case _ => - None - } - } - - def createTemplate(scala: String) = { - val html = scala.stripSuffix(".scala") + ".html" - createTemplates(scala)(html) - } - - /** - * This tests the text without the markup - ex: - * - *

- * - * implicit - * def - * - * - * test(): Int - * - *

- * - * becomes: - * - * implicit def test(): Int - * - * and is required to contain the text in the given checks - * - * NOTE: Comparison is done ignoring all whitespace - */ - def checkText(scalaFile: String, debug: Boolean = true)(checks: (Option[String], String, Boolean)*): Boolean = { - val htmlFile = scalaFile.stripSuffix(".scala") + ".html" - val htmlAllFiles = createTemplates(scalaFile) - var result = true - - for ((fileHint, check, expected) <- checks) { - // resolve the file to be checked - val fileName = fileHint match { - case Some(file) => - if (file endsWith ".html") - file - else - file + ".html" - case None => - htmlFile - } - val fileTextPretty = htmlAllFiles(fileName).text.replace('→',' ').replaceAll("\\s+"," ") - val fileText = fileTextPretty.replaceAll(" ", "") - - val checkTextPretty = check.replace('→',' ').replaceAll("\\s+"," ") - val checkText = checkTextPretty.replaceAll(" ", "") - - val checkValue = fileText.contains(checkText) == expected - if (debug && (!checkValue)) { - Console.err.println("") - Console.err.println("HTML Check failed for resource file " + scalaFile + ":") - Console.err.println("Could not match: \n" + checkTextPretty) - Console.err.println("In the extracted HTML text: \n" + fileTextPretty) - Console.err.println("NOTE: The whitespaces are eliminated before matching!") - Console.err.println("") - } - result &&= checkValue - } - - result - } - - - def shortComments(root: scala.xml.Node) = - XMLUtil.stripGroup(root).descendant.flatMap { - case e: scala.xml.Elem => { - if (e.attribute("class").toString.contains("shortcomment")) { - Some(e) - } else { - None - } - } - case _ => None - } - - property("Trac #3790") = { - createTemplate("Trac3790.scala") match { - case node: scala.xml.Node => { - val comments = shortComments(node) - - comments.exists { _.toString.contains(">A lazy String\n

") } && - comments.exists { _.toString.contains(">A non-lazy String\n

") } - } - case _ => false - } - } - - property("Trac #4306") = { - val files = createTemplates("Trac4306.scala") - files("com/example/trac4306/foo/package$$Bar.html") != None - } - - property("Trac #4366") = { - createTemplate("Trac4366.scala") match { - case node: scala.xml.Node => { - shortComments(node).exists { n => { - val str = n.toString - str.contains("foo") && str.contains("") - } } - } - case _ => false - } - } - - property("Trac #4358") = { - createTemplate("Trac4358.scala") match { - case node: scala.xml.Node => - ! shortComments(node).exists { - _.toString.contains("i.") - } - case _ => false - } - } - - property("Trac #4180") = { - createTemplate("Trac4180.scala") != None - } - - property("Trac #4372") = { - createTemplate("Trac4372.scala") match { - case node: scala.xml.Node => { - val html = node.toString - html.contains("+:") && - html.contains("-:") && - html.contains("""(n: Int): Int""") - } - case _ => false - } - } - - property("Trac #4374 - public") = { - val files = createTemplates("Trac4374.scala") - files("WithPublic.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains("""href="WithPublic$.html"""") && - files.get("WithPublic$.html") != None - } - case _ => false - } - } - - property("Trac #4374 - private") = { - val files = createTemplates("Trac4374.scala") - files("WithPrivate.html") match { - case node: scala.xml.Node => { - val s = node.toString - ! s.contains("""href="WithPrivate$.html"""") && - files.get("WithPrivate$.html") == None - } - case _ => false - } - } - - property("Trac #3484") = { - val files = createTemplates("Trac3484.scala") - - files("Collection.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains(""": Traversable[B]""") - } - case _ => false - } - } - - property("Trac #3484 - SR704") = { - val files = createTemplates("Trac3484.scala") - - files("SR704.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains("Hello Mister John.") - } - case _ => false - } - } - - property("Trac #4325 - files") = { - val files = createTemplates("Trac4325.scala") - - files.get("WithSynthetic.html") != None && - files.get("WithSynthetic$.html") == None && - files.get("WithObject.html") != None && - files.get("WithObject$.html") != None - } - - property("Trac #4325 - Don't link to syntetic companion") = { - val files = createTemplates("Trac4325.scala") - - files("WithSynthetic.html") match { - case node: scala.xml.Node => { - val s = node.toString - ! s.contains("""href="WithSynthetic$.html"""") - } - case _ => false - } - } - - property("Trac #4325 - Link to companion") = { - val files = createTemplates("Trac4325.scala") - - files("WithObject.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains("""href="WithObject$.html"""") - } - case _ => false - } - } - - property("Trac #4420 - no whitespace at end of line") = { - val files = createTemplates("Trac4420.scala") - - files("TestA.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains("""See YYY for more details""") - } - case _ => false - } - } - // - // property("Trac #484 - refinements and existentials") = { - // val files = createTemplates("Trac484.scala") - // val lines = """ - // |type Bar = AnyRef { type Dingus <: T forSome { type T <: String } } - // |type Foo = AnyRef { ... /* 3 definitions in type refinement */ } - // |def g(x: T forSome { type T <: String }): String - // |def h(x: Float): AnyRef { def quux(x: Int,y: Int): Int } - // |def hh(x: Float): AnyRef { def quux(x: Int,y: Int): Int } - // |def j(x: Int): Bar - // |def k(): AnyRef { type Dingus <: T forSome { type T <: String } } - // """.stripMargin.trim.lines map (_.trim) - // - // files("RefinementAndExistentials.html") match { - // case node: scala.xml.Node => { - // val s = node.text.replaceAll("\\s+", " ") - // lines forall (s contains _) - // } - // case _ => false - // } - // } - - property("Trac #4289") = { - val files = createTemplates("Trac4289.scala") - - files("Subclass.html") match { - case node: scala.xml.Node => { - node.toString.contains { - """
returns

123

""" - } - } - case _ => false - } - } - - property("Trac #4409") = { - createTemplate("Trac4409.scala") match { - case node: scala.xml.Node => { - ! node.toString.contains("""
    since""") - } - case _ => false - } - } - - property("Trac #4452") = { - createTemplate("Trac4452.scala") match { - case node: scala.xml.Node => - ! node.toString.contains(">*") - case _ => false - } - } - - property("Trac #4471") = { - createReferenceIndex("Trac4471.scala") match { - case Some(pages) => - (pages.get("index/index-f.html") match { - case Some(node) => node.toString.contains(">A") - case _ => false - }) && (pages.get("index/index-b.html") match { - case Some(node) => node.toString.contains(">bar") - case _ => false - }) - case _ => false - } - } - - property("SI-4641") = { - createReferenceIndex("SI_4641.scala") match { - case Some(pages) => pages.contains("index/index-_.html") - case _ => false - } - } - - property("SI-4421") = { - createTemplate("SI_4421.scala") match { - case node: scala.xml.Node => { - val html = node.toString - html.contains(">Example:") && html.contains(">Note<") - } - case _ => false - } - } - - property("SI-4589") = { - createTemplate("SI_4589.scala") match { - case node: scala.xml.Node => { - val html = node.toString - html.contains(">x0123456789: <") && - html.contains(">x012345678901234567890123456789: <") - } - case _ => false - } - } - - property("Should decode symbolic type alias name.") = { - createTemplate("SI_4715.scala") match { - case node: scala.xml.Node => { - val html = node.toString - html.contains(">: :+:[<") - } - case _ => false - } - } - - property("Shouldn't drop type arguments to aliased tuple.") = { - createTemplate("SI_4676.scala") match { - case node: scala.xml.Node => { - node.toString.contains(">ss: (String, String)<") - } - case _ => false - } - } - - property("Default arguments of synthesized constructor") = { - val files = createTemplates("SI_4287.scala") - - files("ClassWithSugar.html") match { - case node: scala.xml.Node => { - node.toString.contains(">123<") - } - case _ => false - } - } - - property("Default arguments of synthesized constructor") = { - createTemplate("SI_4507.scala") match { - case node: scala.xml.Node => - ! node.toString.contains("
  1. returns silently when evaluating true and true
  2. ") - case _ => false - } - } - - property("Use cases and links should not crash scaladoc") = { - createTemplate("SI_4898.scala") - true - } - - property("Use cases should override their original members") = - checkText("SI_5054_q1.scala")( - (None,"""def test(): Int""", true) - //Disabled because the full signature is now displayed - //(None,"""def test(implicit lost: Int): Int""", false) - ) - - property("Use cases should keep their flags - final should not be lost") = - checkText("SI_5054_q2.scala")((None, """final def test(): Int""", true)) - - property("Use cases should keep their flags - implicit should not be lost") = - checkText("SI_5054_q3.scala")((None, """implicit def test(): Int""", true)) - - property("Use cases should keep their flags - real abstract should not be lost") = - checkText("SI_5054_q4.scala")((None, """abstract def test(): Int""", true)) - - property("Use cases should keep their flags - traits should not be affected") = - checkText("SI_5054_q5.scala")((None, """def test(): Int""", true)) - - property("Use cases should keep their flags - traits should not be affected") = - checkText("SI_5054_q6.scala")((None, """abstract def test(): Int""", true)) - - property("Use case individual signature test") = - checkText("SI_5054_q7.scala")( - (None, """abstract def test2(explicit: Int): Int [use case] This takes the explicit value passed.""", true), - (None, """abstract def test1(): Int [use case] This takes the implicit value in scope.""", true) - ) - - property("Display correct \"Definition classes\"") = - checkText("SI_5287.scala")( - (None, - """def method(): Int - [use case] The usecase explanation - [use case] The usecase explanation - Definition Classes SI_5287 SI_5287_B SI_5287_A""", true) - ) // the explanation appears twice, as small comment and full comment - - property("Correct comment inheritance for overriding") = - checkText("implicit-inheritance-override.scala")( - (Some("Base"), - """def function[T](arg1: T, arg2: String): Double - The base comment. - The base comment. And another sentence... - T the type of the first argument - arg1 The T term comment - arg2 The string comment - returns The return comment - """, true), - (Some("DerivedA"), - """def function[T](arg1: T, arg2: String): Double - Overriding the comment, the params and returns comments should stay the same. - Overriding the comment, the params and returns comments should stay the same. - T the type of the first argument - arg1 The T term comment - arg2 The string comment - returns The return comment - """, true), - (Some("DerivedB"), - """def function[T](arg1: T, arg2: String): Double - T the type of the first argument - arg1 The overridden T term comment - arg2 The overridden string comment - returns The return comment - """, true), - (Some("DerivedC"), - """def function[T](arg1: T, arg2: String): Double - T the type of the first argument - arg1 The T term comment - arg2 The string comment - returns The overridden return comment - """, true), - (Some("DerivedD"), - """def function[T](arg1: T, arg2: String): Double - T The overriden type parameter comment - arg1 The T term comment - arg2 The string comment - returns The return comment - """, true) - ) - - for (useCaseFile <- List("UseCaseInheritance", "UseCaseOverrideInheritance")) { - property("Correct comment inheritance for usecases") = - checkText("implicit-inheritance-usecase.scala")( - (Some(useCaseFile), - """def missing_arg[T](arg1: T): Double - [use case] - [use case] - T The type parameter - arg1 The T term comment - returns The return comment - """, true), - (Some(useCaseFile), - """def missing_targ(arg1: Int, arg2: String): Double - [use case] - [use case] - arg1 The T term comment - arg2 The string comment - returns The return comment - """, true), - (Some(useCaseFile), - """def overridden_arg1[T](implicit arg1: T, arg2: String): Double - [use case] - [use case] - T The type parameter - arg1 The overridden T term comment - arg2 The string comment - returns The return comment - """, true), - (Some(useCaseFile), - """def overridden_targ[T](implicit arg1: T, arg2: String): Double - [use case] - [use case] - T The overridden type parameter comment - arg1 The T term comment - arg2 The string comment - returns The return comment - """, true), - (Some(useCaseFile), - """def overridden_return[T](implicit arg1: T, arg2: String): Double - [use case] - [use case] - T The type parameter - arg1 The T term comment - arg2 The string comment - returns The overridden return comment - """, true), - (Some(useCaseFile), - """def added_arg[T](implicit arg1: T, arg2: String, arg3: Float): Double - [use case] - [use case] - T The type parameter - arg1 The T term comment - arg2 The string comment - arg3 The added float comment - returns The return comment - """, true), - (Some(useCaseFile), - """def overridden_comment[T](implicit arg1: T, arg2: String): Double - [use case] The overridden comment. - [use case] The overridden comment. - T The type parameter - arg1 The T term comment - arg2 The string comment - returns The return comment - """, true) - ) - } - - property("Correct explicit inheritance for override") = - checkText("explicit-inheritance-override.scala")( - (Some("InheritDocDerived"), - """def function[T](arg1: T, arg2: String): Double - Starting line - Starting line - The base comment. And another sentence... - The base comment. And another sentence... - Ending line - T StartT the type of the first argument EndT - arg1 Start1 The T term comment End1 - arg2 Start2 The string comment End2 - returns StartRet The return comment EndRet""", true), - (Some("InheritDocDerived"), - """Definition Classes InheritDocDerived → InheritDocBase - Example: StartExample function[Int](3, "something") EndExample - Version StartVer 0.0.2 EndVer - Since StartSince 0.0.1 EndSince - Exceptions thrown - SomeException StartEx if the function is not called with correct parameters EndEx - SomeOtherException StartSOE Should Warn EndSOE - To do StartTodo Call mom. And dad! EndTodo - Note StartNote Be careful! EndNote - See also StartSee The Manual EndSee - """, true)) - - property("Correct explicit inheritance for usecase") = - checkText("explicit-inheritance-usecase.scala")( - (Some("UseCaseInheritDoc"), - """def function[T](arg1: T, arg2: String): Double - [use case] Starting line - [use case] Starting line - The base comment. And another sentence... - The base comment. And another sentence... - Ending line - T StartT the type of the first argument EndT - arg1 Start1 The T term comment End1 - arg2 Start2 The string comment End2 - returns StartRet The return comment EndRet""", true), - (Some("UseCaseInheritDoc"), - """Example: StartExample function[Int](3,"something") EndExample - Version StartVer 0.0.2 EndVer - Since StartSince 0.0.1 EndSince - Exceptions thrown - SomeException StartEx if the function is not called with correct parameters EndEx - SomeOtherException StartSOE Should Warn EndSOE - To do StartTodo Call mom. And dad! EndTodo - Note StartNote Be careful! EndNote - See also StartSee The Manual EndSee - """, true)) - - property("Correct explicit inheritance in corner cases") = - checkText("inheritdoc-corner-cases.scala")( - (Some("D"), - """def hello1: Int - Inherited: Hello 1 comment - Inherited: Hello 1 comment - Definition Classes D → A - """, true), - (Some("D"), - """def hello2: Int - Inherited: Hello 2 comment - Inherited: Hello 2 comment - Definition Classes D → B - """, true), - (Some("G"), - """def hello1: Int - Inherited: Hello 1 comment - Inherited: Hello 1 comment - Definition Classes G → D → A - """, true), - (Some("G"), - """def hello2: Int - Inherited: Hello 2 comment - Inherited: Hello 2 comment - Definition Classes G → D → B - """, true), - (Some("I"), - """def hello1(i: Int): Unit - [use case] Inherited: Hello 1 comment - [use case] Inherited: Hello 1 comment - Definition Classes I → G → D → A - """, true) - // traits E, F and H shouldn't crash scaladoc but we don't need to check the output - ) - - property("Indentation normalization for code blocks") = { - val files = createTemplates("code-indent.scala") - - files("C.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains("
    a typicial indented\ncomment on multiple\ncomment lines
    ") && - s.contains("
    one liner
    ") && - s.contains("
    two lines, one useful
    ") && - s.contains("
    line1\nline2\nline3\nline4
    ") && - s.contains("
    a ragged example\na (condition)\n  the t h e n branch\nan alternative\n  the e l s e branch
    ") && - s.contains("
    l1\n\nl2\n\nl3\n\nl4\n\nl5
    ") - } - case _ => false - } - } - - { - val files = createTemplates("basic.scala") - //println(files) - - property("class") = files.get("com/example/p1/Clazz.html") match { - case Some(node: scala.xml.Node) => { - property("implicit convertion") = - node.toString contains "implicit " - - property("gt4s") = - node.toString contains "title=\"gt4s: $colon$colon\"" - - property("gt4s of a deprecated method") = - node.toString contains "title=\"gt4s: $colon$colon$colon$colon. Deprecated: " - true - } - case _ => false - } - property("package") = files.get("com/example/p1/package.html") != None - - property("package object") = files("com/example/p1/package.html") match { - case node: scala.xml.Node => - node.toString contains "com.example.p1.package#packageObjectMethod" - case _ => false - } - - property("lower bound") = files("com/example/p1/LowerBound.html") match { - case node: scala.xml.Node => true - case _ => false - } - - property("upper bound") = files("com/example/p1/UpperBound.html") match { - case node: scala.xml.Node => true - case _ => false - } - } -} diff --git a/test/scaladoc/scala/model/CommentFactoryTest.scala b/test/scaladoc/scala/model/CommentFactoryTest.scala deleted file mode 100644 index 69c314a64c..0000000000 --- a/test/scaladoc/scala/model/CommentFactoryTest.scala +++ /dev/null @@ -1,155 +0,0 @@ -import org.scalacheck._ -import org.scalacheck.Prop._ - -import scala.tools.nsc.Global -import scala.tools.nsc.doc -import scala.tools.nsc.doc.model.comment._ - -class Factory(val g: Global, val s: doc.Settings) - extends doc.model.ModelFactory(g, s) { - thisFactory: Factory with CommentFactory with doc.model.TreeFactory => - - def strip(c: Comment): Option[Inline] = { - c.body match { - case Body(List(Paragraph(Chain(List(Summary(inner)))))) => Some(inner) - case _ => None - } - } - - def parseComment(s: String): Option[Inline] = - strip(parse(s, "", scala.tools.nsc.util.NoPosition)) - - def createBody(s: String) = - parse(s, "", scala.tools.nsc.util.NoPosition).body -} - -object Test extends Properties("CommentFactory") { - val factory = { - val settings = new doc.Settings((str: String) => {}) - val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) - val g = new Global(settings, reporter) - (new Factory(g, settings) with CommentFactory with doc.model.TreeFactory) - } - - def parse(src: String, dst: Inline) = { - factory.parseComment(src) match { - case Some(inline) => - inline == dst - case _ => - false - } - } - - property("parse") = parse( - "/** One two three */", - Text("One two three") - ) - property("parse") = parse( - "/** One `two` three */", - Chain(List(Text("One "), Monospace(Text("two")), Text(" three"))) - ) - - property("parse") = parse( - """ -/** One two - * three */""", - Text("One two\nthree") - ) - property("parse") = parse( - """ -/** One `two` - * three */""", - Chain(List(Text("One "), Monospace(Text("two")), Text("\n"), Text("three"))) - ) - - property("parse") = parse( - """ -/** One `two` - * three */""", - Chain(List(Text("One "), Monospace(Text("two")), Text("\n"), Text(" three"))) - ) - - property("parse") = parse( - """ -/** One - * `two` three */""", - Chain(List(Text("One"), Text("\n"), Monospace(Text("two")), Text(" three"))) - ) - - property("Trac #4361 - ^...^") = parse( - """ -/** - * hello ^world^ */""", - Chain(List(Text("hello "), Superscript(Text("world")))) - ) - - property("Trac #4361 - single ^ symbol") = parse( - """ -/** - *
    - * hello ^world
    - * 
    - * - */""", - Chain(List(Text(""), Text("\n"), - - - HtmlTag("
    \nhello ^world\n
    "))) - ) - - property("Trac #4366 - body") = { - val body = factory.createBody( - """ - /** - * foo has been deprecated and will be removed in a future version. Please call bar instead. - */ - """ - ) - - body == Body(List(Paragraph(Chain(List( - Summary(Chain(List(HtmlTag("foo has been deprecated and will be removed in a future version. Please call bar instead."), Text("\n"), Text("")))) - ))))) - } - - property("Trac #4366 - summary") = { - val body = factory.createBody( - """ - /** - * foo has been deprecated and will be removed in a future version. Please call bar instead. - */ - """ - ) - body.summary == Some(Chain(List(HtmlTag("foo has been deprecated and will be removed in a future version. Please call bar instead."), Text("\n"), Text("")))) - } - - property("Trac #4358 - body") = { - factory.createBody( - """ - /** - * Implicit conversion that invokes the expect method on the EasyMock companion object (i.e., the - * static expect method in Java class org.easymock.EasyMock). - */ - """ - ) match { - case Body(List(Paragraph(Chain(List(Summary(Chain(List(Chain(List( - Text("Implicit conversion that invokes the "), - HtmlTag("expect"), - Text(" method on the "), - HtmlTag("EasyMock"), - Text(" companion object ("), - HtmlTag("i.e."), - Text(", the\nstatic "), - HtmlTag("expect"), - Text(" method in Java class "), - HtmlTag("org.easymock.EasyMock"), - Text(")") - )), Text(".")))), Text("\n")))))) => - true - case other => { - println(other) - false - } - } - } - -} diff --git a/test/scaladoc/scalacheck/CommentFactoryTest.scala b/test/scaladoc/scalacheck/CommentFactoryTest.scala new file mode 100644 index 0000000000..69c314a64c --- /dev/null +++ b/test/scaladoc/scalacheck/CommentFactoryTest.scala @@ -0,0 +1,155 @@ +import org.scalacheck._ +import org.scalacheck.Prop._ + +import scala.tools.nsc.Global +import scala.tools.nsc.doc +import scala.tools.nsc.doc.model.comment._ + +class Factory(val g: Global, val s: doc.Settings) + extends doc.model.ModelFactory(g, s) { + thisFactory: Factory with CommentFactory with doc.model.TreeFactory => + + def strip(c: Comment): Option[Inline] = { + c.body match { + case Body(List(Paragraph(Chain(List(Summary(inner)))))) => Some(inner) + case _ => None + } + } + + def parseComment(s: String): Option[Inline] = + strip(parse(s, "", scala.tools.nsc.util.NoPosition)) + + def createBody(s: String) = + parse(s, "", scala.tools.nsc.util.NoPosition).body +} + +object Test extends Properties("CommentFactory") { + val factory = { + val settings = new doc.Settings((str: String) => {}) + val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) + val g = new Global(settings, reporter) + (new Factory(g, settings) with CommentFactory with doc.model.TreeFactory) + } + + def parse(src: String, dst: Inline) = { + factory.parseComment(src) match { + case Some(inline) => + inline == dst + case _ => + false + } + } + + property("parse") = parse( + "/** One two three */", + Text("One two three") + ) + property("parse") = parse( + "/** One `two` three */", + Chain(List(Text("One "), Monospace(Text("two")), Text(" three"))) + ) + + property("parse") = parse( + """ +/** One two + * three */""", + Text("One two\nthree") + ) + property("parse") = parse( + """ +/** One `two` + * three */""", + Chain(List(Text("One "), Monospace(Text("two")), Text("\n"), Text("three"))) + ) + + property("parse") = parse( + """ +/** One `two` + * three */""", + Chain(List(Text("One "), Monospace(Text("two")), Text("\n"), Text(" three"))) + ) + + property("parse") = parse( + """ +/** One + * `two` three */""", + Chain(List(Text("One"), Text("\n"), Monospace(Text("two")), Text(" three"))) + ) + + property("Trac #4361 - ^...^") = parse( + """ +/** + * hello ^world^ */""", + Chain(List(Text("hello "), Superscript(Text("world")))) + ) + + property("Trac #4361 - single ^ symbol") = parse( + """ +/** + *
    + * hello ^world
    + * 
    + * + */""", + Chain(List(Text(""), Text("\n"), + + + HtmlTag("
    \nhello ^world\n
    "))) + ) + + property("Trac #4366 - body") = { + val body = factory.createBody( + """ + /** + * foo has been deprecated and will be removed in a future version. Please call bar instead. + */ + """ + ) + + body == Body(List(Paragraph(Chain(List( + Summary(Chain(List(HtmlTag("foo has been deprecated and will be removed in a future version. Please call bar instead."), Text("\n"), Text("")))) + ))))) + } + + property("Trac #4366 - summary") = { + val body = factory.createBody( + """ + /** + * foo has been deprecated and will be removed in a future version. Please call bar instead. + */ + """ + ) + body.summary == Some(Chain(List(HtmlTag("foo has been deprecated and will be removed in a future version. Please call bar instead."), Text("\n"), Text("")))) + } + + property("Trac #4358 - body") = { + factory.createBody( + """ + /** + * Implicit conversion that invokes the expect method on the EasyMock companion object (i.e., the + * static expect method in Java class org.easymock.EasyMock). + */ + """ + ) match { + case Body(List(Paragraph(Chain(List(Summary(Chain(List(Chain(List( + Text("Implicit conversion that invokes the "), + HtmlTag("expect"), + Text(" method on the "), + HtmlTag("EasyMock"), + Text(" companion object ("), + HtmlTag("i.e."), + Text(", the\nstatic "), + HtmlTag("expect"), + Text(" method in Java class "), + HtmlTag("org.easymock.EasyMock"), + Text(")") + )), Text(".")))), Text("\n")))))) => + true + case other => { + println(other) + false + } + } + } + +} diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.flags b/test/scaladoc/scalacheck/HtmlFactoryTest.flags new file mode 100644 index 0000000000..b2264ec4f4 --- /dev/null +++ b/test/scaladoc/scalacheck/HtmlFactoryTest.flags @@ -0,0 +1 @@ +-encoding UTF-8 \ No newline at end of file diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.scala b/test/scaladoc/scalacheck/HtmlFactoryTest.scala new file mode 100644 index 0000000000..5b6f75426e --- /dev/null +++ b/test/scaladoc/scalacheck/HtmlFactoryTest.scala @@ -0,0 +1,730 @@ +import org.scalacheck._ +import org.scalacheck.Prop._ + +import java.net.{URLClassLoader, URLDecoder} + +object XMLUtil { + import scala.xml._ + + def stripGroup(seq: Node): Node = { + seq match { + case group: Group => { +
    { group.nodes.map(stripGroup _) }
    + } + case e: Elem => { + val child = e.child.map(stripGroup _) + Elem(e.prefix, e.label, e.attributes, e.scope, child : _*) + } + case _ => seq + } + } +} + +object Test extends Properties("HtmlFactory") { + + final val RESOURCES = "test/scaladoc/resources/" + + import scala.tools.nsc.doc.{DocFactory, Settings} + import scala.tools.nsc.doc.model.IndexModelFactory + import scala.tools.nsc.doc.html.HtmlFactory + import scala.tools.nsc.doc.html.page.ReferenceIndex + + def getClasspath = { + // these things can be tricky + // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths + // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. + // this test _will_ fail again some time in the future. + val paths = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) + val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) + (paths ++ morepaths).mkString(java.io.File.pathSeparator) + } + + def createFactory = { + val settings = new Settings({Console.err.println(_)}) + settings.classpath.value = getClasspath + + val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) + new DocFactory(reporter, settings) + } + + def createTemplates(basename: String) = { + val result = scala.collection.mutable.Map[String, scala.xml.NodeSeq]() + + createFactory.makeUniverse(Left(List(RESOURCES+basename))) match { + case Some(universe) => { + val index = IndexModelFactory.makeIndex(universe) + (new HtmlFactory(universe, index)).writeTemplates((page) => { + result += (page.absoluteLinkTo(page.path) -> page.body) + }) + } + case _ => ; + } + + result + } + + def createReferenceIndex(basename: String) = { + createFactory.makeUniverse(Left(List(RESOURCES+basename))) match { + case Some(universe) => { + val index = IndexModelFactory.makeIndex(universe) + val pages = index.firstLetterIndex.map({ + case (key, value) => { + val page = new ReferenceIndex(key, index, universe) + page.absoluteLinkTo(page.path) -> page.body + } + }) + Some(pages) + } + case _ => + None + } + } + + def createTemplate(scala: String) = { + val html = scala.stripSuffix(".scala") + ".html" + createTemplates(scala)(html) + } + + /** + * This tests the text without the markup - ex: + * + *

    + * + * implicit + * def + * + * + * test(): Int + * + *

    + * + * becomes: + * + * implicit def test(): Int + * + * and is required to contain the text in the given checks + * + * NOTE: Comparison is done ignoring all whitespace + */ + def checkText(scalaFile: String, debug: Boolean = true)(checks: (Option[String], String, Boolean)*): Boolean = { + val htmlFile = scalaFile.stripSuffix(".scala") + ".html" + val htmlAllFiles = createTemplates(scalaFile) + var result = true + + for ((fileHint, check, expected) <- checks) { + // resolve the file to be checked + val fileName = fileHint match { + case Some(file) => + if (file endsWith ".html") + file + else + file + ".html" + case None => + htmlFile + } + val fileTextPretty = htmlAllFiles(fileName).text.replace('→',' ').replaceAll("\\s+"," ") + val fileText = fileTextPretty.replaceAll(" ", "") + + val checkTextPretty = check.replace('→',' ').replaceAll("\\s+"," ") + val checkText = checkTextPretty.replaceAll(" ", "") + + val checkValue = fileText.contains(checkText) == expected + if (debug && (!checkValue)) { + Console.err.println("") + Console.err.println("HTML Check failed for resource file " + scalaFile + ":") + Console.err.println("Could not match: \n" + checkTextPretty) + Console.err.println("In the extracted HTML text: \n" + fileTextPretty) + Console.err.println("NOTE: The whitespaces are eliminated before matching!") + Console.err.println("") + } + result &&= checkValue + } + + result + } + + + def shortComments(root: scala.xml.Node) = + XMLUtil.stripGroup(root).descendant.flatMap { + case e: scala.xml.Elem => { + if (e.attribute("class").toString.contains("shortcomment")) { + Some(e) + } else { + None + } + } + case _ => None + } + + property("Trac #3790") = { + createTemplate("Trac3790.scala") match { + case node: scala.xml.Node => { + val comments = shortComments(node) + + comments.exists { _.toString.contains(">A lazy String\n

    ") } && + comments.exists { _.toString.contains(">A non-lazy String\n

    ") } + } + case _ => false + } + } + + property("Trac #4306") = { + val files = createTemplates("Trac4306.scala") + files("com/example/trac4306/foo/package$$Bar.html") != None + } + + property("Trac #4366") = { + createTemplate("Trac4366.scala") match { + case node: scala.xml.Node => { + shortComments(node).exists { n => { + val str = n.toString + str.contains("foo") && str.contains("") + } } + } + case _ => false + } + } + + property("Trac #4358") = { + createTemplate("Trac4358.scala") match { + case node: scala.xml.Node => + ! shortComments(node).exists { + _.toString.contains("i.") + } + case _ => false + } + } + + property("Trac #4180") = { + createTemplate("Trac4180.scala") != None + } + + property("Trac #4372") = { + createTemplate("Trac4372.scala") match { + case node: scala.xml.Node => { + val html = node.toString + html.contains("+:") && + html.contains("-:") && + html.contains("""(n: Int): Int""") + } + case _ => false + } + } + + property("Trac #4374 - public") = { + val files = createTemplates("Trac4374.scala") + files("WithPublic.html") match { + case node: scala.xml.Node => { + val s = node.toString + s.contains("""href="WithPublic$.html"""") && + files.get("WithPublic$.html") != None + } + case _ => false + } + } + + property("Trac #4374 - private") = { + val files = createTemplates("Trac4374.scala") + files("WithPrivate.html") match { + case node: scala.xml.Node => { + val s = node.toString + ! s.contains("""href="WithPrivate$.html"""") && + files.get("WithPrivate$.html") == None + } + case _ => false + } + } + + property("Trac #3484") = { + val files = createTemplates("Trac3484.scala") + + files("Collection.html") match { + case node: scala.xml.Node => { + val s = node.toString + s.contains(""": Traversable[B]""") + } + case _ => false + } + } + + property("Trac #3484 - SR704") = { + val files = createTemplates("Trac3484.scala") + + files("SR704.html") match { + case node: scala.xml.Node => { + val s = node.toString + s.contains("Hello Mister John.") + } + case _ => false + } + } + + property("Trac #4325 - files") = { + val files = createTemplates("Trac4325.scala") + + files.get("WithSynthetic.html") != None && + files.get("WithSynthetic$.html") == None && + files.get("WithObject.html") != None && + files.get("WithObject$.html") != None + } + + property("Trac #4325 - Don't link to syntetic companion") = { + val files = createTemplates("Trac4325.scala") + + files("WithSynthetic.html") match { + case node: scala.xml.Node => { + val s = node.toString + ! s.contains("""href="WithSynthetic$.html"""") + } + case _ => false + } + } + + property("Trac #4325 - Link to companion") = { + val files = createTemplates("Trac4325.scala") + + files("WithObject.html") match { + case node: scala.xml.Node => { + val s = node.toString + s.contains("""href="WithObject$.html"""") + } + case _ => false + } + } + + property("Trac #4420 - no whitespace at end of line") = { + val files = createTemplates("Trac4420.scala") + + files("TestA.html") match { + case node: scala.xml.Node => { + val s = node.toString + s.contains("""See YYY for more details""") + } + case _ => false + } + } + // + // property("Trac #484 - refinements and existentials") = { + // val files = createTemplates("Trac484.scala") + // val lines = """ + // |type Bar = AnyRef { type Dingus <: T forSome { type T <: String } } + // |type Foo = AnyRef { ... /* 3 definitions in type refinement */ } + // |def g(x: T forSome { type T <: String }): String + // |def h(x: Float): AnyRef { def quux(x: Int,y: Int): Int } + // |def hh(x: Float): AnyRef { def quux(x: Int,y: Int): Int } + // |def j(x: Int): Bar + // |def k(): AnyRef { type Dingus <: T forSome { type T <: String } } + // """.stripMargin.trim.lines map (_.trim) + // + // files("RefinementAndExistentials.html") match { + // case node: scala.xml.Node => { + // val s = node.text.replaceAll("\\s+", " ") + // lines forall (s contains _) + // } + // case _ => false + // } + // } + + property("Trac #4289") = { + val files = createTemplates("Trac4289.scala") + + files("Subclass.html") match { + case node: scala.xml.Node => { + node.toString.contains { + """
    returns

    123

    """ + } + } + case _ => false + } + } + + property("Trac #4409") = { + createTemplate("Trac4409.scala") match { + case node: scala.xml.Node => { + ! node.toString.contains("""
      since""") + } + case _ => false + } + } + + property("Trac #4452") = { + createTemplate("Trac4452.scala") match { + case node: scala.xml.Node => + ! node.toString.contains(">*") + case _ => false + } + } + + property("Trac #4471") = { + createReferenceIndex("Trac4471.scala") match { + case Some(pages) => + (pages.get("index/index-f.html") match { + case Some(node) => node.toString.contains(">A") + case _ => false + }) && (pages.get("index/index-b.html") match { + case Some(node) => node.toString.contains(">bar") + case _ => false + }) + case _ => false + } + } + + property("SI-4641") = { + createReferenceIndex("SI_4641.scala") match { + case Some(pages) => pages.contains("index/index-_.html") + case _ => false + } + } + + property("SI-4421") = { + createTemplate("SI_4421.scala") match { + case node: scala.xml.Node => { + val html = node.toString + html.contains(">Example:") && html.contains(">Note<") + } + case _ => false + } + } + + property("SI-4589") = { + createTemplate("SI_4589.scala") match { + case node: scala.xml.Node => { + val html = node.toString + html.contains(">x0123456789: <") && + html.contains(">x012345678901234567890123456789: <") + } + case _ => false + } + } + + property("Should decode symbolic type alias name.") = { + createTemplate("SI_4715.scala") match { + case node: scala.xml.Node => { + val html = node.toString + html.contains(">: :+:[<") + } + case _ => false + } + } + + property("Shouldn't drop type arguments to aliased tuple.") = { + createTemplate("SI_4676.scala") match { + case node: scala.xml.Node => { + node.toString.contains(">ss: (String, String)<") + } + case _ => false + } + } + + property("Default arguments of synthesized constructor") = { + val files = createTemplates("SI_4287.scala") + + files("ClassWithSugar.html") match { + case node: scala.xml.Node => { + node.toString.contains(">123<") + } + case _ => false + } + } + + property("Default arguments of synthesized constructor") = { + createTemplate("SI_4507.scala") match { + case node: scala.xml.Node => + ! node.toString.contains("
    1. returns silently when evaluating true and true
    2. ") + case _ => false + } + } + + property("Use cases and links should not crash scaladoc") = { + createTemplate("SI_4898.scala") + true + } + + property("Use cases should override their original members") = + checkText("SI_5054_q1.scala")( + (None,"""def test(): Int""", true) + //Disabled because the full signature is now displayed + //(None,"""def test(implicit lost: Int): Int""", false) + ) + + property("Use cases should keep their flags - final should not be lost") = + checkText("SI_5054_q2.scala")((None, """final def test(): Int""", true)) + + property("Use cases should keep their flags - implicit should not be lost") = + checkText("SI_5054_q3.scala")((None, """implicit def test(): Int""", true)) + + property("Use cases should keep their flags - real abstract should not be lost") = + checkText("SI_5054_q4.scala")((None, """abstract def test(): Int""", true)) + + property("Use cases should keep their flags - traits should not be affected") = + checkText("SI_5054_q5.scala")((None, """def test(): Int""", true)) + + property("Use cases should keep their flags - traits should not be affected") = + checkText("SI_5054_q6.scala")((None, """abstract def test(): Int""", true)) + + property("Use case individual signature test") = + checkText("SI_5054_q7.scala")( + (None, """abstract def test2(explicit: Int): Int [use case] This takes the explicit value passed.""", true), + (None, """abstract def test1(): Int [use case] This takes the implicit value in scope.""", true) + ) + + property("Display correct \"Definition classes\"") = + checkText("SI_5287.scala")( + (None, + """def method(): Int + [use case] The usecase explanation + [use case] The usecase explanation + Definition Classes SI_5287 SI_5287_B SI_5287_A""", true) + ) // the explanation appears twice, as small comment and full comment + + property("Correct comment inheritance for overriding") = + checkText("implicit-inheritance-override.scala")( + (Some("Base"), + """def function[T](arg1: T, arg2: String): Double + The base comment. + The base comment. And another sentence... + T the type of the first argument + arg1 The T term comment + arg2 The string comment + returns The return comment + """, true), + (Some("DerivedA"), + """def function[T](arg1: T, arg2: String): Double + Overriding the comment, the params and returns comments should stay the same. + Overriding the comment, the params and returns comments should stay the same. + T the type of the first argument + arg1 The T term comment + arg2 The string comment + returns The return comment + """, true), + (Some("DerivedB"), + """def function[T](arg1: T, arg2: String): Double + T the type of the first argument + arg1 The overridden T term comment + arg2 The overridden string comment + returns The return comment + """, true), + (Some("DerivedC"), + """def function[T](arg1: T, arg2: String): Double + T the type of the first argument + arg1 The T term comment + arg2 The string comment + returns The overridden return comment + """, true), + (Some("DerivedD"), + """def function[T](arg1: T, arg2: String): Double + T The overriden type parameter comment + arg1 The T term comment + arg2 The string comment + returns The return comment + """, true) + ) + + for (useCaseFile <- List("UseCaseInheritance", "UseCaseOverrideInheritance")) { + property("Correct comment inheritance for usecases") = + checkText("implicit-inheritance-usecase.scala")( + (Some(useCaseFile), + """def missing_arg[T](arg1: T): Double + [use case] + [use case] + T The type parameter + arg1 The T term comment + returns The return comment + """, true), + (Some(useCaseFile), + """def missing_targ(arg1: Int, arg2: String): Double + [use case] + [use case] + arg1 The T term comment + arg2 The string comment + returns The return comment + """, true), + (Some(useCaseFile), + """def overridden_arg1[T](implicit arg1: T, arg2: String): Double + [use case] + [use case] + T The type parameter + arg1 The overridden T term comment + arg2 The string comment + returns The return comment + """, true), + (Some(useCaseFile), + """def overridden_targ[T](implicit arg1: T, arg2: String): Double + [use case] + [use case] + T The overridden type parameter comment + arg1 The T term comment + arg2 The string comment + returns The return comment + """, true), + (Some(useCaseFile), + """def overridden_return[T](implicit arg1: T, arg2: String): Double + [use case] + [use case] + T The type parameter + arg1 The T term comment + arg2 The string comment + returns The overridden return comment + """, true), + (Some(useCaseFile), + """def added_arg[T](implicit arg1: T, arg2: String, arg3: Float): Double + [use case] + [use case] + T The type parameter + arg1 The T term comment + arg2 The string comment + arg3 The added float comment + returns The return comment + """, true), + (Some(useCaseFile), + """def overridden_comment[T](implicit arg1: T, arg2: String): Double + [use case] The overridden comment. + [use case] The overridden comment. + T The type parameter + arg1 The T term comment + arg2 The string comment + returns The return comment + """, true) + ) + } + + property("Correct explicit inheritance for override") = + checkText("explicit-inheritance-override.scala")( + (Some("InheritDocDerived"), + """def function[T](arg1: T, arg2: String): Double + Starting line + Starting line + The base comment. And another sentence... + The base comment. And another sentence... + Ending line + T StartT the type of the first argument EndT + arg1 Start1 The T term comment End1 + arg2 Start2 The string comment End2 + returns StartRet The return comment EndRet""", true), + (Some("InheritDocDerived"), + """Definition Classes InheritDocDerived → InheritDocBase + Example: StartExample function[Int](3, "something") EndExample + Version StartVer 0.0.2 EndVer + Since StartSince 0.0.1 EndSince + Exceptions thrown + SomeException StartEx if the function is not called with correct parameters EndEx + SomeOtherException StartSOE Should Warn EndSOE + To do StartTodo Call mom. And dad! EndTodo + Note StartNote Be careful! EndNote + See also StartSee The Manual EndSee + """, true)) + + property("Correct explicit inheritance for usecase") = + checkText("explicit-inheritance-usecase.scala")( + (Some("UseCaseInheritDoc"), + """def function[T](arg1: T, arg2: String): Double + [use case] Starting line + [use case] Starting line + The base comment. And another sentence... + The base comment. And another sentence... + Ending line + T StartT the type of the first argument EndT + arg1 Start1 The T term comment End1 + arg2 Start2 The string comment End2 + returns StartRet The return comment EndRet""", true), + (Some("UseCaseInheritDoc"), + """Example: StartExample function[Int](3,"something") EndExample + Version StartVer 0.0.2 EndVer + Since StartSince 0.0.1 EndSince + Exceptions thrown + SomeException StartEx if the function is not called with correct parameters EndEx + SomeOtherException StartSOE Should Warn EndSOE + To do StartTodo Call mom. And dad! EndTodo + Note StartNote Be careful! EndNote + See also StartSee The Manual EndSee + """, true)) + + property("Correct explicit inheritance in corner cases") = + checkText("inheritdoc-corner-cases.scala")( + (Some("D"), + """def hello1: Int + Inherited: Hello 1 comment + Inherited: Hello 1 comment + Definition Classes D → A + """, true), + (Some("D"), + """def hello2: Int + Inherited: Hello 2 comment + Inherited: Hello 2 comment + Definition Classes D → B + """, true), + (Some("G"), + """def hello1: Int + Inherited: Hello 1 comment + Inherited: Hello 1 comment + Definition Classes G → D → A + """, true), + (Some("G"), + """def hello2: Int + Inherited: Hello 2 comment + Inherited: Hello 2 comment + Definition Classes G → D → B + """, true), + (Some("I"), + """def hello1(i: Int): Unit + [use case] Inherited: Hello 1 comment + [use case] Inherited: Hello 1 comment + Definition Classes I → G → D → A + """, true) + // traits E, F and H shouldn't crash scaladoc but we don't need to check the output + ) + + property("Indentation normalization for code blocks") = { + val files = createTemplates("code-indent.scala") + + files("C.html") match { + case node: scala.xml.Node => { + val s = node.toString + s.contains("
      a typicial indented\ncomment on multiple\ncomment lines
      ") && + s.contains("
      one liner
      ") && + s.contains("
      two lines, one useful
      ") && + s.contains("
      line1\nline2\nline3\nline4
      ") && + s.contains("
      a ragged example\na (condition)\n  the t h e n branch\nan alternative\n  the e l s e branch
      ") && + s.contains("
      l1\n\nl2\n\nl3\n\nl4\n\nl5
      ") + } + case _ => false + } + } + + { + val files = createTemplates("basic.scala") + //println(files) + + property("class") = files.get("com/example/p1/Clazz.html") match { + case Some(node: scala.xml.Node) => { + property("implicit convertion") = + node.toString contains "implicit " + + property("gt4s") = + node.toString contains "title=\"gt4s: $colon$colon\"" + + property("gt4s of a deprecated method") = + node.toString contains "title=\"gt4s: $colon$colon$colon$colon. Deprecated: " + true + } + case _ => false + } + property("package") = files.get("com/example/p1/package.html") != None + + property("package object") = files("com/example/p1/package.html") match { + case node: scala.xml.Node => + node.toString contains "com.example.p1.package#packageObjectMethod" + case _ => false + } + + property("lower bound") = files("com/example/p1/LowerBound.html") match { + case node: scala.xml.Node => true + case _ => false + } + + property("upper bound") = files("com/example/p1/UpperBound.html") match { + case node: scala.xml.Node => true + case _ => false + } + } +} diff --git a/test/scaladoc/scalacheck/IndexScriptTest.scala b/test/scaladoc/scalacheck/IndexScriptTest.scala new file mode 100644 index 0000000000..5aef38e00a --- /dev/null +++ b/test/scaladoc/scalacheck/IndexScriptTest.scala @@ -0,0 +1,52 @@ +import org.scalacheck._ +import org.scalacheck.Prop._ + +import scala.tools.nsc.doc +import scala.tools.nsc.doc.html.page.IndexScript +import java.net.{URLClassLoader, URLDecoder} + +object Test extends Properties("IndexScript") { + + def getClasspath = { + val loader = Thread.currentThread.getContextClassLoader + val paths = loader.asInstanceOf[URLClassLoader].getURLs + val morepaths = loader.getParent.asInstanceOf[URLClassLoader].getURLs + (paths ++ morepaths).map(u => URLDecoder.decode(u.getPath)).mkString(java.io.File.pathSeparator) + } + + val docFactory = { + val settings = new doc.Settings({Console.err.println(_)}) + settings.classpath.value = getClasspath + val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) + new doc.DocFactory(reporter, settings) + } + + val indexModelFactory = doc.model.IndexModelFactory + + def createIndexScript(path: String) = + docFactory.makeUniverse(Left(List(path))) match { + case Some(universe) => { + val index = new IndexScript(universe, + indexModelFactory.makeIndex(universe)) + Some(index) + } + case _ => + None + } + + property("allPackages") = { + createIndexScript("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { + case Some(index) => + index.allPackages.map(_.toString) == List( + "scala", + "scala.tools", + "scala.tools.nsc", + "scala.tools.nsc.doc", + "scala.tools.nsc.doc.html", + "scala.tools.nsc.doc.html.page" + ) + case None => + false + } + } +} diff --git a/test/scaladoc/scalacheck/IndexTest.scala b/test/scaladoc/scalacheck/IndexTest.scala new file mode 100644 index 0000000000..29e337da2b --- /dev/null +++ b/test/scaladoc/scalacheck/IndexTest.scala @@ -0,0 +1,82 @@ +import org.scalacheck._ +import org.scalacheck.Prop._ + +import scala.tools.nsc.doc +import scala.tools.nsc.doc.html.page.Index +import java.net.{URLClassLoader, URLDecoder} + +object Test extends Properties("Index") { + + def getClasspath = { + // these things can be tricky + // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths + // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. + // this test _will_ fail again some time in the future. + val paths = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) + val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) + (paths ++ morepaths).mkString(java.io.File.pathSeparator) + } + + val docFactory = { + val settings = new doc.Settings({Console.err.println(_)}) + + settings.classpath.value = getClasspath + println(settings.classpath.value) + + val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) + + new doc.DocFactory(reporter, settings) + } + + val indexModelFactory = doc.model.IndexModelFactory + + def createIndex(path: String): Option[Index] = { + + val maybeUniverse = { + //val stream = new java.io.ByteArrayOutputStream + //val original = Console.out + //Console.setOut(stream) + + val result = docFactory.makeUniverse(Left(List(path))) + + // assert(stream.toString == "model contains 2 documentable templates\n") + //Console.setOut(original) + + result + } + + maybeUniverse match { + case Some(universe) => { + val index = new Index(universe, indexModelFactory.makeIndex(universe)) + return Some(index) + } + case _ => return None + } + + } + + property("path") = { + createIndex("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { + case Some(index) => + index.path == List("index.html") + case None => false + } + } + + property("title") = { + createIndex("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { + case Some(index) => + index.title == "" + + case None => false + } + } + property("browser contants a script element") = { + createIndex("src/compiler/scala/tools/nsc/doc/html/page/Index.scala") match { + case Some(index) => + (index.browser \ "script").size == 1 + + case None => false + } + } +} -- cgit v1.2.3