diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala | 175 |
1 files changed, 175 insertions, 0 deletions
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 0000000000..325151b528 --- /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() + } +} + |