summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSom Snytt <som.snytt@gmail.com>2015-02-25 20:33:20 -0800
committerSom Snytt <som.snytt@gmail.com>2015-03-03 08:10:45 -0800
commita180f5f24f2094112a01cdb02e0e8f218db68d70 (patch)
treeb0a2541bf71bce34177f26c72b414902d21ccda3
parent3a32ae3651f69237bde32598674bc135ad9e4064 (diff)
downloadscala-a180f5f24f2094112a01cdb02e0e8f218db68d70.tar.gz
scala-a180f5f24f2094112a01cdb02e0e8f218db68d70.tar.bz2
scala-a180f5f24f2094112a01cdb02e0e8f218db68d70.zip
SI-9170 More flexible SessionTest
SessionTest session text can include line continuations and pasted text. Pasted script (which looks like a double prompt) probably doesn't work. This commit includes @retronym's SI-9170 one-liner.
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala3
-rw-r--r--src/partest-extras/scala/tools/partest/ReplTest.scala37
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ILoop.scala23
-rw-r--r--test/files/run/t9170.scala58
4 files changed, 106 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 1c9dbad4dd..b233acf271 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -1551,7 +1551,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
if (reporter.hasErrors) {
for ((sym, file) <- symSource.iterator) {
- sym.reset(new loaders.SourcefileLoader(file))
+ if (file != null)
+ sym.reset(new loaders.SourcefileLoader(file))
if (sym.isTerm)
sym.moduleClass reset loaders.moduleClassLoader
}
diff --git a/src/partest-extras/scala/tools/partest/ReplTest.scala b/src/partest-extras/scala/tools/partest/ReplTest.scala
index a728e8bdef..5b65d6ab9b 100644
--- a/src/partest-extras/scala/tools/partest/ReplTest.scala
+++ b/src/partest-extras/scala/tools/partest/ReplTest.scala
@@ -8,6 +8,7 @@ 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.util.matching.Regex.Match
/** A class for testing repl code.
* It filters the line of output that mentions a version number.
@@ -22,6 +23,9 @@ abstract class ReplTest extends DirectTest {
s.Xnojline.value = true
transformSettings(s)
}
+ /** True for SessionTest to preserve session text. */
+ def inSession: Boolean = false
+ /** True to preserve welcome text. */
def welcoming: Boolean = false
lazy val welcome = "(Welcome to Scala) version .*".r
def normalize(s: String) = s match {
@@ -36,7 +40,7 @@ abstract class ReplTest extends DirectTest {
val s = settings
log("eval(): settings = " + s)
//ILoop.runForTranscript(code, s).lines drop 1 // not always first line
- val lines = ILoop.runForTranscript(code, s).lines
+ val lines = ILoop.runForTranscript(code, s, inSession = inSession).lines
if (welcoming) lines map normalize
else lines filter unwelcoming
}
@@ -57,13 +61,30 @@ abstract class SessionTest extends ReplTest {
/** Session transcript, as a triple-quoted, multiline, marginalized string. */
def session: String
- /** Expected output, as an iterator. */
- def expected = session.stripMargin.lines
+ /** Expected output, as an iterator, optionally marginally stripped. */
+ def expected = if (stripMargins) session.stripMargin.lines else session.lines
+
+ /** Override with false if we should not strip margins because of leading continuation lines. */
+ def stripMargins: Boolean = true
+
+ /** Analogous to stripMargins, don't mangle continuation lines on echo. */
+ override def inSession: Boolean = true
/** Code is the command list culled from the session (or the expected session output).
- * Would be nicer if code were lazy lines.
+ * 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.
*/
- override final def code = expected filter (_ startsWith prompt) map (_ drop prompt.length) mkString "\n"
+ import SessionTest._
+ override final def code = input findAllMatchIn (expected mkString ("", "\n", "\n")) map {
+ case input(null, null, prompted) =>
+ def continued(m: Match): Option[String] = m match {
+ case margin(text) => Some(text)
+ case _ => None
+ }
+ margin.replaceSomeIn(prompted, continued)
+ case input(cmd, pasted, null) =>
+ cmd + pasted + "\u0004"
+ } mkString
final def prompt = "scala> "
@@ -75,3 +96,9 @@ abstract class SessionTest extends ReplTest {
if (evaled != wanted) Console print nest.FileManager.compareContents(wanted, evaled, "expected", "actual")
}
}
+object SessionTest {
+ // \R for line break is Java 8, \v for vertical space might suffice
+ val input = """(?m)^scala> (:pa.*\u000A)// Entering paste mode.*\u000A\u000A((?:.*\u000A)*)\u000A// Exiting paste mode.*\u000A|^scala> (.*\u000A(?:\s*\| .*\u000A)*)""".r
+
+ val margin = """(?m)^\s*\| (.*)$""".r
+}
diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
index 4d71e0e09e..4221126caa 100644
--- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
@@ -937,25 +937,30 @@ object ILoop {
// Designed primarily for use by test code: take a String with a
// bunch of code, and prints out a transcript of what it would look
// like if you'd just typed it into the repl.
- def runForTranscript(code: String, settings: Settings): String = {
+ def runForTranscript(code: String, settings: Settings, inSession: Boolean = false): String = {
import java.io.{ BufferedReader, StringReader, OutputStreamWriter }
stringFromStream { ostream =>
Console.withOut(ostream) {
val output = new JPrintWriter(new OutputStreamWriter(ostream), true) {
- override def write(str: String) = {
- // completely skip continuation lines
- if (str forall (ch => ch.isWhitespace || ch == '|')) ()
+ // skip margin prefix for continuation lines, unless preserving session text for test
+ override def write(str: String) =
+ if (!inSession && (str forall (ch => ch.isWhitespace || ch == '|'))) () // repl.paste.ContinueString
else super.write(str)
- }
}
val input = new BufferedReader(new StringReader(code.trim + "\n")) {
override def readLine(): String = {
- val s = super.readLine()
- // helping out by printing the line being interpreted.
- if (s != null)
+ mark(1) // default buffer is 8k
+ val c = read()
+ if (c == -1 || c == 4) {
+ null
+ } else {
+ reset()
+ val s = super.readLine()
+ // helping out by printing the line being interpreted.
output.println(s)
- s
+ s
+ }
}
}
val repl = new ILoop(input, output)
diff --git a/test/files/run/t9170.scala b/test/files/run/t9170.scala
new file mode 100644
index 0000000000..25a0e84581
--- /dev/null
+++ b/test/files/run/t9170.scala
@@ -0,0 +1,58 @@
+
+import scala.tools.partest.SessionTest
+
+object Test extends SessionTest {
+
+ override def stripMargins = false
+
+ def session =
+"""Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
+<console>:7: error: double definition:
+def f[A](a: => A): Int at line 7 and
+def f[A](a: => Either[Exception,A]): Int at line 7
+have same type after erasure: (a: Function0)Int
+ object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
+ ^
+
+scala> object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
+<console>:7: error: double definition:
+def f[A](a: => A): Int at line 7 and
+def f[A](a: => Either[Exception,A]): Int at line 7
+have same type after erasure: (a: Function0)Int
+ object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
+ ^
+
+scala> object Y {
+ | def f[A](a: => A) = 1
+ | def f[A](a: => Either[Exception, A]) = 2
+ | }
+<console>:9: error: double definition:
+def f[A](a: => A): Int at line 8 and
+def f[A](a: => Either[Exception,A]): Int at line 9
+have same type after erasure: (a: Function0)Int
+ def f[A](a: => Either[Exception, A]) = 2
+ ^
+
+scala> :pa
+// Entering paste mode (ctrl-D to finish)
+
+object Y {
+ def f[A](a: => A) = 1
+ def f[A](a: => Either[Exception, A]) = 2
+}
+
+// Exiting paste mode, now interpreting.
+
+<console>:9: error: double definition:
+def f[A](a: => A): Int at line 8 and
+def f[A](a: => Either[Exception,A]): Int at line 9
+have same type after erasure: (a: Function0)Int
+ def f[A](a: => Either[Exception, A]) = 2
+ ^
+
+scala> :quit"""
+}
+