scala.tools.partest.ScaladocModelTest.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-partest Show documentation
Show all versions of scala-partest Show documentation
Scala Compiler Testing Tool
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.tools.partest
import scala.tools.nsc._
import scala.tools.nsc.doc.{DocFactory, Universe}
import scala.tools.nsc.doc.base.comment._
import scala.tools.nsc.doc.model._
import scala.tools.nsc.doc.model.diagram._
import scala.tools.nsc.reporters.ConsoleReporter
import scala.util.chaining._
/** 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 {
override def code = """ ... """ // or override def resourceFile = ".scala" (from test/scaladoc/resources)
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
/** Override to feed a file in resources to scaladoc*/
def resourceFile: String = null
/** Override to feed code into scaladoc */
override def code =
if (resourceFile ne null)
io.File(s"$resourcePath/$resourceFile").slurp()
else
sys.error("Scaladoc Model Test: You need to give a file or some code to feed to scaladoc!")
def resourcePath = io.Directory(sys.props("partest.cwd") + "/../resources")
// 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 universe = model.getOrElse { sys.error("Scaladoc Model Test ERROR: No universe generated!") }
// 2 - check the model generated
testModel(universe.rootPackage)
println("Done.")
} catch {
case e: Exception =>
println(e)
e.printStackTrace
}
// set err back to the real err handler
System.setErr(prevErr)
}
private[this] var docSettings: doc.Settings = null
// custom settings, silencing "model contains X documentable templates"
override def newBaseSettings(): doc.Settings = new doc.Settings(_ => ()).tap(_.scaladocQuietRun = true)
override def newSettings(args: List[String]): doc.Settings = super.newSettings(args).asInstanceOf[doc.Settings]
override def settings: doc.Settings = newSettings(tokenize(s"$extraSettings $scaladocSettings"))
// create a new scaladoc compiler
def newDocFactory: DocFactory = {
docSettings = settings
new DocFactory(new ConsoleReporter(docSettings), docSettings)
}
// compile with scaladoc and output the result
def model: Option[Universe] = newDocFactory.makeUniverse(Right(code))
// enable easy navigation inside the entities
object access {
implicit 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).collect({ case c: DocTemplateEntity with Class => c})
def _classMbr(name: String): MemberTemplateEntity = getTheFirst(_classesMbr(name), tpl.qualifiedName + ".classMember(" + name + ")")
def _classesMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: MemberTemplateEntity if c.isClass => c})
def _trait(name: String): DocTemplateEntity = getTheFirst(_traits(name), tpl.qualifiedName + ".trait(" + name + ")")
def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: DocTemplateEntity with Trait => t})
def _annotation(name: String): DocTemplateEntity = getTheFirst(_annotations(name), tpl.qualifiedName + ".annotation(" + name + ")")
def _annotations(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: DocTemplateEntity with AnnotationClass => t})
def _traitMbr(name: String): MemberTemplateEntity = getTheFirst(_traitsMbr(name), tpl.qualifiedName + ".traitMember(" + name + ")")
def _traitsMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: MemberTemplateEntity if t.isTrait => t})
def _object(name: String): DocTemplateEntity = getTheFirst(_objects(name), tpl.qualifiedName + ".object(" + name + ")")
def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: DocTemplateEntity with Object => o})
def _objectMbr(name: String): MemberTemplateEntity = getTheFirst(_objectsMbr(name), tpl.qualifiedName + ".objectMember(" + name + ")")
def _objectsMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: MemberTemplateEntity if o.isObject => 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 _conversion(name: String): ImplicitConversion = getTheFirst(_conversions(name), tpl.qualifiedName + ".conversion(" + name + ")")
def _conversions(name: String): List[ImplicitConversion] = tpl.conversions.filter(_.conversionQualifiedName == name)
def _absType(name: String): MemberEntity = getTheFirst(_absTypes(name), tpl.qualifiedName + ".abstractType(" + name + ")")
def _absTypes(name: String): List[MemberEntity] = tpl.members.filter(mbr => mbr.name == name && mbr.isAbstractType)
def _absTypeTpl(name: String): DocTemplateEntity = getTheFirst(_absTypeTpls(name), tpl.qualifiedName + ".abstractType(" + name + ")")
def _absTypeTpls(name: String): List[DocTemplateEntity] = tpl.members.collect({ case dtpl: DocTemplateEntity with AbstractType if dtpl.name == name => dtpl })
def _aliasType(name: String): MemberEntity = getTheFirst(_aliasTypes(name), tpl.qualifiedName + ".aliasType(" + name + ")")
def _aliasTypes(name: String): List[MemberEntity] = tpl.members.filter(mbr => mbr.name == name && mbr.isAliasType)
def _aliasTypeTpl(name: String): DocTemplateEntity = getTheFirst(_aliasTypeTpls(name), tpl.qualifiedName + ".aliasType(" + name + ")")
def _aliasTypeTpls(name: String): List[DocTemplateEntity] = tpl.members.collect({ case dtpl: DocTemplateEntity with AliasType if dtpl.name == name => dtpl })
}
trait WithMembers {
def members: List[MemberEntity]
def _member(name: String): MemberEntity = getTheFirst(_members(name), this.toString + ".member(" + name + ")")
def _members(name: String): List[MemberEntity] = members.filter(_.name == name)
}
implicit 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 class DocTemplateEntityMembers(val underlying: DocTemplateEntity) extends WithMembers {
def members = underlying.members
}
implicit class ImplicitConversionMembers(val underlying: ImplicitConversion) extends WithMembers {
def members = underlying.members
}
def getTheFirst[T](list: List[T], expl: String): T = list.length match {
case 1 => list.head
case 0 => sys.error("Error getting " + expl + ": No such element.")
case _ => sys.error("Error getting " + expl + ": " + list.length + " elements with this name. " +
"All elements in list: [" + list.map({
case ent: Entity => ent.kind + " " + ent.qualifiedName
case other => other.toString
}).mkString(", ") + "]")
}
def extractCommentText(c: Any) = {
def extractText(body: Any): String = body match {
case s: String => s
case s: Seq[_] => s.toList.map(extractText(_)).mkString
case p: Product => p.productIterator.toList.map(extractText(_)).mkString
case _ => ""
}
c match {
case c: Comment => extractText(c.body)
case b: Body => extractText(b)
case x => throw new MatchError(x)
}
}
def countLinks(c: Comment, p: EntityLink => Boolean): Int = countLinksInBody(c.body, p)
def countLinksInBody(body: Body, linkTest: EntityLink => Boolean): Int = {
def countLinks(b: Any): Int = b match {
case el: EntityLink if linkTest(el) => 1
case s: collection.Seq[_] => s.toList.map(countLinks(_)).sum
case p: Product => p.productIterator.map(countLinks(_)).sum
case _ => 0
}
countLinks(body)
}
def testDiagram(doc: DocTemplateEntity, diag: Option[Diagram], nodes: Int, edges: Int) = {
assert(diag.isDefined, doc.qualifiedName + " diagram missing")
assert(diag.get.nodes.length == nodes,
doc.qualifiedName + "'s diagram: node count " + diag.get.nodes.length + " == " + nodes)
assert(diag.get.edges.map(_._2.length).sum == edges,
doc.qualifiedName + "'s diagram: edge count " + diag.get.edges.length + " == " + edges)
}
}
}