From 9c215faff7666ff698605478cf43abc98ee7c068 Mon Sep 17 00:00:00 2001 From: dragos Date: Mon, 24 Jan 2011 19:17:32 +0000 Subject: [PATCH] Added presentation compiler tests. review by phaller, extempore. git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@24075 5e8d7ff9-d8ef-0310-90f0-a4852d11357a --- build.xml | 7 +- .../interactive/tests/InteractiveTest.scala | 175 +++++++++++++++ .../tools/nsc/interactive/tests/Tester.scala | 2 +- .../scala/tools/partest/PartestTask.scala | 25 ++- .../tools/partest/nest/CompileManager.scala | 17 +- .../tools/partest/nest/ConsoleRunner.scala | 6 +- .../scala/tools/partest/nest/TestFile.scala | 1 + .../scala/tools/partest/nest/Worker.scala | 6 +- test/files/presentation/find-trees.check | 8 + .../presentation/find-trees/FindTrees.scala | 43 ++++ .../find-trees/src/InteractiveTest.scala | 36 ++++ test/files/presentation/simple-tests.check | 191 ++++++++++++++++ .../simple-tests/SimpleInteractiveTest.scala | 9 + .../simple-tests/src/Tester.scala | 204 ++++++++++++++++++ 14 files changed, 713 insertions(+), 17 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala create mode 100644 test/files/presentation/find-trees.check create mode 100644 test/files/presentation/find-trees/FindTrees.scala create mode 100644 test/files/presentation/find-trees/src/InteractiveTest.scala create mode 100644 test/files/presentation/simple-tests.check create mode 100644 test/files/presentation/simple-tests/SimpleInteractiveTest.scala create mode 100644 test/files/presentation/simple-tests/src/Tester.scala diff --git a/build.xml b/build.xml index 8ac74e546..0479a6745 100644 --- a/build.xml +++ b/build.xml @@ -1567,11 +1567,14 @@ BOOTRAPING TEST AND TEST SUITE - + - + + + + diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala new file mode 100644 index 000000000..9c568a462 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -0,0 +1,175 @@ +package scala.tools.nsc.interactive +package tests + +import scala.tools.nsc.Settings +import scala.tools.nsc.reporters.StoreReporter +import scala.tools.nsc.util.{BatchSourceFile, SourceFile, Position} +import scala.tools.nsc.io._ + +import scala.collection.{immutable, mutable} + +/** A base class for writing interactive compiler tests. + * + * This class tries to cover common functionality needed when testing the presentation + * compiler: instantiation source files, reloading, creating positions, instantiating + * the presentation compiler, random stress testing. + * + * By default, this class loads all classes found under `src/`. They are found in + * `sourceFiles`. Positions can be created using `pos(file, line, col)`. The presentation + * compiler is available through `compiler`. + * + * It is easy to test member completion and type at a given position. Source + * files are searched for /markers/. By default, the completion marker is `/*!*/` and the + * typedAt marker is `/*?*/`. Place these markers in your source files, and call `completionTests` + * and `typedAtTests` to print the results at all these positions. Sources are reloaded by `reloadSources` + * (blocking call). All ask operations are placed on the work queue without waiting for each one to + * complete before asking the next. After all asks, it waits for each response in turn and prints the result. + * The default timout is 5 seconds per operation. + * + * The same mechanism can be used for custom operations. Use `askAllSources(marker)(f)(g)`. Give your custom + * marker, and provide the two functions: one for creating the request, and the second for processing the + * response, if it didn't time out and there was no error. + * + * @see Check existing tests under test/files/presentation + * + * @author Iulian Dragos + */ +abstract class InteractiveTest { + + val completionMarker = "/*!*/" + val typedAtMarker = "/*?*/" + val TIMEOUT = 5000 // 5 seconds + + val settings = new Settings + val reporter= new StoreReporter + + // need this so that the classpath comes from what partest + // instead of scala.home + settings.usejavacp.value = true + + /** The root directory for this test suite, usually the test kind ("test/files/presentation"). */ + val outDir = Path(Option(System.getProperty("partest.cwd")).getOrElse(".")) + + /** The base directory for this test, usually a subdirectory of "test/files/presentation/" */ + val baseDir = Option(System.getProperty("partest.testname")).map(outDir / _).getOrElse(Path(".")) + +// settings.YpresentationDebug.value = true + lazy val compiler = new Global(settings, reporter) + + def sources(filename: String*): Seq[SourceFile] = + for (f <- filename) yield + source(if (f.startsWith("/")) Path(f) else baseDir / f) + + def source(file: Path) = new BatchSourceFile(AbstractFile.getFile(file.toFile)) + def source(filename: String): SourceFile = new BatchSourceFile(AbstractFile.getFile(filename)) + + def pos(file: SourceFile, line: Int, col: Int): Position = + file.position(line, col) + + def filesInDir(dir: Path): Iterator[Path] = { + dir.toDirectory.list.filter(_.isFile) + } + + /** Where source files are placed. */ + val sourceDir = "src" + + /** All .scala files below "src" directory. */ + lazy val sourceFiles: Array[SourceFile] = + filesInDir(baseDir / sourceDir).filter(_.extension == "scala").map(source).toArray + + /** All positions of the given string in all source files. */ + def allPositionsOf(sources: Seq[SourceFile] = sourceFiles, str: String): immutable.Map[SourceFile, Seq[Position]] = { + (for (s <- sources; p <- positionsOf(s, str)) yield p).groupBy(_.source) + } + + /** Return all positions of the given str in the given source file. */ + def positionsOf(source: SourceFile, str: String): Seq[Position] = { + val buf = new mutable.ListBuffer[Position] + var pos = source.content.indexOfSlice(str) + while (pos >= 0) { +// buf += compiler.rangePos(source, pos - 1, pos - 1, pos - 1) + buf += source.position(pos - 1) // we need the position before the first character of this marker + pos = source.content.indexOfSlice(str, pos + 1) + } + buf.toList + } + + /** Perform an operation on all sources at all positions that match the given + * marker string. For instance, askAllSources("/*!*/")(askTypeAt)(println) would + * ask the tyep at all positions marked with /*!*/ and println the result. + */ + def askAllSources[T](marker: String)(askAt: Position => Response[T])(f: (Position, T) => Unit) { + val positions = allPositionsOf(str = marker).valuesIterator.toList.flatten + val responses = for (pos <- positions) yield askAt(pos) + + for ((pos, r) <- positions zip responses) r.get(TIMEOUT) match { + case Some(Left(members)) => + f(pos, members) + case None => + println("TIMEOUT: " + r) + case _ => + println("ERROR: " + r) + } + } + + /** Ask completion for all marked positions in all sources. + * A completion position is marked with /*!*/. + */ + def completionTests() { + askAllSources(completionMarker) { pos => + println("askTypeCompletion at " + pos) + val r = new Response[List[compiler.Member]] + compiler.askTypeCompletion(pos, r) + r + } { (pos, members) => + println("\n" + "=" * 80) + println("[response] aksTypeCompletion at " + (pos.line, pos.column)) + println(members.sortBy(_.sym.name.toString).mkString("\n")) + } + } + + /** Ask for typedAt for all marker positions in all sources. + */ + def typeAtTests() { + askAllSources(typedAtMarker) { pos => + println("askTypeAt at " + pos) + val r = new Response[compiler.Tree] + compiler.askTypeAt(pos, r) + r + } { (pos, tree) => + println("[response] askTypeAt at " + (pos.line, pos.column)) + println(tree) + } + } + + /** Reload the given source files and wait for them to be reloaded. */ + def reloadSources(sources: Seq[SourceFile] = sourceFiles) { +// println("basedir: " + baseDir.path) +// println("sourcedir: " + (baseDir / sourceDir).path) + println("reload: " + sourceFiles.mkString("", ", ", "")) + val reload = new Response[Unit] + compiler.askReload(sourceFiles.toList, reload) + reload.get + } + + def runTest: Unit = { + if (runRandomTests) randomTests(20, sourceFiles) + completionTests() + typeAtTests() + } + + /** Perform n random tests with random changes. */ + def randomTests(n: Int, files: Array[SourceFile]) { + val tester = new Tester(n, files, settings) + tester.run() + } + + val runRandomTests = true + + def main(args: Array[String]) { + reloadSources() + runTest + compiler.askShutdown() + } +} + diff --git a/src/compiler/scala/tools/nsc/interactive/tests/Tester.scala b/src/compiler/scala/tools/nsc/interactive/tests/Tester.scala index 87b110df4..3d3af809a 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/Tester.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/Tester.scala @@ -153,7 +153,7 @@ class Tester(ntests: Int, inputs: Array[SourceFile], settings: Settings) { changes foreach (_.deleteAll()) otherTest() def errorCount() = compiler.ask(() => reporter.ERROR.count) - println("\nhalf test round: "+errorCount()) +// println("\nhalf test round: "+errorCount()) changes.view.reverse foreach (_.insertAll()) otherTest() println("done test round: "+errorCount()) diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala index 9000b886c..83b9d7ae6 100644 --- a/src/partest/scala/tools/partest/PartestTask.scala +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -71,6 +71,11 @@ class PartestTask extends Task with CompilationPathProperty { def addConfiguredSpecializedTests(input: FileSet) { specializedFiles = Some(input) } + + def addConfiguredPresentationTests(input: FileSet) { + presentationFiles = Some(input) + } + def setSrcDir(input: String) { srcDir = Some(input) @@ -146,6 +151,7 @@ class PartestTask extends Task with CompilationPathProperty { private var shootoutFiles: Option[FileSet] = None private var scalapFiles: Option[FileSet] = None private var specializedFiles: Option[FileSet] = None + private var presentationFiles: Option[FileSet] = None private var errorOnFailed: Boolean = false private var scalacOpts: Option[String] = None private var timeout: Option[String] = None @@ -170,14 +176,25 @@ class PartestTask extends Task with CompilationPathProperty { // println("----> " + fileSet) val fileTests = getFiles(Some(fs)) filterNot (x => shouldExclude(x.getName)) - val dirTests: Iterator[SPath] = fileSetToDir(fs).dirs filterNot (x => shouldExclude(x.name)) - val dirResult = dirTests.toList.toArray map (_.jfile) + val dirResult = getDirs(Some(fs)) filterNot (x => shouldExclude(x.getName)) // println("dirs: " + dirResult.toList) // println("files: " + fileTests.toList) dirResult ++ fileTests } + private def getDirs(fileSet: Option[FileSet]): Array[File] = fileSet match { + case None => Array() + case Some(fs) => + def shouldExclude(name: String) = (name endsWith ".obj") || (name startsWith ".") + + val dirTests: Iterator[SPath] = fileSetToDir(fs).dirs filterNot (x => shouldExclude(x.name)) + val dirResult = dirTests.toList.toArray map (_.jfile) + + dirResult + } + + private def getPosFiles = getFilesAndDirs(posFiles) private def getNegFiles = getFilesAndDirs(negFiles) private def getRunFiles = getFilesAndDirs(runFiles) @@ -189,6 +206,7 @@ class PartestTask extends Task with CompilationPathProperty { private def getShootoutFiles = getFiles(shootoutFiles) private def getScalapFiles = getFiles(scalapFiles) private def getSpecializedFiles = getFiles(specializedFiles) + private def getPresentationFiles = getDirs(presentationFiles) override def execute() { if (isPartestDebug || debug) { @@ -236,7 +254,8 @@ class PartestTask extends Task with CompilationPathProperty { (getScriptFiles, "script", "Running script files"), (getShootoutFiles, "shootout", "Running shootout tests"), (getScalapFiles, "scalap", "Running scalap tests"), - (getSpecializedFiles, "specialized", "Running specialized files") + (getSpecializedFiles, "specialized", "Running specialized files"), + (getPresentationFiles, "presentation", "Running presentation compiler test files") ) def runSet(set: TFSet): (Int, Int, Iterable[String]) = { diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala index fbbb47ada..089013782 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -84,14 +84,15 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { val testRep: ExtConsoleReporter = global.reporter.asInstanceOf[ExtConsoleReporter] val testFileFn: (File, FileManager) => TestFile = kind match { - case "pos" => PosTestFile.apply - case "neg" => NegTestFile.apply - case "run" => RunTestFile.apply - case "jvm" => JvmTestFile.apply - case "shootout" => ShootoutTestFile.apply - case "scalap" => ScalapTestFile.apply - case "scalacheck" => ScalaCheckTestFile.apply - case "specialized" => SpecializedTestFile.apply + case "pos" => PosTestFile.apply + case "neg" => NegTestFile.apply + case "run" => RunTestFile.apply + case "jvm" => JvmTestFile.apply + case "shootout" => ShootoutTestFile.apply + case "scalap" => ScalapTestFile.apply + case "scalacheck" => ScalaCheckTestFile.apply + case "specialized" => SpecializedTestFile.apply + case "presentation" => PresentationTestFile.apply } val test: TestFile = testFileFn(files.head, fileManager) test.defineSettings(command.settings, out.isEmpty) diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index dd36f5ac0..019f32b68 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -36,7 +36,8 @@ class ConsoleRunner extends DirectRunner { TestSet("script", pathFilter, "Testing script tests"), TestSet("scalacheck", x => pathFilter(x) || x.isDirectory, "Testing ScalaCheck tests"), TestSet("scalap", _.isDirectory, "Run scalap decompiler tests"), - TestSet("specialized", pathFilter, "Testing specialized tests") + TestSet("specialized", pathFilter, "Testing specialized tests"), + TestSet("presentation", _.isDirectory, "Testing presentation compiler tests.") ) } @@ -57,7 +58,7 @@ class ConsoleRunner extends DirectRunner { private val unaryArgs = List( "--pack", "--all", "--verbose", "--show-diff", "--show-log", - "--failed", "--update-check", "--version", "--ansi", "--debug" + "--failed", "--update-check", "--version", "--ansi", "--debug", "--help" ) ::: testSetArgs private val binaryArgs = List( @@ -71,6 +72,7 @@ class ConsoleRunner extends DirectRunner { /** Early return on no args, version, or invalid args */ if (argstr == "") return NestUI.usage() if (parsed isSet "--version") return printVersion + if (parsed isSet "--help") return NestUI.usage() if (args exists (x => !denotesTestPath(x))) { val invalid = (args filterNot denotesTestPath).head NestUI.failure("Invalid argument '%s'\n" format invalid) diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala index 8ac95d6ac..140e4218e 100644 --- a/src/partest/scala/tools/partest/nest/TestFile.scala +++ b/src/partest/scala/tools/partest/nest/TestFile.scala @@ -55,3 +55,4 @@ case class SpecializedTestFile(file: JFile, fileManager: FileManager) extends Te settings.classpath.value = ClassPath.join(PathSettings.srcSpecLib.toString, settings.classpath.value) } } +case class PresentationTestFile(file: JFile, fileManager: FileManager) extends TestFile("presentation") diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 2ccbd6884..bd3ac9dfc 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -315,6 +315,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor "-Dpartest.output="+outDir.getAbsolutePath, "-Dpartest.lib="+LATEST_LIB, "-Dpartest.cwd="+outDir.getParent, + "-Dpartest.testname="+fileBase, "-Djavacmd="+JAVACMD, "-Djavaccmd="+javacCmd, "-Duser.language=en -Duser.country=US" @@ -540,7 +541,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor execTest(outDir, logFile, PathSettings.srcSpecLib.toString) diffCheck(compareOutput(dir, logFile)) }) - + def processSingleFile(file: File): LogContext = kind match { case "scalacheck" => val succFn: (File, File) => Boolean = { (logFile, outDir) => @@ -592,6 +593,9 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor case "specialized" => runSpecializedTest(file) + + case "presentation" => + runJvmTest(file) // for the moment, it's exactly the same as for a run test case "buildmanager" => val logFile = createLogFile(file) diff --git a/test/files/presentation/find-trees.check b/test/files/presentation/find-trees.check new file mode 100644 index 000000000..73405e818 --- /dev/null +++ b/test/files/presentation/find-trees.check @@ -0,0 +1,8 @@ +reload: InteractiveTest.scala +asking position at 19:4 +retrieved tree: InteractiveTest.this.settings + +==================== +asking position at 20:16 +retrieved tree: lazy private[this] var compiler: scala.tools.nsc.interactive.CompilerControl = new Global(InteractiveTest.this.settings, InteractiveTest.this.reporter) + diff --git a/test/files/presentation/find-trees/FindTrees.scala b/test/files/presentation/find-trees/FindTrees.scala new file mode 100644 index 000000000..8371c7fd2 --- /dev/null +++ b/test/files/presentation/find-trees/FindTrees.scala @@ -0,0 +1,43 @@ +import scala.tools.nsc.interactive.tests.InteractiveTest +import scala.tools.nsc.util.Position + + +/** Example interactive test that does everything by hand. It would be much simpler + * to just add the markers in the test file. This test shows how to drive + * the presentation compiler manually. + */ +object Test extends InteractiveTest { + + def askForPos(pos: Position) { + import compiler._ + val response = new Response[Tree] + + println("asking position at %d:%d".format(pos.line, pos.column)) + compiler.askTypeAt(pos, response) + response.get match { + case Left(EmptyTree) => + println("error retrieving tree at %d:%d".format(pos.line, pos.column)) + case Left(t) => + println("retrieved tree: " + t) + } + println(this.reporter.infos.mkString("\n")) + } + + override def runTest { + import compiler._ + val src = sourceFiles(0) // only one under src/ + val pos = rangePos(src, 426, 426, 433) + val pos1 = src.position(19, 15) + + // reload is issued already by the framework, but we can redo it here as an example + val reload = new Response[Unit] + compiler.askReload(List(src), reload) + reload.get // it's important to let reload finish before asking other things. + + askForPos(pos) + println("=" * 20) + askForPos(pos1) + + compiler.askShutdown() + } +} diff --git a/test/files/presentation/find-trees/src/InteractiveTest.scala b/test/files/presentation/find-trees/src/InteractiveTest.scala new file mode 100644 index 000000000..5a0dab990 --- /dev/null +++ b/test/files/presentation/find-trees/src/InteractiveTest.scala @@ -0,0 +1,36 @@ +package scala.tools.nsc.interactive +package tests + +import scala.tools.nsc.Settings +import scala.tools.nsc.reporters.StoreReporter +import scala.tools.nsc.util.{BatchSourceFile, SourceFile, Position} +import scala.tools.nsc.io._ + +/** A base class for writing interactive compiler tests. + * + * @author Iulian Dragos + * + */ +abstract class InteractiveTest { + + val settings = new Settings + val reporter= new StoreReporter + + settings.YpresentationDebug.value = true + lazy val compiler: CompilerControl = new Global(settings, reporter) + + def sources(filename: String*): Seq[SourceFile] = + filename map source + + def source(filename: String) = new BatchSourceFile(AbstractFile.getFile(filename)) + + def pos(filename: String, line: Int, col: Int): Position = + source(filename).position(line, col) + + def runTest: Unit + + def main(args: Array[String]) { + runTest + } +} + diff --git a/test/files/presentation/simple-tests.check b/test/files/presentation/simple-tests.check new file mode 100644 index 000000000..80bbdee46 --- /dev/null +++ b/test/files/presentation/simple-tests.check @@ -0,0 +1,191 @@ +reload: Tester.scala +askTypeCompletion at source-/Users/dragos/workspace/git/scala/test/files/presentation/simple-tests/src/Tester.scala,line-12,offset=268 +askTypeCompletion at source-/Users/dragos/workspace/git/scala/test/files/presentation/simple-tests/src/Tester.scala,line-23,offset=722 +askTypeCompletion at source-/Users/dragos/workspace/git/scala/test/files/presentation/simple-tests/src/Tester.scala,line-105,offset=3041 + +================================================================================ +[response] aksTypeCompletion at (12,39) +TypeMember(method $asInstanceOf,[T0]()T0,true,false,method any2stringadd) +TypeMember(method !=,(x$1: AnyRef)Boolean,true,false,method any2stringadd) +TypeMember(method !=,(x$1: Any)Boolean,true,false,method any2stringadd) +TypeMember(method ==,(x$1: AnyRef)Boolean,true,false,method any2stringadd) +TypeMember(method ==,(x$1: Any)Boolean,true,false,method any2stringadd) +TypeMember(method ##,()Int,true,false,method any2stringadd) +TypeMember(method $isInstanceOf,[T0]()Boolean,true,false,method any2stringadd) +TypeMember(method ->,[B](y: B)(scala.tools.nsc.reporters.StoreReporter, B),true,false,method any2ArrowAssoc) +TypeMember(method +,(other: String)java.lang.String,true,false,method any2stringadd) +TypeMember(method ?,[B](y: B)(scala.tools.nsc.reporters.StoreReporter, B),true,false,method any2ArrowAssoc) +TypeMember(constructor StoreReporter,()scala.tools.nsc.reporters.StoreReporter,true,false,) +TypeMember(constructor StringAdd,(self: Any)scala.runtime.StringAdd,true,false,method any2stringadd) +TypeMember(constructor ArrowAssoc,(x: scala.tools.nsc.reporters.StoreReporter)ArrowAssoc[scala.tools.nsc.reporters.StoreReporter],true,false,method any2ArrowAssoc) +TypeMember(value ERROR,=> scala.tools.nsc.reporters.StoreReporter#Severity,true,true,) +TypeMember(value INFO,=> scala.tools.nsc.reporters.StoreReporter#Severity,true,true,) +TypeMember(class Info,scala.tools.nsc.reporters.StoreReporter#Info,true,false,) +TypeMember(class Severity,scala.tools.nsc.reporters.StoreReporter#Severity,true,true,) +TypeMember(value WARNING,=> scala.tools.nsc.reporters.StoreReporter#Severity,true,true,) +TypeMember(method _truncationOK,=> Boolean,false,true,) +TypeMember(method asInstanceOf,[T0]=> T0,true,true,) +TypeMember(method cancelled,=> Boolean,true,true,) +TypeMember(method clone,()java.lang.Object,false,true,) +TypeMember(method comment,(pos: scala.tools.nsc.util.Position,msg: String)Unit,true,true,) +TypeMember(method countAsString,(n: Int)String,true,true,) +TypeMember(method countElementsAsString,(n: Int,elements: String)String,true,true,) +TypeMember(method ensuring,(cond: (scala.tools.nsc.reporters.StoreReporter) => Boolean,msg: => Any)scala.tools.nsc.reporters.StoreReporter,true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: (scala.tools.nsc.reporters.StoreReporter) => Boolean)scala.tools.nsc.reporters.StoreReporter,true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: Boolean,msg: => Any)scala.tools.nsc.reporters.StoreReporter,true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: Boolean)scala.tools.nsc.reporters.StoreReporter,true,false,method any2Ensuring) +TypeMember(method eq,(x$1: AnyRef)Boolean,true,true,) +TypeMember(method equals,(x$1: Any)Boolean,true,true,) +TypeMember(method error,(pos: scala.tools.nsc.util.Position,msg: String)Unit,true,true,) +TypeMember(method finalize,()Unit,false,true,) +TypeMember(method flush,()Unit,true,true,) +TypeMember(method formatted,(fmtstr: String)String,true,false,method any2stringadd) +TypeMember(method getClass,()java.lang.Class[_],true,true,) +TypeMember(method getSource,=> scala.tools.nsc.util.SourceFile,true,true,) +TypeMember(method hasErrors,=> Boolean,true,true,) +TypeMember(method hasWarnings,=> Boolean,true,true,) +TypeMember(method hashCode,()Int,true,true,) +TypeMember(method incompleteHandled,=> Boolean,true,true,) +TypeMember(method incompleteInputError,=> (scala.tools.nsc.util.Position, String) => Unit,true,true,) +TypeMember(method info,(pos: scala.tools.nsc.util.Position,msg: String,force: Boolean)Unit,true,true,) +TypeMember(method info0,((pos: scala.tools.nsc.util.Position,msg: java.lang.String,severity: _5.Severity,force: Boolean)Unit) forSome { val _5: scala.tools.nsc.reporters.StoreReporter },false,false,) +TypeMember(value infos,(=> scala.collection.mutable.HashSet[_3.Info]) forSome { val _3: scala.tools.nsc.reporters.StoreReporter },true,false,) +TypeMember(value infos,scala.collection.mutable.HashSet[_4.Info] forSome { val _4: scala.tools.nsc.reporters.StoreReporter },false,false,) +TypeMember(method isInstanceOf,[T0]=> Boolean,true,true,) +TypeMember(method ne,(x$1: AnyRef)Boolean,true,true,) +TypeMember(method notify,()Unit,true,true,) +TypeMember(method notifyAll,()Unit,true,true,) +TypeMember(method reset,()Unit,true,false,) +TypeMember(value self,Any,false,false,method any2stringadd) +TypeMember(method setSource,(source: scala.tools.nsc.util.SourceFile)Unit,true,true,) +TypeMember(object severity,object scala.tools.nsc.reporters.StoreReporter#severity,true,true,) +TypeMember(method source,=> scala.tools.nsc.util.SourceFile,false,true,) +TypeMember(method synchronized,[T0](x$1: T0)T0,true,true,) +TypeMember(method toString,()java.lang.String,true,true,) +TypeMember(method truncationOK,=> Boolean,true,true,) +TypeMember(method wait,()Unit,true,true,) +TypeMember(method wait,(x$1: Long,x$2: Int)Unit,true,true,) +TypeMember(method wait,(x$1: Long)Unit,true,true,) +TypeMember(method warning,(pos: scala.tools.nsc.util.Position,msg: String)Unit,true,true,) +TypeMember(method withIncompleteHandler,[T](handler: (scala.tools.nsc.util.Position, String) => Unit)(thunk: => T)T,true,true,) +TypeMember(method withSource,[A](src: scala.tools.nsc.util.SourceFile)(op: => A)A,true,true,) +TypeMember(method withoutTruncating,[T](body: => T)T,true,true,) +TypeMember(value x,=> scala.tools.nsc.reporters.StoreReporter,true,false,method any2ArrowAssoc) +TypeMember(value x,scala.tools.nsc.reporters.StoreReporter,false,false,method any2ArrowAssoc) + +================================================================================ +[response] aksTypeCompletion at (23,24) +TypeMember(method $asInstanceOf,[T0]()T0,true,false,method any2stringadd) +TypeMember(method !=,(x$1: AnyRef)Boolean,true,false,method any2stringadd) +TypeMember(method !=,(x$1: Any)Boolean,true,false,method any2stringadd) +TypeMember(method ==,(x$1: AnyRef)Boolean,true,false,method any2stringadd) +TypeMember(method ==,(x$1: Any)Boolean,true,false,method any2stringadd) +TypeMember(method ##,()Int,true,false,method any2stringadd) +TypeMember(method $isInstanceOf,[T0]()Boolean,true,false,method any2stringadd) +TypeMember(method ->,[B](y: B)(scala.tools.nsc.interactive.Response[U], B),true,false,method any2ArrowAssoc) +TypeMember(method +,(other: String)java.lang.String,true,false,method any2stringadd) +TypeMember(method ?,[B](y: B)(scala.tools.nsc.interactive.Response[U], B),true,false,method any2ArrowAssoc) +TypeMember(constructor Response,()scala.tools.nsc.interactive.Response[U],true,false,) +TypeMember(constructor StringAdd,(self: Any)scala.runtime.StringAdd,true,false,method any2stringadd) +TypeMember(constructor ArrowAssoc,(x: scala.tools.nsc.interactive.Response[U])ArrowAssoc[scala.tools.nsc.interactive.Response[U]],true,false,method any2ArrowAssoc) +TypeMember(method asInstanceOf,[T0]=> T0,true,true,) +TypeMember(method cancel,()Unit,true,false,) +TypeMember(method cancelled,=> Boolean,false,false,) +TypeMember(variable cancelled,Boolean,false,false,) +TypeMember(method clear,()Unit,true,false,) +TypeMember(method clone,()java.lang.Object,false,true,) +TypeMember(method complete,=> Boolean,false,false,) +TypeMember(variable complete,Boolean,false,false,) +TypeMember(method data,=> Option[Either[U,Throwable]],false,false,) +TypeMember(variable data,Option[Either[U,Throwable]],false,false,) +TypeMember(method ensuring,(cond: (scala.tools.nsc.interactive.Response[U]) => Boolean,msg: => Any)scala.tools.nsc.interactive.Response[U],true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: (scala.tools.nsc.interactive.Response[U]) => Boolean)scala.tools.nsc.interactive.Response[U],true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: Boolean,msg: => Any)scala.tools.nsc.interactive.Response[U],true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: Boolean)scala.tools.nsc.interactive.Response[U],true,false,method any2Ensuring) +TypeMember(method eq,(x$1: AnyRef)Boolean,true,true,) +TypeMember(method equals,(x$1: Any)Boolean,true,true,) +TypeMember(method finalize,()Unit,false,true,) +TypeMember(method formatted,(fmtstr: String)String,true,false,method any2stringadd) +TypeMember(method get,=> Either[U,Throwable],true,false,) +TypeMember(method get,(timeout: Long)Option[Either[U,Throwable]],true,false,) +TypeMember(method getClass,()java.lang.Class[_],true,true,) +TypeMember(method hashCode,()Int,true,true,) +TypeMember(method isCancelled,=> Boolean,true,false,) +TypeMember(method isComplete,=> Boolean,true,false,) +TypeMember(method isInstanceOf,[T0]=> Boolean,true,true,) +TypeMember(method ne,(x$1: AnyRef)Boolean,true,true,) +TypeMember(method notify,()Unit,true,true,) +TypeMember(method notifyAll,()Unit,true,true,) +TypeMember(method raise,(exc: Throwable)Unit,true,false,) +TypeMember(value self,Any,false,false,method any2stringadd) +TypeMember(method set,(x: U)Unit,true,false,) +TypeMember(method setProvisionally,(x: U)Unit,true,false,) +TypeMember(method synchronized,[T0](x$1: T0)T0,true,true,) +TypeMember(method toString,()java.lang.String,true,true,) +TypeMember(method wait,()Unit,true,true,) +TypeMember(method wait,(x$1: Long,x$2: Int)Unit,true,true,) +TypeMember(method wait,(x$1: Long)Unit,true,true,) +TypeMember(value x,=> scala.tools.nsc.interactive.Response[U],true,false,method any2ArrowAssoc) +TypeMember(value x,scala.tools.nsc.interactive.Response[U],false,false,method any2ArrowAssoc) + +================================================================================ +[response] aksTypeCompletion at (105,29) +TypeMember(method $asInstanceOf,[T0]()T0,true,false,method any2stringadd) +TypeMember(method !=,(x$1: AnyRef)Boolean,true,false,method any2stringadd) +TypeMember(method !=,(x$1: Any)Boolean,true,false,method any2stringadd) +TypeMember(method ==,(x$1: AnyRef)Boolean,true,false,method any2stringadd) +TypeMember(method ==,(x$1: Any)Boolean,true,false,method any2stringadd) +TypeMember(method ##,()Int,true,false,method any2stringadd) +TypeMember(method $isInstanceOf,[T0]()Boolean,true,false,method any2stringadd) +TypeMember(method ->,[B](y: B)(scala.tools.nsc.util.SourceFile, B),true,false,method any2ArrowAssoc) +TypeMember(method +,(other: String)java.lang.String,true,false,method any2stringadd) +TypeMember(method ?,[B](y: B)(scala.tools.nsc.util.SourceFile, B),true,false,method any2ArrowAssoc) +TypeMember(constructor SourceFile,()scala.tools.nsc.util.SourceFile,true,false,) +TypeMember(constructor StringAdd,(self: Any)scala.runtime.StringAdd,true,false,method any2stringadd) +TypeMember(constructor ArrowAssoc,(x: scala.tools.nsc.util.SourceFile)ArrowAssoc[scala.tools.nsc.util.SourceFile],true,false,method any2ArrowAssoc) +TypeMember(method asInstanceOf,[T0]=> T0,true,true,) +TypeMember(method beginsWith,(offset: Int,text: String)Boolean,true,false,) +TypeMember(method clone,()java.lang.Object,false,true,) +TypeMember(method content,=> Array[Char],true,false,) +TypeMember(method dbg,(offset: Int)java.lang.String,true,false,) +TypeMember(method ensuring,(cond: (scala.tools.nsc.util.SourceFile) => Boolean,msg: => Any)scala.tools.nsc.util.SourceFile,true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: (scala.tools.nsc.util.SourceFile) => Boolean)scala.tools.nsc.util.SourceFile,true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: Boolean,msg: => Any)scala.tools.nsc.util.SourceFile,true,false,method any2Ensuring) +TypeMember(method ensuring,(cond: Boolean)scala.tools.nsc.util.SourceFile,true,false,method any2Ensuring) +TypeMember(method eq,(x$1: AnyRef)Boolean,true,true,) +TypeMember(method equals,(x$1: Any)Boolean,true,true,) +TypeMember(method file,=> scala.tools.nsc.io.AbstractFile,true,false,) +TypeMember(method finalize,()Unit,false,true,) +TypeMember(method formatted,(fmtstr: String)String,true,false,method any2stringadd) +TypeMember(method getClass,()java.lang.Class[_],true,true,) +TypeMember(method hashCode,()Int,true,true,) +TypeMember(method identifier,(pos: scala.tools.nsc.util.Position,compiler: scala.tools.nsc.Global)Option[String],true,false,) +TypeMember(method isInstanceOf,[T0]=> Boolean,true,true,) +TypeMember(method isLineBreak,(idx: Int)Boolean,true,false,) +TypeMember(method isSelfContained,=> Boolean,true,false,) +TypeMember(method length,=> Int,true,false,) +TypeMember(method lineToOffset,(index: Int)Int,true,false,) +TypeMember(method lineToString,(index: Int)String,true,false,) +TypeMember(method ne,(x$1: AnyRef)Boolean,true,true,) +TypeMember(method notify,()Unit,true,true,) +TypeMember(method notifyAll,()Unit,true,true,) +TypeMember(method offsetToLine,(offset: Int)Int,true,false,) +TypeMember(method path,=> String,true,false,) +TypeMember(method position,(offset: Int)scala.tools.nsc.util.Position,true,false,) +TypeMember(method position,(line: Int,column: Int)scala.tools.nsc.util.Position,true,false,) +TypeMember(method positionInUltimateSource,(position: scala.tools.nsc.util.Position)scala.tools.nsc.util.Position,true,false,) +TypeMember(value self,Any,false,false,method any2stringadd) +TypeMember(method skipWhitespace,(offset: Int)Int,true,false,) +TypeMember(method synchronized,[T0](x$1: T0)T0,true,true,) +TypeMember(method toString,()String,true,false,) +TypeMember(method wait,()Unit,true,true,) +TypeMember(method wait,(x$1: Long,x$2: Int)Unit,true,true,) +TypeMember(method wait,(x$1: Long)Unit,true,true,) +TypeMember(value x,=> scala.tools.nsc.util.SourceFile,true,false,method any2ArrowAssoc) +TypeMember(value x,scala.tools.nsc.util.SourceFile,false,false,method any2ArrowAssoc) +askTypeAt at source-/Users/dragos/workspace/git/scala/test/files/presentation/simple-tests/src/Tester.scala,line-12,offset=243 +askTypeAt at source-/Users/dragos/workspace/git/scala/test/files/presentation/simple-tests/src/Tester.scala,line-13,offset=288 +[response] askTypeAt at (12,14) +private[this] val reporter: scala.tools.nsc.reporters.StoreReporter = new reporters.StoreReporter() +[response] askTypeAt at (13,14) +private[this] val compiler: scala.tools.nsc.interactive.Global = new Global(Tester.this.settings, Tester.this.reporter) diff --git a/test/files/presentation/simple-tests/SimpleInteractiveTest.scala b/test/files/presentation/simple-tests/SimpleInteractiveTest.scala new file mode 100644 index 000000000..0456f9f5e --- /dev/null +++ b/test/files/presentation/simple-tests/SimpleInteractiveTest.scala @@ -0,0 +1,9 @@ +import scala.tools.nsc.interactive.tests._ + +/** Simple test that shows how to use the InteractiveTest class. It uses the + * inherited runTest method that runs completion and typedAt tests on all + * sources found under src/ + */ +object Test extends InteractiveTest { + override val runRandomTests = false +} diff --git a/test/files/presentation/simple-tests/src/Tester.scala b/test/files/presentation/simple-tests/src/Tester.scala new file mode 100644 index 000000000..5c25507ab --- /dev/null +++ b/test/files/presentation/simple-tests/src/Tester.scala @@ -0,0 +1,204 @@ +package scala.tools.nsc +package interactive +package tests + +import util._ +import reporters._ +import io.AbstractFile +import collection.mutable.ArrayBuffer + +class Tester(ntests: Int, inputs: Array[SourceFile], settings: Settings) { + + val reporter/*?*/ = new StoreReporter/*!*/ + val compiler/*?*/ = new Global(settings, reporter) + + def askAndListen[T, U](msg: String, arg: T, op: (T, Response[U]) => Unit) { + if (settings.verbose.value) print(msg+" "+arg+": ") + val TIMEOUT = 10 // ms + val limit = System.currentTimeMillis() + randomDelayMillis + val res = new Response[U] + op(arg, res) + while (!res.isComplete && !res.isCancelled) { + if (System.currentTimeMillis() > limit) { + print("c"); res./*!*/cancel() + } else res.get(TIMEOUT) match { + case Some(Left(t)) => + /**/ + if (settings.verbose.value) println(t) + case Some(Right(ex)) => + ex.printStackTrace() + println(ex) + case None => + } + } + } + + def askReload(sfs: SourceFile*) = askAndListen("reload", sfs.toList, compiler.askReload) + def askTypeAt(pos: Position) = askAndListen("type at", pos, compiler.askTypeAt) + def askTypeCompletion(pos: Position) = askAndListen("type at", pos, compiler.askTypeCompletion) + def askScopeCompletion(pos: Position) = askAndListen("type at", pos, compiler.askScopeCompletion) + + val rand = new java.util.Random() + + private def randomInverse(n: Int) = n / (rand.nextInt(n) + 1) + + private def randomDecreasing(n: Int) = { + var r = rand.nextInt((1 to n).sum) + var limit = n + var result = 0 + while (r > limit) { + result += 1 + r -= limit + limit -= 1 + } + result + } + + def randomSourceFileIdx() = rand.nextInt(inputs.length) + + def randomBatchesPerSourceFile(): Int = randomDecreasing(100) + + def randomChangesPerBatch(): Int = randomInverse(50) + + def randomPositionIn(sf: SourceFile) = rand.nextInt(sf.content.length) + + def randomNumChars() = randomInverse(100) + + def randomDelayMillis = randomInverse(10000) + + class Change(sfidx: Int, start: Int, nchars: Int, toLeft: Boolean) { + + private var pos = start + private var deleted: List[Char] = List() + + override def toString = + "In "+inputs(sfidx)+" at "+start+" take "+nchars+" to "+ + (if (toLeft) "left" else "right") + + def deleteOne() { + val sf = inputs(sfidx) + deleted = sf.content(pos) :: deleted + val sf1 = new BatchSourceFile(sf.file, sf.content.take(pos) ++ sf.content.drop(pos + 1)) + inputs(sfidx) = sf1 + askReload(sf1) + } + + def deleteAll() { + print("/"+nchars) + for (i <- 0 until nchars) { + if (toLeft) { + if (pos > 0 && pos <= inputs(sfidx).length) { + pos -= 1 + deleteOne() + } + } else { + if (pos < inputs(sfidx).length) { + deleteOne() + } + } + } + } + + def insertAll() { + for (chr <- if (toLeft) deleted else deleted.reverse) { + val sf = inputs(sfidx) + val (pre, post) = sf./*!*/content splitAt pos + pos += 1 + val sf1 = new BatchSourceFile(sf.file, pre ++ (chr +: post)) + inputs(sfidx) = sf1 + askReload(sf1) + } + } + } + + val testComment = "/**/" + + def testFileChanges(sfidx: Int) = { + lazy val testPositions: Seq[Int] = { + val sf = inputs(sfidx) + val buf = new ArrayBuffer[Int] + var pos = sf.content.indexOfSlice(testComment) + while (pos > 0) { + buf += pos + pos = sf.content.indexOfSlice(testComment, pos + 1) + } + buf + } + def otherTest() { + if (testPositions.nonEmpty) { + val pos = new OffsetPosition(inputs(sfidx), rand.nextInt(testPositions.length)) + rand.nextInt(3) match { + case 0 => askTypeAt(pos) + case 1 => askTypeCompletion(pos) + case 2 => askScopeCompletion(pos) + } + } + } + for (i <- 0 until randomBatchesPerSourceFile()) { + val changes = Vector.fill(/**/randomChangesPerBatch()) { + /**/ + new Change(sfidx, randomPositionIn(inputs(sfidx)), randomNumChars(), rand.nextBoolean()) + } + doTest(sfidx, changes, testPositions, otherTest) match { + case Some(errortrace) => + println(errortrace) + minimize(errortrace) + case None => + } + } + } + + def doTest(sfidx: Int, changes: Seq[Change], testPositions: Seq[Int], otherTest: () => Unit): Option[ErrorTrace] = { + print("new round with "+changes.length+" changes:") + changes foreach (_.deleteAll()) + otherTest() + def errorCount() = compiler.ask(() => reporter.ERROR.count) +// println("\nhalf test round: "+errorCount()) + changes.view.reverse foreach (_.insertAll()) + otherTest() + println("done test round: "+errorCount()) + if (errorCount() != 0) + Some(ErrorTrace(sfidx, changes, reporter.infos, inputs(sfidx).content)) + else + None + } + + case class ErrorTrace( + sfidx: Int, changes: Seq[Change], infos: collection.Set[reporter.Info], content: Array[Char]) { + override def toString = + "Sourcefile: "+inputs(sfidx)+ + "\nChanges:\n "+changes.mkString("\n ")+ + "\nErrors:\n "+infos.mkString("\n ")+ + "\nContents:\n"+content.mkString + } + + def minimize(etrace: ErrorTrace) {} + + /**/ + def run() { + askReload(inputs: _*) + for (i <- 0 until ntests) + testFileChanges(randomSourceFileIdx()) + } +} + +/* A program to do presentation compiler stress tests. + * Usage: + * + * scala scala.tools.nsc.interactive.test.Tester + * + * where is the number os tests to be run and is the set of files to test. + * This will do random deletions and re-insertions in any of the files. + * At places where an empty comment /**/ appears it will in addition randomly + * do ask-types, type-completions, or scope-completions. + */ +object Tester { + def main(args: Array[String]) { + val settings = new Settings() + val (_, filenames) = settings.processArguments(args.toList.tail, true) + println("filenames = "+filenames) + val files = filenames.toArray map (str => new BatchSourceFile(AbstractFile.getFile(str)): SourceFile) + new Tester(args(0).toInt, files, settings).run() + sys.exit(0) + } +}