Skip to content

Commit 5fc321f

Browse files
vsalvisVladimirNik
authored andcommitted
Restructured JUnit neg tests
1 parent 418c118 commit 5fc321f

File tree

1 file changed

+107
-50
lines changed

1 file changed

+107
-50
lines changed

test/test/CompilerTest.scala

Lines changed: 107 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -190,73 +190,130 @@ abstract class CompilerTest {
190190
val nerrors = reporter.errorCount
191191
assert(nerrors == xerrors, s"Wrong # of errors. Expected: $xerrors, found: $nerrors")
192192

193-
// is neg test, check errors occur on right line
193+
// NEG TEST
194194
if (xerrors > 0) {
195195
val errorLines = reporter.allErrors.map(_.pos)
196196
// reporter didn't record as many errors as its errorCount says
197197
assert(errorLines.length == nerrors, s"Not enough errors recorded.")
198-
val (byFile, noPos) = errorLines.groupBy(_.source.file).partition(_._1.toString != "<no source>")
199198

200-
// check the compiler errors that have a source position
201-
val noPosErrFiles = byFile.foldLeft(0)(_ + checkErrorsInFile(_))
202-
203-
// check that files without compiler errors don't contain error markers
204199
val allFiles = (allArgs filter {
205200
case arg => arg.endsWith(".scala") || arg.endsWith(".java")
206201
}).toList
207-
val checkedFiles = byFile.keys.toList.map(_.toString)
208-
val noPosExpected = noPosErrFiles + checkNoErrorMissing(allFiles.filter(!checkedFiles.contains(_)))
209-
210-
// check compiler errors without source position, their number should
211-
// correspond to all "// nopos-error" markers in any files
212-
val noPosFound = noPos.foldLeft(0)(_ + _._2.length)
213-
assert(noPosFound == noPosExpected,
214-
s"Wrong # of errors without source position. Expected (all files): $noPosExpected, found (compiler): $noPosFound")
202+
val expectedErrorsPerFile = allFiles.map(getErrors(_))
203+
204+
// Some compiler errors have an associated source position. Each error
205+
// needs to correspond to a "// error" marker on that line in the source
206+
// file and vice versa.
207+
// Other compiler errors don't have an associated source position. Their
208+
// number should correspond to the total count of "// nopos-error"
209+
// markers in all files
210+
val (errorsByFile, errorsWithoutPos) = errorLines.groupBy(_.source.file).toList.partition(_._1.toString != "<no source>")
211+
212+
// check errors with source position
213+
val foundErrorsPerFile = errorsByFile.map({ case (fileName, errorList) =>
214+
val posErrorLinesToNr = errorList.groupBy(_.line).toList.map({ case (line, list) => (line, list.length) }).sortBy(_._1)
215+
ErrorsInFile(fileName.toString, 0, posErrorLinesToNr)
216+
})
217+
val expectedErrorsPerFileZeroed = expectedErrorsPerFile.map({
218+
case ErrorsInFile(fileName, _, posErrorLinesToNr) =>
219+
ErrorsInFile(fileName.toString, 0, posErrorLinesToNr)
220+
})
221+
checkErrorsWithPosition(expectedErrorsPerFileZeroed, foundErrorsPerFile)
222+
223+
// check errors without source position
224+
val expectedNoPos = expectedErrorsPerFile.map(_.noposErrorNr).sum
225+
val foundNoPos = errorsWithoutPos.map(_._2.length).sum
226+
assert(foundNoPos == expectedNoPos,
227+
s"Wrong # of errors without source position. Expected (all files): $expectedNoPos, found (compiler): $foundNoPos")
215228
}
216229
}
217230

