diff options
Diffstat (limited to 'src/test/scala/chiselTests')
4 files changed, 209 insertions, 67 deletions
diff --git a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala index 6d3d526c..40358d11 100644 --- a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala +++ b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala @@ -8,16 +8,15 @@ import org.scalatest.exceptions.TestFailedException class ChiselTestUtilitiesSpec extends ChiselFlatSpec { // Who tests the testers? "assertKnownWidth" should "error when the expected width is wrong" in { - val caught = intercept[ChiselException] { + intercept[TestFailedException] { assertKnownWidth(7) { Wire(UInt(8.W)) } } - assert(caught.getCause.isInstanceOf[TestFailedException]) } it should "error when the width is unknown" in { - a [ChiselException] shouldBe thrownBy { + intercept[ChiselException] { assertKnownWidth(7) { Wire(UInt()) } @@ -31,12 +30,11 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec { } "assertInferredWidth" should "error if the width is known" in { - val caught = intercept[ChiselException] { + intercept[TestFailedException] { assertInferredWidth(8) { Wire(UInt(8.W)) } } - assert(caught.getCause.isInstanceOf[TestFailedException]) } it should "error if the expected width is wrong" in { @@ -57,4 +55,3 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec { } } } - diff --git a/src/test/scala/chiselTests/OneHotMuxSpec.scala b/src/test/scala/chiselTests/OneHotMuxSpec.scala index 887843d4..7608a3e7 100644 --- a/src/test/scala/chiselTests/OneHotMuxSpec.scala +++ b/src/test/scala/chiselTests/OneHotMuxSpec.scala @@ -37,26 +37,18 @@ class OneHotMuxSpec extends AnyFreeSpec with Matchers with ChiselRunners { } } "simple one hot mux with all fixed width bundles but with different bundles should Not work" in { - try { + intercept[IllegalArgumentException] { assertTesterPasses(new DifferentBundleOneHotTester) - } catch { - case a: ChiselException => a.getCause match { - case _: IllegalArgumentException => - } } } "UIntToOH with output width greater than 2^(input width)" in { assertTesterPasses(new UIntToOHTester) } "UIntToOH should not accept width of zero (until zero-width wires are fixed" in { - try { + intercept[IllegalArgumentException] { assertTesterPasses(new BasicTester { val out = UIntToOH(0.U, 0) }) - } catch { - case a: ChiselException => a.getCause match { - case _: IllegalArgumentException => - } } } diff --git a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala index 0fc42fc6..1634e765 100644 --- a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala +++ b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala @@ -10,6 +10,8 @@ import chisel3.aop.inspecting.{InspectingAspect, InspectorAspect} import org.scalatest.GivenWhenThen import org.scalatest.featurespec.AnyFeatureSpec import org.scalatest.matchers.should.Matchers +import org.scalatest.Inside._ +import org.scalatest.EitherValues._ import scala.io.Source import firrtl.Parser @@ -32,7 +34,7 @@ object ChiselMainSpec { /** A module that fails a requirement */ class FailingRequirementModule extends RawModule { - require(false) + require(false, "the user wrote a failing requirement") } /** A module that triggers a Builder.error (as opposed to exception) */ @@ -69,14 +71,35 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit args: Array[String], generator: Option[Class[_ <: RawModule]] = None, files: Seq[String] = Seq.empty, - stdout: Option[String] = None, - stderr: Option[String] = None, + stdout: Seq[Either[String, String]] = Seq.empty, + stderr: Seq[Either[String, String]] = Seq.empty, result: Int = 0, fileChecks: Map[String, File => Unit] = Map.empty) { def testName: String = "args" + args.mkString("_") def argsString: String = args.mkString(" ") } + /** A test of ChiselMain that is going to involve catching an exception. + * @param args command line arguments (excluding --module) to pass in + * @param generator the module to build (used to generate --module) + * @param message snippets of text that should appear (Right) or not appear (Left) in the exception message + * @param stdout snippets of text that should appear (Right) or not appear (Left) in STDOUT + * @param stderr snippets of text that should appear (Right) or not appear (Left) in STDERR + * @param stackTrace snippets of text that should appear (Right) or not appear (Left) in the stack trace + * @tparam the type of exception that should occur + */ + case class ChiselMainExceptionTest[A <: Throwable]( + args: Array[String], + generator: Option[Class[_ <: RawModule]] = None, + message: Seq[Either[String, String]] = Seq.empty, + stdout: Seq[Either[String, String]] = Seq.empty, + stderr: Seq[Either[String, String]] = Seq.empty, + stackTrace: Seq[Either[String, String]] = Seq.empty + ) { + def testName: String = "args" + args.mkString("_") + def argsString: String = args.mkString(" ") + } + def runStageExpectFiles(p: ChiselMainTest): Unit = { Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") { val f = new ChiselMainFixture @@ -85,32 +108,33 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit p.files.foreach( f => new File(td.buildDir + s"/$f").delete() ) When(s"""the user tries to compile with '${p.argsString}'""") + val module: Array[String] = + (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) } + else { Array.empty[String] }) + f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args) val (stdout, stderr, result) = grabStdOutErr { catchStatus { - val module: Array[String] = Array("foo") ++ - (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) } - else { Array.empty[String] }) f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args) } } - p.stdout match { - case Some(a) => + p.stdout.foreach { + case Right(a) => Then(s"""STDOUT should include "$a"""") stdout should include (a) - case None => - Then(s"nothing should print to STDOUT") - stdout should be (empty) + case Left(a) => + Then(s"""STDOUT should not include "$a"""") + stdout should not include (a) } - p.stderr match { - case Some(a) => - And(s"""STDERR should include "$a"""") + p.stderr.foreach { + case Right(a) => + Then(s"""STDERR should include "$a"""") stderr should include (a) - case None => - And(s"nothing should print to STDERR") - stderr should be (empty) + case Left(a) => + Then(s"""STDERR should not include "$a"""") + stderr should not include (a) } p.result match { @@ -131,56 +155,128 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit } } + /** Run a ChiselMainExceptionTest and verify that all the properties it spells out hold. + * @param p the test to run + * @tparam the type of the exception to catch (you shouldn't have to explicitly provide this) + */ + def runStageExpectException[A <: Throwable: scala.reflect.ClassTag](p: ChiselMainExceptionTest[A]): Unit = { + Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") { + val f = new ChiselMainFixture + val td = new TargetDirectoryFixture(p.testName) + + When(s"""the user tries to compile with '${p.argsString}'""") + val module: Array[String] = + (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) } + else { Array.empty[String] }) + val (stdout, stderr, result) = + grabStdOutErr { + catchStatus { + intercept[A] { + f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args) + } + } + } + + Then("the expected exception was thrown") + result should be a ('right) + val exception = result.right.get + info(s""" - Exception was a "${exception.getClass.getName}"""") + + val message = exception.getMessage + p.message.foreach { + case Right(a) => + Then(s"""STDOUT should include "$a"""") + message should include (a) + case Left(a) => + Then(s"""STDOUT should not include "$a"""") + message should not include (a) + } + + p.stdout.foreach { + case Right(a) => + Then(s"""STDOUT should include "$a"""") + stdout should include (a) + case Left(a) => + Then(s"""STDOUT should not include "$a"""") + stdout should not include (a) + } + + p.stderr.foreach { + case Right(a) => + Then(s"""STDERR should include "$a"""") + stderr should include (a) + case Left(a) => + Then(s"""STDERR should not include "$a"""") + stderr should not include (a) + } + + val stackTraceString = exception.getStackTrace.mkString("\n") + p.stackTrace.foreach { + case Left(a) => + And(s"""the stack does not include "$a"""") + stackTraceString should not include (a) + case Right(a) => + And(s"""the stack trace includes "$a"""") + stackTraceString should include (a) + } + + } + } + info("As a Chisel user") info("I compile a design") Feature("show elaborating message") { runStageExpectFiles( ChiselMainTest(args = Array("-X", "high"), - generator = Some(classOf[SameTypesModule]), - stdout = Some("Done elaborating.") + generator = Some(classOf[SameTypesModule]) ) ) } info("I screw up and compile some bad code") - Feature("Stack trace trimming") { + Feature("Stack trace trimming of ChiselException") { Seq( - ChiselMainTest(args = Array("-X", "low"), - generator = Some(classOf[DifferentTypesModule]), - stdout = Some("Stack trace trimmed to user code only"), - result = 1), - ChiselMainTest(args = Array("-X", "high", "--full-stacktrace"), - generator = Some(classOf[DifferentTypesModule]), - stdout = Some("org.scalatest"), - result = 1) - ).foreach(runStageExpectFiles) + ChiselMainExceptionTest[chisel3.internal.ChiselException]( + args = Array("-X", "low"), + generator = Some(classOf[DifferentTypesModule]), + stackTrace = Seq(Left("java"), Right(classOf[DifferentTypesModule].getName)) + ), + ChiselMainExceptionTest[chisel3.internal.ChiselException]( + args = Array("-X", "low", "--full-stacktrace"), + generator = Some(classOf[DifferentTypesModule]), + stackTrace = Seq(Right("java"), Right(classOf[DifferentTypesModule].getName)) + ) + ).foreach(runStageExpectException) } - Feature("Report properly trimmed stack traces") { + Feature("Stack trace trimming of user exceptions") { Seq( - ChiselMainTest(args = Array("-X", "low"), - generator = Some(classOf[FailingRequirementModule]), - stdout = Some("requirement failed"), - result = 1), - ChiselMainTest(args = Array("-X", "low", "--full-stacktrace"), - generator = Some(classOf[FailingRequirementModule]), - stdout = Some("chisel3.internal.ChiselException"), - result = 1) - ).foreach(runStageExpectFiles) + ChiselMainExceptionTest[java.lang.IllegalArgumentException]( + args = Array("-X", "low"), + generator = Some(classOf[FailingRequirementModule]), + stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Left("java")) + ), + ChiselMainExceptionTest[java.lang.IllegalArgumentException]( + args = Array("-X", "low", "--full-stacktrace"), + generator = Some(classOf[FailingRequirementModule]), + stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Right("java")) + ) + ).foreach(runStageExpectException) } - Feature("Builder.error source locator") { + Feature("Stack trace trimming and Builder.error errors") { Seq( - ChiselMainTest(args = Array("-X", "none"), + ChiselMainExceptionTest[chisel3.internal.ChiselException]( + args = Array("-X", "low"), generator = Some(classOf[BuilderErrorModule]), - stdout = Some("ChiselMainSpec.scala:41: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"), - result = 1) - ).foreach(runStageExpectFiles) + message = Seq(Right("Fatal errors during hardware elaboration")), + stdout = Seq(Right("ChiselMainSpec.scala:43: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule")) + ) + ).foreach(runStageExpectException) } Feature("Specifying a custom output file") { runStageExpectFiles(ChiselMainTest( args = Array("--chisel-output-file", "Foo", "--no-run-firrtl"), generator = Some(classOf[SameTypesModule]), - stdout = Some(""), files = Seq("Foo.fir"), fileChecks = Map( "Foo.fir" -> { file => @@ -192,7 +288,6 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit runStageExpectFiles(ChiselMainTest( args = Array("--chisel-output-file", "Foo.pb", "--no-run-firrtl"), generator = Some(classOf[SameTypesModule]), - stdout = Some(""), files = Seq("Foo.pb"), fileChecks = Map( "Foo.pb" -> { file => @@ -209,10 +304,10 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit Seq( ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestClassAspect" ), generator = Some(classOf[SameTypesModule]), - stdout = Some("Ran inspectingAspect")), + stdout = Seq(Right("Ran inspectingAspect"))), ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestObjectAspect" ), generator = Some(classOf[SameTypesModule]), - stdout = Some("Ran inspectingAspect")) + stdout = Seq(Right("Ran inspectingAspect"))) ).foreach(runStageExpectFiles) } diff --git a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala index 98bbb2ea..167e414b 100644 --- a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala +++ b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala @@ -30,6 +30,10 @@ object ChiselStageSpec { out := memory(bar.out) } + class UserExceptionModule extends RawModule { + assert(false, "User threw an exception") + } + } class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { @@ -40,13 +44,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { val stage = new ChiselStage } - behavior of "ChiselStage.emitChirrtl" + behavior of "ChiselStage$.emitChirrtl" it should "return a CHIRRTL string" in { ChiselStage.emitChirrtl(new Foo) should include ("infer mport") } - behavior of "ChiselStage.emitFirrtl" + behavior of "ChiselStage$.emitFirrtl" it should "return a High FIRRTL string" in { ChiselStage.emitFirrtl(new Foo) should include ("mem memory") @@ -58,7 +62,7 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { .emitFirrtl(new Foo, args) should include ("module Bar") } - behavior of "ChiselStage.emitVerilog" + behavior of "ChiselStage$.emitVerilog" it should "return a Verilog string" in { ChiselStage.emitVerilog(new Foo) should include ("endmodule") @@ -142,4 +146,58 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { exactly (1, order) should be (Dependency[chisel3.stage.phases.Elaborate]) } + behavior of "ChiselStage$ exception handling" + + it should "truncate a user exception" in { + info("The user's java.lang.AssertionError was thrown") + val exception = intercept[java.lang.AssertionError] { + ChiselStage.emitChirrtl(new UserExceptionModule) + } + + val message = exception.getMessage + info("The exception includes the user's message") + message should include ("User threw an exception") + + info("The stack trace is trimmed") + exception.getStackTrace.mkString("\n") should not include ("java") + } + + behavior of "ChiselStage exception handling" + + it should "truncate a user exception" in { + info("The user's java.lang.AssertionError was thrown") + val exception = intercept[java.lang.AssertionError] { + (new ChiselStage).emitChirrtl(new UserExceptionModule) + } + + info(s""" - Exception was a ${exception.getClass.getName}""") + + val message = exception.getMessage + info("The exception includes the user's message") + message should include ("User threw an exception") + + val stackTrace = exception.getStackTrace.mkString("\n") + info("The stack trace is trimmed") + stackTrace should not include ("java") + + info("The stack trace include information about running --full-stacktrace") + stackTrace should include ("--full-stacktrace") + } + + it should """not truncate a user exception with "--full-stacktrace"""" in { + info("The user's java.lang.AssertionError was thrown") + val exception = intercept[java.lang.AssertionError] { + (new ChiselStage).emitChirrtl(new UserExceptionModule, Array("--full-stacktrace")) + } + + info(s""" - Exception was a ${exception.getClass.getName}""") + + val message = exception.getMessage + info("The exception includes the user's message") + message should include ("User threw an exception") + + info("The stack trace is not trimmed") + exception.getStackTrace.mkString("\n") should include ("java") + } + } |
