Skip to content

Commit ad902d8

Browse files
wip neg tests
1 parent 9cd51b9 commit ad902d8

File tree

3 files changed

+306
-37
lines changed

3 files changed

+306
-37
lines changed

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,22 @@ trait ParallelTesting extends RunnerOrchestration { self =>
200200
final def countWarnings(reporters: Seq[TestReporter]) = countErrorsAndWarnings(reporters)._2
201201
final def reporterFailed(r: TestReporter) = r.compilerCrashed || r.errorCount > 0
202202

203+
final def checkFile(testSource: TestSource): Option[JFile] = (testSource match {
204+
case ts: JointCompilationSource =>
205+
ts.files.filter(f => !f.isDirectory).map { f => new JFile(f.getAbsolutePath.replaceFirst("\\.scala$", ".check")) }.headOption
206+
207+
case ts: SeparateCompilationSource =>
208+
Option(new JFile(dir.getAbsolutePath + ".check"))
209+
}).filter(_.exists)
210+
211+
final def diffTest(sourceName: String, checkFile: JFile, actual: List[String]) = {
212+
val expected = Source.fromFile(checkFile, "UTF-8").getLines().toList
213+
for (msg <- diffMessage(sourceName, actual, expected)) {
214+
fail(msg)
215+
dumpOutputToFile(checkFile, actual)
216+
}
217+
}
218+
203219
final def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable { self =>
204220
def checkTestSource(): Unit = tryCompile(testSource) {
205221
val reporters = compileTestSource(testSource)
@@ -606,47 +622,17 @@ trait ParallelTesting extends RunnerOrchestration { self =>
606622
if (Properties.testsNoRun) addNoRunWarning()
607623
else runMain(testSource.runClassPath) match {
608624
case Success(_) if !checkFile.isDefined || !checkFile.get.exists => // success!
609-
case Success(output) => {
610-
val outputLines = output.linesIterator.toSeq
611-
val checkLines: Seq[String] = Source.fromFile(checkFile.get, "UTF-8").getLines().toSeq
612-
val sourceTitle = testSource.title
613-
614-
diffMessage(sourceTitle, outputLines, checkLines).foreach { msg =>
615-
616-
echo(msg)
617-
addFailureInstruction(msg)
618-
619-
// Print build instructions to file and summary:
620-
val buildInstr = testSource.buildInstructions(0, warnings)
621-
addFailureInstruction(buildInstr)
622-
623-
// Fail target:
624-
failTestSource(testSource)
625-
626-
dumpOutputToFile(checkFile.get, outputLines)
627-
}
628-
}
629-
625+
case Success(output) => checkFile.foreach(diffTest(testSource, _, output.linesIterator.toSeq))
630626
case Failure(output) =>
631627
echo(s"Test '${testSource.title}' failed with output:")
632628
echo(output)
633629
failTestSource(testSource)
634-
635630
case Timeout =>
636631
echo("failed because test " + testSource.title + " timed out")
637632
failTestSource(testSource, TimeoutFailure(testSource.title))
638633
}
639634
}
640635

641-
def checkFile(testSource: TestSource): Option[JFile] = testSource match {
642-
case ts: JointCompilationSource => ts.files.filter(f => !f.isDirectory).flatMap { file =>
643-
Option(new JFile(file.getAbsolutePath.reverse.dropWhile(_ != '.').reverse + "check")).filter(_.exists) }.headOption
644-
645-
case ts: SeparateCompilationSource =>
646-
Option(new JFile(ts.dir.getAbsolutePath.reverse.dropWhile(_ == JFile.separatorChar).reverse + ".check")).filter(_.exists)
647-
}
648-
649-
650636
override def onSuccess(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable) =
651637
verifyOutput(checkFile(testSource), testSource.outDir, testSource, countWarnings(reporters))
652638
}
@@ -666,16 +652,60 @@ trait ParallelTesting extends RunnerOrchestration { self =>
666652
else None
667653
}
668654

669-
def files: List[JFile] = testSource match {
670-
case ts: JointCompilationSource =>
671-
files.filter(f => !f.isDirectory).map { f =>
672-
new JFile(f.getAbsolutePath.replaceFirst("\\.scala$", ".check")) }
655+
override def onSuccess(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable): Unit =
656+
checkFile(testSource).foreach(diffTest(testSource.title, _, reporterOutputLines(reporters)))
657+
658+
def reporterOutputLines(reporters: List[TestReporter]): List[String] =
659+
reporters.flatMap(_.allErrors).sortBy(_.pos.source.toString).flatMap { error =>
660+
(error.pos.span.toString + " in " + error.pos.source.file.name) :: error.getMessage().linesIterator.toList }
661+
662+
// In neg-tests we allow two types of error annotations,
663+
// "nopos-error" which doesn't care about position and "error" which
664+
// has to be annotated on the correct line number.
665+
//
666+
// We collect these in a map `"file:row" -> numberOfErrors`, for
667+
// nopos errors we save them in `"file" -> numberOfNoPosErrors`
668+
def getErrorMapAndExpectedCount(files: Array[JFile]): (HashMap[String, Integer], Int) = {
669+
val errorMap = new HashMap[String, Integer]()
670+
var expectedErrors = 0
671+
files.filter(_.getName.endsWith(".scala")).foreach { file =>
672+
Source.fromFile(file, "UTF-8").getLines().zipWithIndex.foreach { case (line, lineNbr) =>
673+
val errors = line.sliding("// error".length).count(_.mkString == "// error")
674+
if (errors > 0)
675+
errorMap.put(s"${file.getAbsolutePath}:${lineNbr}", errors)
676+
677+
val noposErrors = line.sliding("// nopos-error".length).count(_.mkString == "// nopos-error")
678+
if (noposErrors > 0) {
679+
val nopos = errorMap.get("nopos")
680+
val existing: Integer = if (nopos eq null) 0 else nopos
681+
errorMap.put("nopos", noposErrors + existing)
682+
}
673683

674-
case ts: SeparateCompilationSource =>
684+
expectedErrors += noposErrors + errors
685+
}
686+
}
687+
688+
(errorMap, expectedErrors)
675689
}
676690

677-
override def onSuccess(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable): Unit = {
691+
def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[MessageContainer]) = !reporterErrors.forall { error =>
692+
val key = if (error.pos.exists) {
693+
val fileName = error.pos.source.file.toString
694+
s"$fileName:${error.pos.line}"
695+
696+
} else "nopos"
678697

698+
val errors = errorMap.get(key)
699+
700+
if (errors ne null) {
701+
if (errors == 1) errorMap.remove(key)
702+
else errorMap.put(key, errors - 1)
703+
true
704+
}
705+
else {
706+
echo(s"Error reported in ${error.pos.source}, but no annotation found")
707+
false
708+
}
679709
}
680710
}
681711

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<2471..2471> in doubleDefinition.scala
2+
Double definition:
3+
def foo: [T <: Int](x: T): T in class Test20 at line 143 and
4+
def foo: [S <: Int, T <: Int](x: S): T in class Test20 at line 144
5+
have the same type after erasure.
6+
<2338..2338> in doubleDefinition.scala
7+
Double definition:
8+
def foo: [T <: Int](x: T): T in class Test19 at line 137 and
9+
def foo(x: Int): Int in class Test19 at line 138
10+
have the same type after erasure.
11+
<2016..2016> in doubleDefinition.scala
12+
Double definition:
13+
var foo: Int in class Test16 at line 115 and
14+
def foo: => Int in class Test16 at line 116
15+
<1949..1949> in doubleDefinition.scala
16+
Double definition:
17+
var foo: Int in class Test15 at line 111 and
18+
def foo: => String in class Test15 at line 112
19+
<1885..1885> in doubleDefinition.scala
20+
Double definition:
21+
var foo: Int in class Test14 at line 107 and
22+
def foo: => Int in class Test14 at line 108
23+
<1827..1827> in doubleDefinition.scala
24+
Double definition:
25+
var foo: Int in class Test13 at line 103 and
26+
def foo: => String in class Test13 at line 104
27+
<1772..1772> in doubleDefinition.scala
28+
Double definition:
29+
val foo: Int in class Test12 at line 99 and
30+
def foo: => Int in class Test12 at line 100
31+
<1705..1705> in doubleDefinition.scala
32+
Double definition:
33+
val foo: Int in class Test11 at line 95 and
34+
def foo: => String in class Test11 at line 96
35+
<1641..1641> in doubleDefinition.scala
36+
Double definition:
37+
val foo: Int in class Test10 at line 91 and
38+
def foo: => Int in class Test10 at line 92
39+
<1583..1583> in doubleDefinition.scala
40+
Double definition:
41+
val foo: Int in class Test9 at line 87 and
42+
def foo: => String in class Test9 at line 88
43+
<1481..1481> in doubleDefinition.scala
44+
Double definition:
45+
var foo: Int in class Test8d at line 81 and
46+
def foo: => Int in class Test8d at line 82
47+
<1428..1428> in doubleDefinition.scala
48+
Double definition:
49+
def foo: => Int in class Test8c at line 76 and
50+
var foo: Int in class Test8c at line 77
51+
<1375..1375> in doubleDefinition.scala
52+
Double definition:
53+
def foo: => Int in class Test8b at line 71 and
54+
val foo: Int in class Test8b at line 72
55+
<1322..1322> in doubleDefinition.scala
56+
Double definition:
57+
val foo: Int in class Test8 at line 66 and
58+
def foo: => Int in class Test8 at line 67
59+
<1236..1236> in doubleDefinition.scala
60+
Double definition:
61+
def foo(x: List[A]): A => A in trait Test7 at line 61 and
62+
def foo(x: List[A]): (B, B) => B in trait Test7 at line 62
63+
have matching parameter types.
64+
<984..984> in doubleDefinition.scala
65+
Double definition:
66+
def foo(x: List[A]): A => A in trait Test6 at line 54 and
67+
def foo(x: List[B]): B => B in trait Test6 at line 55
68+
have the same type after erasure.
69+
<739..739> in doubleDefinition.scala
70+
Double definition:
71+
var foo: Int in class Test4d at line 40 and
72+
def foo: => Int in class Test4d at line 41
73+
<686..686> in doubleDefinition.scala
74+
Double definition:
75+
def foo: => Int in class Test4c at line 35 and
76+
var foo: Int in class Test4c at line 36
77+
<633..633> in doubleDefinition.scala
78+
Double definition:
79+
def foo: => Int in class Test4b at line 30 and
80+
val foo: Int in class Test4b at line 31
81+
<580..580> in doubleDefinition.scala
82+
Double definition:
83+
val foo: Int in class Test4 at line 25 and
84+
def foo: => Int in class Test4 at line 26
85+
<494..494> in doubleDefinition.scala
86+
Double definition:
87+
def foo(x: List[A]): A => A in class Test3 at line 20 and
88+
def foo(x: List[A]): (B, B) => B in class Test3 at line 21
89+
have matching parameter types.
90+
<242..242> in doubleDefinition.scala
91+
Double definition:
92+
def foo(x: List[A]): A => A in class Test2 at line 13 and
93+
def foo(x: List[B]): B => B in class Test2 at line 14
94+
have the same type after erasure.
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
trait A
2+
trait B
3+
4+
// test with classes
5+
6+
class Test1 {
7+
def foo(x: List[A]): Function1[A, A] = ???
8+
def foo(x: List[B]): Function2[B, B, B] = ???
9+
// ok, different jvm signature
10+
}
11+
12+
class Test2 {
13+
def foo(x: List[A]): Function1[A, A] = ???
14+
def foo(x: List[B]): Function1[B, B] = ??? // error: same jvm signature
15+
// scalac calls this "have same type after erasure"
16+
}
17+
18+
class Test3 {
19+
// overload with same argument type, but different return types
20+
def foo(x: List[A]): Function1[A, A] = ???
21+
def foo(x: List[A]): Function2[B, B, B] = ??? // error
22+
}
23+
24+
class Test4 {
25+
val foo = 1
26+
def foo = 2 // error
27+
}
28+
29+
class Test4b {
30+
def foo = 2
31+
val foo = 1 // error
32+
}
33+
34+
class Test4c {
35+
def foo = 2
36+
var foo = 1 // error
37+
}
38+
39+
class Test4d {
40+
var foo = 1
41+
def foo = 2 // error
42+
}
43+
44+
45+
// test with traits
46+
47+
trait Test5 {
48+
def foo(x: List[A]): Function1[A, A] = ???
49+
def foo(x: List[B]): Function2[B, B, B] = ???
50+
// ok, different jvm signature
51+
}
52+
53+
trait Test6 {
54+
def foo(x: List[A]): Function1[A, A] = ???
55+
def foo(x: List[B]): Function1[B, B] = ??? // error: same jvm signature
56+
// scalac calls this "have same type after erasure"
57+
}
58+
59+
trait Test7 {
60+
// overload with same argument type, but different return types
61+
def foo(x: List[A]): Function1[A, A] = ???
62+
def foo(x: List[A]): Function2[B, B, B] = ??? // error
63+
}
64+
65+
class Test8 {
66+
val foo = 1
67+
def foo = 2 // error
68+
}
69+
70+
class Test8b {
71+
def foo = 2
72+
val foo = 1 // error
73+
}
74+
75+
class Test8c {
76+
def foo = 2
77+
var foo = 1 // error
78+
}
79+
80+
class Test8d {
81+
var foo = 1
82+
def foo = 2 // error
83+
}
84+
85+
// test method and contructor argument clashing
86+
87+
class Test9(val foo: Int) {
88+
def foo: String // error
89+
}
90+
91+
class Test10(val foo: Int) {
92+
def foo: Int // error
93+
}
94+
95+
abstract class Test11(val foo: Int) {
96+
def foo: String // error
97+
}
98+
99+
abstract class Test12(val foo: Int) {
100+
def foo: Int // error
101+
}
102+
103+
class Test13(var foo: Int) {
104+
def foo: String // error
105+
}
106+
107+
class Test14(var foo: Int) {
108+
def foo: Int // error
109+
}
110+
111+
abstract class Test15(var foo: Int) {
112+
def foo: String // error
113+
}
114+
115+
abstract class Test16(var foo: Int) {
116+
def foo: Int // error
117+
}
118+
119+
// don't error when shadowing
120+
121+
class Test17 {
122+
val foo = 1
123+
def bar() = {
124+
val foo = ""
125+
}
126+
}
127+
128+
// no error when overloading
129+
130+
class Test18 {
131+
def foo(a: A) = 1
132+
def foo(b: B) = 1
133+
}
134+
135+
// Error when overloading polymorphic and non-polymorphic methods
136+
class Test19 {
137+
def foo[T <: Int](x: T): T = x
138+
def foo(x: Int): Int = x // error
139+
}
140+
141+
// Error when overloading polymorphic methods
142+
class Test20 {
143+
def foo[T <: Int](x: T): T = x
144+
def foo[S <: Int, T <: Int](x: S): T = ??? // error
145+
}

0 commit comments

Comments
 (0)