218-
/** For neg tests, check that all errors thrown by compiler have a "// error"
219-
* on the corresponding line in the source file.
220-
*/
221-
def checkErrorsInFile(errors: (AbstractFile, List[SourcePosition])): Int = {
222-
errors match {
223-
case (fileName, pos@(first :: rest)) =>
224-
val content = first.source.content.mkString
225-
val (line, rest) = content.span(_ != '\n')
226-
val byLine = scala.collection.mutable.Map(errors._2.groupBy(_.line).toSeq: _*)
227-
228-
@tailrec
229-
def checkLine(line: String, rest: String, index: Int): Unit = {
230-
val expected = countErrors(line)
231-
byLine.remove(index) match {
232-
case Some(pos) => checkErrors(fileName.toString, Some(index), expected, pos.length)
233-
case None => checkErrors(fileName.toString, Some(index), expected, 0)
234-
}
235-
val (newLine, newRest) = rest.span(_ != '\n')
236-
if (!newRest.isEmpty)
237-
checkLine(newLine, newRest.drop(1), index + 1)
238-
}
239-
240-
checkLine(line, rest.drop(1), 0)
241-
assert(byLine.isEmpty, "Some compiler errors don't correspond to any line in the source file: " + fileName + ": " + byLine)
242-
countNoPosErrors(content)
243-
case (fileName, Nil) => assert(false, "impossible: empty groupBy value in file: " + fileName); 0
231+
// ========== NEG TEST HELPERS =============
232+
233+
/** Captures the number of nopos-errors in the given file and the number of
234+
* errors with a position, represented as a tuple of source line and number
235+
* of errors on that line. */
236+
case class ErrorsInFile(fileName: String, noposErrorNr: Int, posErrorLinesToNr: List[(Int, Int)])
237+
238+
/** Extracts the errors expected for the given neg test file. */
239+
def getErrors(fileName: String): ErrorsInFile = {
240+
val content = SFile(fileName).slurp
241+
val (line, rest) = content.span(_ != '\n')
242+
243+
@tailrec
244+
def checkLine(line: String, rest: String, index: Int, noposAcc: Int, posAcc: List[(Int, Int)]): ErrorsInFile = {
245+
val posErrors = "// ?error".r.findAllIn(line).length
246+
val newPosAcc = if (posErrors > 0) (index, posErrors) :: posAcc else posAcc
247+
val newNoPosAcc = noposAcc + "// ?nopos-error".r.findAllIn(line).length
248+
val (newLine, newRest) = rest.span(_ != '\n')
249+
if (newRest.isEmpty)
250+
ErrorsInFile(fileName.toString, newNoPosAcc, newPosAcc.reverse)
251+
else
252+
checkLine(newLine, newRest.tail, index + 1, newNoPosAcc, newPosAcc) // skip leading '\n'
244253
}
245-
}
246254

247-
def countErrors(s: String) = "// ?error".r.findAllIn(s).length
248-
def countNoPosErrors(s: String) = "// ?nopos-error".r.findAllIn(s).length
255+
checkLine(line, rest.tail, 0, 0, Nil) // skip leading '\n'
256+
}
249257

250-
def checkErrors(fileName: String, index: Option[Int], exp: Int, found: Int) = {
251-
val i = index.map({ i => ":" + (i + 1) }).getOrElse("")
258+
/** Asserts that the expected and found number of errors correspond, and
259+
* otherwise throws an error with the filename, plus optionally a line
260+
* number if available. */
261+
def errorMsg(fileName: String, lineNumber: Option[Int], exp: Int, found: Int) = {
262+
val i = lineNumber.map({ i => ":" + (i + 1) }).getOrElse("")
252263
assert(found == exp, s"Wrong # of errors for $fileName$i. Expected (file): $exp, found (compiler): $found")
253264
}
254265

255-
def checkNoErrorMissing(files: List[String]) = files.foldLeft(0)({ case (sum, fileName) =>
256-
val content = SFile(fileName).slurp
257-
checkErrors(fileName, None, countErrors(content), 0)
258-
sum + countNoPosErrors(content)
259-
})
266+
/** Compares the expected with the found errors and creates a nice error
267+
* message if they don't agree. */
268+
def checkErrorsWithPosition(expected: List[ErrorsInFile], found: List[ErrorsInFile]): Unit = {
269+
// create nice error messages
270+
expected.diff(found) match {
271+
case Nil => // nothing missing
272+
case ErrorsInFile(fileName, _, expectedLines) :: xs =>
273+
found.find(_.fileName == fileName) match {
274+
case None =>
275+
// expected some errors, but none found for this file
276+
errorMsg(fileName, None, expectedLines.map(_._2).sum, 0)
277+
case Some(ErrorsInFile(_,_,foundLines)) =>
278+
// found wrong number/location of markers for this file
279+
compareLines(fileName, expectedLines, foundLines)
280+
}
281+
}
282+
283+
found.diff(expected) match {
284+
case Nil => // nothing missing
285+
case ErrorsInFile(fileName, _, foundLines) :: xs =>
286+
expected.find(_.fileName == fileName) match {
287+
case None =>
288+
// found some errors, but none expected for this file
289+
errorMsg(fileName, None, 0, foundLines.map(_._2).sum)
290+
case Some(ErrorsInFile(_,_,expectedLines)) =>
291+
// found wrong number/location of markers for this file
292+
compareLines(fileName, expectedLines, foundLines)
293+
}
294+
}
295+
}
296+
297+
/** Gives an error message for one line where the expected number of errors and
298+
* the number of compiler errors differ. */
299+
def compareLines(fileName: String, expectedLines: List[(Int, Int)], foundLines: List[(Int, Int)]) = {
300+
expectedLines.foreach({ case (line, expNr) =>
301+
foundLines.find(_._1 == line) match {
302+
case Some((_, `expNr`)) => // this line is ok
303+
case Some((_, foundNr)) => errorMsg(fileName, Some(line), expNr, foundNr)
304+
case None => errorMsg(fileName, Some(line), expNr, 0)
305+
}
306+
})
307+
foundLines.foreach({ case (line, foundNr) =>
308+
expectedLines.find(_._1 == line) match {
309+
case Some((_, `foundNr`)) => // this line is ok
310+
case Some((_, expNr)) => errorMsg(fileName, Some(line), expNr, foundNr)
311+
case None => errorMsg(fileName, Some(line), 0, foundNr)
312+
}
313+
})
314+
}
315+
316+
// ========== PARTEST HELPERS =============
260317

261318
// In particular, don't copy flags from scalac tests
262319
private val extensionsToCopy = scala.collection.immutable.HashSet("scala", "java")

0 commit comments

Comments
 (0)