diff options
Diffstat (limited to 'src/partest-extras')
8 files changed, 259 insertions, 22 deletions
diff --git a/src/partest-extras/scala/tools/partest/ASMConverters.scala b/src/partest-extras/scala/tools/partest/ASMConverters.scala index b4c686473b..445d3c89c2 100644 --- a/src/partest-extras/scala/tools/partest/ASMConverters.scala +++ b/src/partest-extras/scala/tools/partest/ASMConverters.scala @@ -38,6 +38,28 @@ object ASMConverters { } def dropNonOp = dropLinesFrames.dropStaleLabels + + def summary: List[Any] = dropNonOp map { + case i: Invoke => i.name + case i => i.opcode + } + + def summaryText: String = { + def comment(i: Instruction) = i match { + case j: Jump => s" /*${j.label.offset}*/" + case l: Label => s" /*${l.offset}*/" + case _ => "" + } + dropNonOp.map({ + case i: Invoke => s""""${i.name}"""" + case ins => opcodeToString(ins.opcode, ins.opcode) + comment(ins) + }).mkString("List(", ", ", ")") + } + } + + def opcodeToString(op: Int, default: Any = "?"): String = { + import scala.tools.asm.util.Printer.OPCODES + if (OPCODES.isDefinedAt(op)) OPCODES(op) else default.toString } sealed abstract class Instruction extends Product { @@ -45,12 +67,9 @@ object ASMConverters { // toString such that the first field, "opcode: Int", is printed textually. final override def toString() = { - import scala.tools.asm.util.Printer.OPCODES - def opString(op: Int) = if (OPCODES.isDefinedAt(op)) OPCODES(op) else "?" val printOpcode = opcode != -1 - productPrefix + ( - if (printOpcode) Iterator(opString(opcode)) ++ productIterator.drop(1) + if (printOpcode) Iterator(opcodeToString(opcode)) ++ productIterator.drop(1) else productIterator ).mkString("(", ", ", ")") } @@ -75,7 +94,7 @@ object ASMConverters { case class FrameEntry (`type`: Int, local: List[Any], stack: List[Any]) extends Instruction { def opcode: Int = -1 } case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: Int = -1 } - case class MethodHandle(tag: Int, owner: String, name: String, desc: String) + case class MethodHandle(tag: Int, owner: String, name: String, desc: String, itf: Boolean) case class ExceptionHandler(start: Label, end: Label, handler: Label, desc: Option[String]) case class LocalVariable(name: String, desc: String, signature: Option[String], start: Label, end: Label, index: Int) @@ -128,7 +147,7 @@ object ASMConverters { case _ => a // can be: Class, method Type, primitive constant })(collection.breakOut) - private def convertMethodHandle(h: asm.Handle): MethodHandle = MethodHandle(h.getTag, h.getOwner, h.getName, h.getDesc) + private def convertMethodHandle(h: asm.Handle): MethodHandle = MethodHandle(h.getTag, h.getOwner, h.getName, h.getDesc, h.isInterface) private def convertHandlers(method: t.MethodNode): List[ExceptionHandler] = { method.tryCatchBlocks.asScala.map(h => ExceptionHandler(applyLabel(h.start), applyLabel(h.end), applyLabel(h.handler), Option(h.`type`)))(collection.breakOut) @@ -208,7 +227,7 @@ object ASMConverters { case x => x.asInstanceOf[Object] } - def unconvertMethodHandle(h: MethodHandle): asm.Handle = new asm.Handle(h.tag, h.owner, h.name, h.desc) + def unconvertMethodHandle(h: MethodHandle): asm.Handle = new asm.Handle(h.tag, h.owner, h.name, h.desc, h.itf) def unconvertBsmArgs(a: List[Object]): Array[Object] = a.map({ case h: MethodHandle => unconvertMethodHandle(h) case o => o diff --git a/src/partest-extras/scala/tools/partest/BytecodeTest.scala b/src/partest-extras/scala/tools/partest/BytecodeTest.scala index 8459419fa5..532dfd2a73 100644 --- a/src/partest-extras/scala/tools/partest/BytecodeTest.scala +++ b/src/partest-extras/scala/tools/partest/BytecodeTest.scala @@ -1,10 +1,10 @@ package scala.tools.partest -import scala.tools.nsc.util.JavaClassPath import scala.collection.JavaConverters._ -import scala.tools.asm.{ClassWriter, ClassReader} +import scala.tools.asm.{ClassReader, ClassWriter} import scala.tools.asm.tree._ -import java.io.{FileOutputStream, FileInputStream, File => JFile, InputStream} +import java.io.{InputStream, File => JFile} + import AsmNode._ /** @@ -125,12 +125,16 @@ abstract class BytecodeTest { cn } - protected lazy val classpath: JavaClassPath = { - import scala.tools.nsc.util.ClassPath.DefaultJavaContext + protected lazy val classpath: scala.tools.nsc.util.ClassPath = { + import scala.tools.nsc.classpath.AggregateClassPath + import scala.tools.nsc.classpath.ClassPathFactory import scala.tools.util.PathResolver.Defaults + import scala.tools.nsc.Settings // logic inspired by scala.tools.util.PathResolver implementation - val containers = DefaultJavaContext.classesInExpandedPath(Defaults.javaUserClassPath) - new JavaClassPath(containers, DefaultJavaContext) + // `Settings` is used to check YdisableFlatCpCaching in ZipArchiveFlatClassPath + val factory = new ClassPathFactory(new Settings()) + val containers = factory.classesInExpandedPath(Defaults.javaUserClassPath) + new AggregateClassPath(containers) } } diff --git a/src/partest-extras/scala/tools/partest/JavapTest.scala b/src/partest-extras/scala/tools/partest/JavapTest.scala index 3cb3dc6ca8..cfca49b3a7 100644 --- a/src/partest-extras/scala/tools/partest/JavapTest.scala +++ b/src/partest-extras/scala/tools/partest/JavapTest.scala @@ -1,7 +1,6 @@ package scala.tools.partest -import scala.util.{Try,Success,Failure} import java.lang.System.{out => sysout} /** A trait for testing repl's javap command @@ -9,7 +8,7 @@ import java.lang.System.{out => sysout} */ abstract class JavapTest extends ReplTest { - /** Your Assertion Here, whatever you want to bejahen. + /** Your Assertion Here, whatever you want to affirm. * Assertions must be satisfied by all flavors of javap * and should not be fragile with respect to compiler output. */ diff --git a/src/partest-extras/scala/tools/partest/ReplTest.scala b/src/partest-extras/scala/tools/partest/ReplTest.scala index 20dfe0eb16..9c95a718ca 100644 --- a/src/partest-extras/scala/tools/partest/ReplTest.scala +++ b/src/partest-extras/scala/tools/partest/ReplTest.scala @@ -7,8 +7,6 @@ package scala.tools.partest import scala.tools.nsc.Settings import scala.tools.nsc.interpreter.{ ILoop, replProps } -import java.lang.reflect.{ Method => JMethod, Field => JField } -import scala.util.matching.Regex import scala.util.matching.Regex.Match /** A class for testing repl code. @@ -76,7 +74,7 @@ abstract class SessionTest extends ReplTest { /** Code is the command list culled from the session (or the expected session output). * Would be nicer if code were lazy lines so you could generate arbitrarily long text. - * Retain user input: prompt lines and continuations, without the prefix; or pasted text plus ctl-D. + * Retain user input: prompt lines and continuations, without the prefix; or pasted text plus ctrl-D. */ import SessionTest._ lazy val pasted = input(prompt) diff --git a/src/partest-extras/scala/tools/partest/ScaladocJavaModelTest.scala b/src/partest-extras/scala/tools/partest/ScaladocJavaModelTest.scala new file mode 100644 index 0000000000..1008be5b87 --- /dev/null +++ b/src/partest-extras/scala/tools/partest/ScaladocJavaModelTest.scala @@ -0,0 +1,15 @@ +package scala.tools.partest + +import scala.tools.nsc.doc.Universe + +/** A class for testing scaladoc model generation on java sources. */ +abstract class ScaladocJavaModelTest extends ScaladocModelTest { + + // overridden to pass explicit files to newDocFactory.makeUniverse (rather than code strings) + // since the .java file extension is required + override def model: Option[Universe] = { + val path = resourcePath + "/" + resourceFile + newDocFactory.makeUniverse(Left(List(path))) + } + +} diff --git a/src/partest-extras/scala/tools/partest/ScaladocModelTest.scala b/src/partest-extras/scala/tools/partest/ScaladocModelTest.scala new file mode 100644 index 0000000000..44c1146a14 --- /dev/null +++ b/src/partest-extras/scala/tools/partest/ScaladocModelTest.scala @@ -0,0 +1,204 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Vlad Ureche + */ + +package scala.tools.partest + +import scala.tools.nsc._ +import scala.tools.cmd.CommandLineParser +import scala.tools.nsc.doc.{ DocFactory, Universe } +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.model.diagram._ +import scala.tools.nsc.doc.base.comment._ +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 { + + override def code = """ ... """ // or override def resourceFile = "<file>.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(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 settings: doc.Settings = null + + // create a new scaladoc compiler + def newDocFactory: DocFactory = { + settings = new doc.Settings(_ => ()) + settings.scaladocQuietRun = true // yaay, no more "model contains X documentable templates"! + val args = extraSettings + " " + scaladocSettings + new ScalaDoc.Command((CommandLineParser tokenize (args)), settings) // side-effecting, I think + val docFact = new DocFactory(new ConsoleReporter(settings), settings) + docFact + } + + // compile with scaladoc and output the result + def model: Option[Universe] = newDocFactory.makeUniverse(Right(code)) + + // so we don't get the newSettings warning + override def isDebug = false + + // finally, 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 _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) + } + } + + def countLinks(c: Comment, p: EntityLink => Boolean): Int = countLinksInBody(c.body, p) + + def countLinksInBody(body: Body, p: EntityLink => Boolean): Int = { + def countLinks(b: Any): Int = b match { + case el: EntityLink if p(el) => 1 + case s: Seq[_] => s.toList.map(countLinks(_)).sum + case p: Product => p.productIterator.toList.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) + } + } +} diff --git a/src/partest-extras/scala/tools/partest/SigTest.scala b/src/partest-extras/scala/tools/partest/SigTest.scala index fe233a4fb5..a516daa629 100644 --- a/src/partest-extras/scala/tools/partest/SigTest.scala +++ b/src/partest-extras/scala/tools/partest/SigTest.scala @@ -5,8 +5,6 @@ package scala.tools.partest -import scala.tools.nsc.Settings -import scala.tools.nsc.interpreter.ILoop import java.lang.reflect.{ Method => JMethod, Field => JField } import scala.reflect.{ClassTag, classTag} diff --git a/src/partest-extras/scala/tools/partest/Util.scala b/src/partest-extras/scala/tools/partest/Util.scala index 60e9dbb0f9..511997ea35 100644 --- a/src/partest-extras/scala/tools/partest/Util.scala +++ b/src/partest-extras/scala/tools/partest/Util.scala @@ -14,7 +14,7 @@ object Util { * An alternative to [[scala.tools.partest.ReplTest]] that avoids the inconvenience of embedding * test code in a string. */ - def trace[A](a: A) = macro traceImpl[A] + def trace[A](a: A): A = macro traceImpl[A] import scala.reflect.macros.blackbox.Context def traceImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]): c.Expr[A] = { |