package wacc import org.scalatest.BeforeAndAfterAll import org.scalatest.Inspectors.forEvery import org.scalatest.matchers.should.Matchers._ import org.scalatest.freespec.AsyncFreeSpec import cats.effect.testing.scalatest.AsyncIOSpec import java.io.File import java.nio.file.Path import sys.process._ import scala.io.Source import cats.effect.IO import wacc.{compile as compileWacc} class ParallelExamplesSpec extends AsyncFreeSpec with AsyncIOSpec with BeforeAndAfterAll { val files = allWaccFiles("wacc-examples/valid").map { p => (p.toString, List(0)) } ++ allWaccFiles("wacc-examples/invalid/syntaxErr").map { p => (p.toString, List(100)) } ++ allWaccFiles("wacc-examples/invalid/semanticErr").map { p => (p.toString, List(200)) } ++ allWaccFiles("wacc-examples/invalid/whack").map { p => (p.toString, List(100, 200)) } forEvery(files) { (filename, expectedResult) => val baseFilename = filename.stripSuffix(".wacc") s"$filename" - { "should be compiled with correct result" in { compileWacc(Path.of(filename), outputDir = None, log = false).map { result => expectedResult should contain(result) } } if (expectedResult == List(0)) { "should run with correct result" in { if (fileIsDisallowedBackend(filename)) IO.pure( succeed ) // TODO: remove when advanced tests removed. not sure how to "pending" this otherwise else { for { contents <- IO(Source.fromFile(File(filename)).getLines.toList) inputLine = extractInput(contents) expectedOutput = extractOutput(contents) expectedExit = extractExit(contents) asmFilename = baseFilename + ".s" execFilename = baseFilename gccResult <- IO(s"gcc -o $execFilename -z noexecstack $asmFilename".!) _ = assert(gccResult == 0) stdout <- IO.pure(new StringBuilder) process <- IO { s"timeout 5s $execFilename" run ProcessIO( in = w => { w.write(inputLine.getBytes) w.close() }, out = Source.fromInputStream(_).addString(stdout), err = _ => () ) } exitCode <- IO.pure(process.exitValue) } yield { exitCode shouldBe expectedExit normalizeOutput(stdout.toString) shouldBe expectedOutput } } } } } } def allWaccFiles(dir: String): IndexedSeq[os.Path] = val d = java.io.File(dir) os.walk(os.Path(d.getAbsolutePath)).filter(_.ext == "wacc") // TODO: eventually remove this I think def fileIsDisallowedBackend(filename: String): Boolean = Seq( "^.*wacc-examples/valid/advanced.*$" ).exists(filename.matches) private def extractInput(contents: List[String]): String = contents .find(_.matches("^# ?[Ii]nput:.*$")) .map(_.split(":").last.strip + "\n") .getOrElse("") private def extractOutput(contents: List[String]): String = { val outputLineIdx = contents.indexWhere(_.matches("^# ?[Oo]utput:.*$")) if (outputLineIdx == -1) "" else contents .drop(outputLineIdx + 1) .takeWhile(_.startsWith("#")) .map(_.stripPrefix("#").stripLeading) .mkString("\n") } private def extractExit(contents: List[String]): Int = { val exitLineIdx = contents.indexWhere(_.matches("^# ?[Ee]xit:.*$")) if (exitLineIdx == -1) 0 else contents(exitLineIdx + 1).stripPrefix("#").strip.toInt } private def normalizeOutput(output: String): String = output .replaceAll("0x[0-9a-f]+", "#addrs#") .replaceAll("fatal error:.*", "#runtime_error#\u0000") .takeWhile(_ != '\u0000') }