feat: remove unsaferunsync and integrate io in tests instead

This commit is contained in:
Jonny
2025-02-28 16:24:53 +00:00
parent e54e5ce151
commit 1a72decf55
2 changed files with 83 additions and 76 deletions

View File

@@ -11,6 +11,8 @@
//> using dep com.monovore::decline-effect::2.5.0 //> using dep com.monovore::decline-effect::2.5.0
//> using dep com.github.scopt::scopt::4.1.0 //> using dep com.github.scopt::scopt::4.1.0
//> using test.dep org.scalatest::scalatest::3.2.19 //> using test.dep org.scalatest::scalatest::3.2.19
//> using dep org.typelevel::cats-effect-testing-scalatest::1.6.0
// sensible defaults for warnings and compiler checks // sensible defaults for warnings and compiler checks
//> using options -deprecation -unchecked -feature //> using options -deprecation -unchecked -feature

View File

@@ -1,15 +1,19 @@
package wacc package wacc
import org.scalatest.BeforeAndAfterAll import org.scalatest.BeforeAndAfterAll
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.Inspectors.forEvery 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.io.File
import sys.process._ import sys.process._
import java.io.PrintStream import java.io.PrintStream
import scala.io.Source import scala.io.Source
import cats.effect.unsafe.implicits.global import cats.effect.IO
import wacc.{compile as compileWacc}
class ParallelExamplesSpec extends AsyncFreeSpec with AsyncIOSpec with BeforeAndAfterAll {
class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll {
val files = val files =
allWaccFiles("wacc-examples/valid").map { p => allWaccFiles("wacc-examples/valid").map { p =>
(p.toString, List(0)) (p.toString, List(0))
@@ -24,50 +28,39 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll {
(p.toString, List(100, 200)) (p.toString, List(100, 200))
} }
// tests go here
forEvery(files) { (filename, expectedResult) => forEvery(files) { (filename, expectedResult) =>
val baseFilename = filename.stripSuffix(".wacc") val baseFilename = filename.stripSuffix(".wacc")
given stdout: PrintStream = PrintStream(File(baseFilename + ".out")) given stdout: PrintStream = PrintStream(File(baseFilename + ".out"))
s"$filename" should "be compiled with correct result" in { s"$filename" - {
val result = compile(filename).unsafeRunSync() "should be compiled with correct result" in {
assert(expectedResult.contains(result)) compileWacc(filename).map { result =>
expectedResult should contain(result)
}
} }
if (expectedResult == List(0)) it should "run with correct result" in { if (expectedResult == List(0)) {
if (fileIsDisallowedBackend(filename)) pending "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)
// Retrieve contents to get input and expected output + exit code asmFilename = baseFilename + ".s"
val contents = scala.io.Source.fromFile(File(filename)).getLines.toList execFilename = baseFilename
val inputLine = gccResult <- IO(s"gcc -o $execFilename -z noexecstack $asmFilename".!)
contents
.find(_.matches("^# ?[Ii]nput:.*$"))
.map(_.split(":").last.strip + "\n")
.getOrElse("")
val outputLineIdx = contents.indexWhere(_.matches("^# ?[Oo]utput:.*$"))
val expectedOutput =
if (outputLineIdx == -1) ""
else
contents
.drop(outputLineIdx + 1)
.takeWhile(_.startsWith("#"))
.map(_.stripPrefix("#").stripLeading)
.mkString("\n")
val exitLineIdx = contents.indexWhere(_.matches("^# ?[Ee]xit:.*$")) _ = assert(gccResult == 0)
val expectedExit =
if (exitLineIdx == -1) 0
else contents(exitLineIdx + 1).stripPrefix("#").strip.toInt
// Assembly and link using gcc stdout <- IO.pure(new StringBuilder)
val asmFilename = baseFilename + ".s" process <- IO {
val execFilename = baseFilename s"timeout 5s $execFilename" run ProcessIO(
val gccResult = s"gcc -o $execFilename -z noexecstack $asmFilename".!
assert(gccResult == 0)
// Run the executable with the provided input
val stdout = new StringBuilder
val process = s"timeout 5s $execFilename" run ProcessIO(
in = w => { in = w => {
w.write(inputLine.getBytes) w.write(inputLine.getBytes)
w.close() w.close()
@@ -75,44 +68,56 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll {
out = Source.fromInputStream(_).addString(stdout), out = Source.fromInputStream(_).addString(stdout),
err = _ => () err = _ => ()
) )
}
assert(process.exitValue == expectedExit) exitCode <- IO.pure(process.exitValue)
assert(
stdout.toString } yield {
.replaceAll("0x[0-9a-f]+", "#addrs#") exitCode shouldBe expectedExit
.replaceAll("fatal error:.*", "#runtime_error#\u0000") normalizeOutput(stdout.toString) shouldBe expectedOutput
.takeWhile(_ != '\u0000') }
== expectedOutput }
) }
}
} }
} }
def allWaccFiles(dir: String): IndexedSeq[os.Path] = def allWaccFiles(dir: String): IndexedSeq[os.Path] =
val d = java.io.File(dir) val d = java.io.File(dir)
os.walk(os.Path(d.getAbsolutePath)).filter { _.ext == "wacc" } os.walk(os.Path(d.getAbsolutePath)).filter(_.ext == "wacc")
// TODO: eventually remove this I think
def fileIsDisallowedBackend(filename: String): Boolean = def fileIsDisallowedBackend(filename: String): Boolean =
Seq( Seq(
// format: off "^.*wacc-examples/valid/advanced.*$"
// disable formatting to avoid binPack ).exists(filename.matches)
"^.*wacc-examples/valid/advanced.*$",
// "^.*wacc-examples/valid/array.*$", private def extractInput(contents: List[String]): String =
// "^.*wacc-examples/valid/basic/exit.*$", contents
// "^.*wacc-examples/valid/basic/skip.*$", .find(_.matches("^# ?[Ii]nput:.*$"))
// "^.*wacc-examples/valid/expressions.*$", .map(_.split(":").last.strip + "\n")
// "^.*wacc-examples/valid/function/nested_functions.*$", .getOrElse("")
// "^.*wacc-examples/valid/function/simple_functions.*$",
// "^.*wacc-examples/valid/if.*$", private def extractOutput(contents: List[String]): String = {
// "^.*wacc-examples/valid/IO/print.*$", val outputLineIdx = contents.indexWhere(_.matches("^# ?[Oo]utput:.*$"))
// "^.*wacc-examples/valid/IO/read.*$", if (outputLineIdx == -1) ""
// "^.*wacc-examples/valid/IO/IOLoop.wacc.*$", else
// "^.*wacc-examples/valid/IO/IOSequence.wacc.*$", contents
// "^.*wacc-examples/valid/pairs.*$", .drop(outputLineIdx + 1)
//"^.*wacc-examples/valid/runtimeErr.*$", .takeWhile(_.startsWith("#"))
// "^.*wacc-examples/valid/scope.*$", .map(_.stripPrefix("#").stripLeading)
// "^.*wacc-examples/valid/sequence.*$", .mkString("\n")
// "^.*wacc-examples/valid/variables.*$", }
// "^.*wacc-examples/valid/while.*$",
// format: on private def extractExit(contents: List[String]): Int = {
).find(filename.matches).isDefined 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')
} }