Skip to content

Commit ee864c1

Browse files
committed
Merge pull request scala#4364 from som-snytt/issue/sessiontest
SI-9170 More flexible SessionTest
2 parents 0abafb5 + a180f5f commit ee864c1

File tree

4 files changed

+106
-15
lines changed

4 files changed

+106
-15
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1551,7 +1551,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
15511551

15521552
if (reporter.hasErrors) {
15531553
for ((sym, file) <- symSource.iterator) {
1554-
sym.reset(new loaders.SourcefileLoader(file))
1554+
if (file != null)
1555+
sym.reset(new loaders.SourcefileLoader(file))
15551556
if (sym.isTerm)
15561557
sym.moduleClass reset loaders.moduleClassLoader
15571558
}

src/partest-extras/scala/tools/partest/ReplTest.scala

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package scala.tools.partest
88
import scala.tools.nsc.Settings
99
import scala.tools.nsc.interpreter.ILoop
1010
import java.lang.reflect.{ Method => JMethod, Field => JField }
11+
import scala.util.matching.Regex.Match
1112

1213
/** A class for testing repl code.
1314
* It filters the line of output that mentions a version number.
@@ -22,6 +23,9 @@ abstract class ReplTest extends DirectTest {
2223
s.Xnojline.value = true
2324
transformSettings(s)
2425
}
26+
/** True for SessionTest to preserve session text. */
27+
def inSession: Boolean = false
28+
/** True to preserve welcome text. */
2529
def welcoming: Boolean = false
2630
lazy val welcome = "(Welcome to Scala) version .*".r
2731
def normalize(s: String) = s match {
@@ -36,7 +40,7 @@ abstract class ReplTest extends DirectTest {
3640
val s = settings
3741
log("eval(): settings = " + s)
3842
//ILoop.runForTranscript(code, s).lines drop 1 // not always first line
39-
val lines = ILoop.runForTranscript(code, s).lines
43+
val lines = ILoop.runForTranscript(code, s, inSession = inSession).lines
4044
if (welcoming) lines map normalize
4145
else lines filter unwelcoming
4246
}
@@ -57,13 +61,30 @@ abstract class SessionTest extends ReplTest {
5761
/** Session transcript, as a triple-quoted, multiline, marginalized string. */
5862
def session: String
5963

60-
/** Expected output, as an iterator. */
61-
def expected = session.stripMargin.lines
64+
/** Expected output, as an iterator, optionally marginally stripped. */
65+
def expected = if (stripMargins) session.stripMargin.lines else session.lines
66+
67+
/** Override with false if we should not strip margins because of leading continuation lines. */
68+
def stripMargins: Boolean = true
69+
70+
/** Analogous to stripMargins, don't mangle continuation lines on echo. */
71+
override def inSession: Boolean = true
6272

6373
/** Code is the command list culled from the session (or the expected session output).
64-
* Would be nicer if code were lazy lines.
74+
* Would be nicer if code were lazy lines so you could generate arbitrarily long text.
75+
* Retain user input: prompt lines and continuations, without the prefix; or pasted text plus ctl-D.
6576
*/
66-
override final def code = expected filter (_ startsWith prompt) map (_ drop prompt.length) mkString "\n"
77+
import SessionTest._
78+
override final def code = input findAllMatchIn (expected mkString ("", "\n", "\n")) map {
79+
case input(null, null, prompted) =>
80+
def continued(m: Match): Option[String] = m match {
81+
case margin(text) => Some(text)
82+
case _ => None
83+
}
84+
margin.replaceSomeIn(prompted, continued)
85+
case input(cmd, pasted, null) =>
86+
cmd + pasted + "\u0004"
87+
} mkString
6788

6889
final def prompt = "scala> "
6990

@@ -75,3 +96,9 @@ abstract class SessionTest extends ReplTest {
7596
if (evaled != wanted) Console print nest.FileManager.compareContents(wanted, evaled, "expected", "actual")
7697
}
7798
}
99+
object SessionTest {
100+
// \R for line break is Java 8, \v for vertical space might suffice
101+
val input = """(?m)^scala> (:pa.*\u000A)// Entering paste mode.*\u000A\u000A((?:.*\u000A)*)\u000A// Exiting paste mode.*\u000A|^scala> (.*\u000A(?:\s*\| .*\u000A)*)""".r
102+
103+
val margin = """(?m)^\s*\| (.*)$""".r
104+
}

src/repl/scala/tools/nsc/interpreter/ILoop.scala

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -937,25 +937,30 @@ object ILoop {
937937
// Designed primarily for use by test code: take a String with a
938938
// bunch of code, and prints out a transcript of what it would look
939939
// like if you'd just typed it into the repl.
940-
def runForTranscript(code: String, settings: Settings): String = {
940+
def runForTranscript(code: String, settings: Settings, inSession: Boolean = false): String = {
941941
import java.io.{ BufferedReader, StringReader, OutputStreamWriter }
942942

943943
stringFromStream { ostream =>
944944
Console.withOut(ostream) {
945945
val output = new JPrintWriter(new OutputStreamWriter(ostream), true) {
946-
override def write(str: String) = {
947-
// completely skip continuation lines
948-
if (str forall (ch => ch.isWhitespace || ch == '|')) ()
946+
// skip margin prefix for continuation lines, unless preserving session text for test
947+
override def write(str: String) =
948+
if (!inSession && (str forall (ch => ch.isWhitespace || ch == '|'))) () // repl.paste.ContinueString
949949
else super.write(str)
950-
}
951950
}
952951
val input = new BufferedReader(new StringReader(code.trim + "\n")) {
953952
override def readLine(): String = {
954-
val s = super.readLine()
955-
// helping out by printing the line being interpreted.
956-
if (s != null)
953+
mark(1) // default buffer is 8k
954+
val c = read()
955+
if (c == -1 || c == 4) {
956+
null
957+
} else {
958+
reset()
959+
val s = super.readLine()
960+
// helping out by printing the line being interpreted.
957961
output.println(s)
958-
s
962+
s
963+
}
959964
}
960965
}
961966
val repl = new ILoop(input, output)

test/files/run/t9170.scala

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
import scala.tools.partest.SessionTest
3+
4+
object Test extends SessionTest {
5+
6+
override def stripMargins = false
7+
8+
def session =
9+
"""Type in expressions to have them evaluated.
10+
Type :help for more information.
11+
12+
scala> object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
13+
<console>:7: error: double definition:
14+
def f[A](a: => A): Int at line 7 and
15+
def f[A](a: => Either[Exception,A]): Int at line 7
16+
have same type after erasure: (a: Function0)Int
17+
object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
18+
^
19+
20+
scala> object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
21+
<console>:7: error: double definition:
22+
def f[A](a: => A): Int at line 7 and
23+
def f[A](a: => Either[Exception,A]): Int at line 7
24+
have same type after erasure: (a: Function0)Int
25+
object Y { def f[A](a: => A) = 1 ; def f[A](a: => Either[Exception, A]) = 2 }
26+
^
27+
28+
scala> object Y {
29+
| def f[A](a: => A) = 1
30+
| def f[A](a: => Either[Exception, A]) = 2
31+
| }
32+
<console>:9: error: double definition:
33+
def f[A](a: => A): Int at line 8 and
34+
def f[A](a: => Either[Exception,A]): Int at line 9
35+
have same type after erasure: (a: Function0)Int
36+
def f[A](a: => Either[Exception, A]) = 2
37+
^
38+
39+
scala> :pa
40+
// Entering paste mode (ctrl-D to finish)
41+
42+
object Y {
43+
def f[A](a: => A) = 1
44+
def f[A](a: => Either[Exception, A]) = 2
45+
}
46+
47+
// Exiting paste mode, now interpreting.
48+
49+
<console>:9: error: double definition:
50+
def f[A](a: => A): Int at line 8 and
51+
def f[A](a: => Either[Exception,A]): Int at line 9
52+
have same type after erasure: (a: Function0)Int
53+
def f[A](a: => Either[Exception, A]) = 2
54+
^
55+
56+
scala> :quit"""
57+
}
58+

0 commit comments

Comments
 (0)