Skip to content

Commit 955cbe7

Browse files
committed
Restructured JUnit neg tests
1 parent 6d311ed commit 955cbe7

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

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

245-
def countErrors(s: String) = "// ?error".r.findAllIn(s).length
246-
def countNoPosErrors(s: String) = "// ?nopos-error".r.findAllIn(s).length
253+
checkLine(line, rest.tail, 0, 0, Nil) // skip leading '\n'
254+
}
247255

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

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

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

0 commit comments

Comments
 (0)