alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (InteractiveTest.scala)

This example Scala source code file (InteractiveTest.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

array, int, path, position, position, response, response, seq, seq, sourcefile, string, string, t, unit

The Scala InteractiveTest.scala source code

/* NSC -- new Scala compiler
 * Copyright 2009-2011 Scala Solutions and LAMP/EPFL
 * @author Martin Odersky
 */
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 = 10000 // timeout in milliseconds
  
  val settings = new Settings
  val reporter= new StoreReporter
  
  /** 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("."))

  /** If there's a file ending in .opts, read it and parse it for cmd line arguments. */
  val argsString = {
    val optsFile = outDir / "%s.opts".format(System.getProperty("partest.testname"))
    val str = try File(optsFile).slurp() catch {
      case e: java.io.IOException => ""
    }
    str.lines.filter(!_.startsWith("#")).mkString(" ")
  }

  /** Prepare the settings object. Load the .opts file and adjust all paths from the 
   *  Unix-like syntax to the platform specific syntax. This is necessary so that a
   *  single .opts file can be used on all platforms.
   *  
   *  @note Bootclasspath is treated specially. If there is a -bootclasspath option in
   *        the file, the 'usejavacp' setting is set to false. This ensures that the
   *        bootclasspath takes precedence over the scala-library used to run the current
   *        test.
   */
  def prepareSettings() {
    import java.io.File._
    def adjustPaths(paths: settings.PathSetting*) {
      for (p <- paths if argsString.contains(p.name)) p.value = p.value.map {
        case '/' => separatorChar
        case ':' => pathSeparatorChar 
        case c => c
      }
    }
      
    // need this so that the classpath comes from what partest 
    // instead of scala.home
    settings.usejavacp.value = !argsString.contains("-bootclasspath")

    // pass any options coming from outside
    settings.processArgumentString(argsString) match {
      case (false, rest) =>
        println("error processing arguments (unprocessed: %s)".format(rest))
      case _ => ()
    }
    adjustPaths(settings.bootclasspath, settings.classpath, settings.javabootclasspath, settings.sourcepath)
  }

  protected def printClassPath() {
    println("\toutDir: %s".format(outDir.path))
    println("\tbaseDir: %s".format(baseDir.path))
    println("\targsString: %s".format(argsString))
    println("\tbootClassPath: %s".format(settings.bootclasspath.value))
    println("\tverbose: %b".format(settings.verbose.value))
  }
  
  lazy val compiler = {
    prepareSettings()
    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
  }
  
  /** Should askAllSources wait for each ask to finish before issueing the next? */
  val synchronousRequests = true

  /** 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 askAllSourcesAsync[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) withResponse(pos, r)(f)
  }

  /** Synchronous version of askAllSources. Each position is treated in turn, waiting for the
   *  response before going to the next one. 
   */
  def askAllSourcesSync[T](marker: String)(askAt: Position => Response[T])(f: (Position, T) => Unit) {
    val positions = allPositionsOf(str = marker).valuesIterator.toList.flatten
    for (pos <- positions) withResponse(pos, askAt(pos))(f)
  }
  
  def askAllSources[T] = if (synchronousRequests) askAllSourcesSync[T] _ else askAllSourcesAsync[T] _
  
  /** Return the filename:line:col version of this position. */
  def showPos(pos: Position): String = 
    "%s:%d:%d".format(pos.source.file.name, pos.line, pos.column)
  
  protected def withResponse[T](pos: Position, response: Response[T])(f: (Position, T) => Unit) {
    response.get(TIMEOUT) match {
      case Some(Left(t)) =>
        f(pos, t)
      case None =>
        println("TIMEOUT: " + showPos(pos))
      case Some(r) =>
        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.source.file.name + ((pos.line, pos.column)))
      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))
      // we skip getClass because it changed signature between 1.5 and 1.6, so there is no
      // universal check file that we can provide for this to work
      println("retreived %d members".format(members.size))
      compiler ask { () =>
        println(members.sortBy(_.sym.name.toString).filterNot(_.sym.name.toString == "getClass").mkString("\n"))
      }
    }
  }
  
  /** Ask for typedAt for all marker positions in all sources.
   */
  def typeAtTests() {
    askAllSources(typedAtMarker) { pos =>
      println("askTypeAt at " + pos.source.file.name + ((pos.line, pos.column)))
      val r = new Response[compiler.Tree]
      compiler.askTypeAt(pos, r)
      r
    } { (pos, tree) =>
      println("[response] askTypeAt at " + (pos.line, pos.column))
      compiler.ask(() => 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 = false
  
  def main(args: Array[String]) {
    reloadSources()
    runTest
    compiler.askShutdown()
  }
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala InteractiveTest.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.