aboutsummaryrefslogtreecommitdiff
path: root/src/test/scala/firrtlTests/options/phases
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/scala/firrtlTests/options/phases')
-rw-r--r--src/test/scala/firrtlTests/options/phases/ChecksSpec.scala48
-rw-r--r--src/test/scala/firrtlTests/options/phases/GetIncludesSpec.scala96
-rw-r--r--src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala108
3 files changed, 252 insertions, 0 deletions
diff --git a/src/test/scala/firrtlTests/options/phases/ChecksSpec.scala b/src/test/scala/firrtlTests/options/phases/ChecksSpec.scala
new file mode 100644
index 00000000..e04ba554
--- /dev/null
+++ b/src/test/scala/firrtlTests/options/phases/ChecksSpec.scala
@@ -0,0 +1,48 @@
+// See LICENSE for license details.
+
+package firrtlTests.options.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import firrtl.AnnotationSeq
+import firrtl.options.{OptionsException, OutputAnnotationFileAnnotation, Phase, TargetDirAnnotation}
+import firrtl.options.phases.Checks
+
+class ChecksSpec extends FlatSpec with Matchers {
+
+ val targetDir = TargetDirAnnotation("foo")
+ val annoOut = OutputAnnotationFileAnnotation("bar")
+
+ /* A minimum annotation Seq that will pass [[Checks]] */
+ val min = Seq(targetDir)
+
+ def checkExceptionMessage(phase: Phase, annotations: AnnotationSeq, messageStart: String): Unit =
+ intercept[OptionsException]{ phase.transform(annotations) }.getMessage should startWith(messageStart)
+
+ class Fixture { val phase: Phase = new Checks }
+
+ behavior of classOf[Checks].toString
+
+ it should "enforce exactly one TargetDirAnnotation" in new Fixture {
+ info("0 target directories throws an exception")
+ checkExceptionMessage(phase, Seq.empty, "Exactly one target directory must be specified")
+
+ info("2 target directories throws an exception")
+ checkExceptionMessage(phase, Seq(targetDir, targetDir), "Exactly one target directory must be specified")
+ }
+
+ it should "enforce zero or one output annotation files" in new Fixture {
+ info("0 output annotation files is okay")
+ phase.transform(Seq(targetDir))
+
+ info("2 output annotation files throws an exception")
+ val in = Seq(targetDir, annoOut, annoOut)
+ checkExceptionMessage(phase, in, "At most one output annotation file can be specified")
+ }
+
+ it should "pass if the minimum annotations are specified" in new Fixture {
+ info(s"""Minimum required: ${min.map(_.getClass.getSimpleName).mkString(", ")}""")
+ phase.transform(min)
+ }
+
+}
diff --git a/src/test/scala/firrtlTests/options/phases/GetIncludesSpec.scala b/src/test/scala/firrtlTests/options/phases/GetIncludesSpec.scala
new file mode 100644
index 00000000..5b07d0e0
--- /dev/null
+++ b/src/test/scala/firrtlTests/options/phases/GetIncludesSpec.scala
@@ -0,0 +1,96 @@
+// See LICENSE for license details.
+
+package firrtlTests.options.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import java.io.{File, PrintWriter}
+
+import firrtl.AnnotationSeq
+import firrtl.annotations.{Annotation, AnnotationFileNotFoundException, JsonProtocol,
+ NoTargetAnnotation}
+import firrtl.options.phases.GetIncludes
+import firrtl.options.{InputAnnotationFileAnnotation, Phase}
+import firrtl.util.BackendCompilationUtilities
+
+case object A extends NoTargetAnnotation
+case object B extends NoTargetAnnotation
+case object C extends NoTargetAnnotation
+case object D extends NoTargetAnnotation
+case object E extends NoTargetAnnotation
+
+class GetIncludesSpec extends FlatSpec with Matchers with BackendCompilationUtilities with firrtlTests.Utils {
+
+
+ val dir = new File("test_run_dir/GetIncludesSpec")
+ dir.mkdirs()
+
+ def ref(filename: String): InputAnnotationFileAnnotation = InputAnnotationFileAnnotation(s"$dir/$filename.anno.json")
+
+ def checkAnnos(a: AnnotationSeq, b: AnnotationSeq): Unit = {
+ info("read the expected number of annotations")
+ a.size should be (b.size)
+
+ info("annotations match exact order")
+ a.zip(b).foreach{ case (ax, bx) => ax should be (bx) }
+ }
+
+ val files = Seq(
+ new File(dir + "/a.anno.json") -> Seq(A, ref("b")),
+ new File(dir + "/b.anno.json") -> Seq(B, ref("c"), ref("a")),
+ new File(dir + "/c.anno.json") -> Seq(C, ref("d"), ref("e")),
+ new File(dir + "/d.anno.json") -> Seq(D),
+ new File(dir + "/e.anno.json") -> Seq(E)
+ )
+
+ files.foreach{ case (file, annotations) =>
+ val pw = new PrintWriter(file)
+ pw.write(JsonProtocol.serialize(annotations))
+ pw.close()
+ }
+
+ class Fixture { val phase: Phase = new GetIncludes }
+
+ behavior of classOf[GetIncludes].toString
+
+ it should "throw an exception if the annotation file doesn't exit" in new Fixture {
+ intercept[AnnotationFileNotFoundException]{ phase.transform(Seq(ref("f"))) }
+ .getMessage should startWith("Annotation file")
+ }
+
+ it should "read annotations from a file" in new Fixture {
+ val e = ref("e")
+ val in = Seq(e)
+ val expect = Seq(E)
+ val out = phase.transform(in)
+
+ checkAnnos(out, expect)
+ }
+
+ it should "read annotations from multiple files, but not reading duplicates" in new Fixture {
+ val Seq(d, e) = Seq("d", "e").map(ref)
+ val in = Seq(d, e, e, d)
+ val expect = Seq(D, E)
+ val (stdout, _, out) = grabStdOutErr { phase.transform(in) }
+
+ checkAnnos(out, expect)
+
+ Seq("d", "e").foreach{ x =>
+ info(s"a warning about '$x.anno.json' was printed")
+ stdout should include (s"Warning: Annotation file ($dir/$x.anno.json) already included!")
+ }
+ }
+
+ it should "handle recursive references gracefully, but show a warning" in new Fixture {
+ val Seq(a, b, c, d, e) = Seq("a", "b", "c", "d", "e").map(ref)
+ val in = Seq(a)
+ val expect = Seq(A, B, C, D, E)
+ val (stdout, _, out) = grabStdOutErr { phase.transform(in) }
+
+ checkAnnos(out, expect)
+
+ info("a warning about 'a.anno.json' was printed")
+ stdout should include (s"Warning: Annotation file ($dir/a.anno.json)")
+ }
+
+}
diff --git a/src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala b/src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala
new file mode 100644
index 00000000..07aad53f
--- /dev/null
+++ b/src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala
@@ -0,0 +1,108 @@
+// See LICENSE for license details.
+
+package firrtlTests.options.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import java.io.File
+
+import firrtl.{AnnotationSeq, EmittedFirrtlCircuitAnnotation, EmittedFirrtlCircuit}
+import firrtl.annotations.{DeletedAnnotation, NoTargetAnnotation}
+import firrtl.options.{InputAnnotationFileAnnotation, OutputAnnotationFileAnnotation, Phase, WriteDeletedAnnotation}
+import firrtl.options.phases.{GetIncludes, WriteOutputAnnotations}
+import firrtl.stage.FirrtlFileAnnotation
+
+class WriteOutputAnnotationsSpec extends FlatSpec with Matchers with firrtlTests.Utils {
+
+ val dir = "test_run_dir/WriteOutputAnnotationSpec"
+
+ /** Check if the annotations contained by a [[File]] and the same, and in the same order, as a reference
+ * [[AnnotationSeq]]. This uses [[GetIncludes]] as that already knows how to read annotation files.
+ * @param f a file to read
+ * @param a the expected annotations
+ */
+ private def fileContainsAnnotations(f: File, a: AnnotationSeq): Unit = {
+ info(s"output file '$f' exists")
+ f should (exist)
+
+ info(s"reading '$f' works")
+ val read = (new GetIncludes)
+ .transform(Seq(InputAnnotationFileAnnotation(f.toString)))
+ .filterNot{
+ case a @ DeletedAnnotation(_, _: InputAnnotationFileAnnotation) => true
+ case _ => false }
+
+ info(s"annotations in file are expected size")
+ read.size should be (a.size)
+
+ read
+ .zip(a)
+ .foreach{ case (read, expected) =>
+ info(s"$read matches")
+ read should be (expected) }
+
+ f.delete()
+ }
+
+ class Fixture { val phase: Phase = new WriteOutputAnnotations }
+
+ behavior of classOf[WriteOutputAnnotations].toString
+
+ it should "write annotations to a file (excluding DeletedAnnotations)" in new Fixture {
+ val file = new File(dir + "/should-write-annotations-to-a-file.anno.json")
+ val annotations = Seq( OutputAnnotationFileAnnotation(file.toString),
+ WriteOutputAnnotationsSpec.FooAnnotation,
+ WriteOutputAnnotationsSpec.BarAnnotation(0),
+ WriteOutputAnnotationsSpec.BarAnnotation(1),
+ DeletedAnnotation("foo", WriteOutputAnnotationsSpec.FooAnnotation) )
+ val expected = annotations.filter {
+ case a: DeletedAnnotation => false
+ case a => true
+ }
+ val out = phase.transform(annotations)
+
+ info("annotations are unmodified")
+ out.toSeq should be (annotations)
+
+ fileContainsAnnotations(file, expected)
+ }
+
+ it should "include DeletedAnnotations if a WriteDeletedAnnotation is present" in new Fixture {
+ val file = new File(dir + "should-include-deleted.anno.json")
+ val annotations = Seq( OutputAnnotationFileAnnotation(file.toString),
+ WriteOutputAnnotationsSpec.FooAnnotation,
+ WriteOutputAnnotationsSpec.BarAnnotation(0),
+ WriteOutputAnnotationsSpec.BarAnnotation(1),
+ DeletedAnnotation("foo", WriteOutputAnnotationsSpec.FooAnnotation),
+ WriteDeletedAnnotation )
+ val out = phase.transform(annotations)
+
+ info("annotations are unmodified")
+ out.toSeq should be (annotations)
+
+ fileContainsAnnotations(file, annotations)
+ }
+
+ it should "do nothing if no output annotation file is specified" in new Fixture {
+ val annotations = Seq( WriteOutputAnnotationsSpec.FooAnnotation,
+ WriteOutputAnnotationsSpec.BarAnnotation(0),
+ WriteOutputAnnotationsSpec.BarAnnotation(1) )
+
+ val out = catchWrites { phase.transform(annotations) } match {
+ case Right(a) =>
+ info("no file writes occurred")
+ a
+ case Left(a) =>
+ fail(s"No file writes expected, but a write to '$a' ocurred!")
+ }
+
+ info("annotations are unmodified")
+ out.toSeq should be (annotations)
+ }
+
+}
+
+private object WriteOutputAnnotationsSpec {
+ case object FooAnnotation extends NoTargetAnnotation
+ case class BarAnnotation(x: Int) extends NoTargetAnnotation
+}