diff options
| author | Schuyler Eldridge | 2018-09-28 12:39:01 -0400 |
|---|---|---|
| committer | Schuyler Eldridge | 2018-11-07 13:26:09 -0500 |
| commit | 27c1b366ce58e93434e77e964365474f5e7aa8d7 (patch) | |
| tree | e54301b8019d17cce4448ce9d09589815a7315d5 /src | |
| parent | e0951fef346c4e3e2d19a57b396012e9800b69b6 (diff) | |
Add firrtl.options tests
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Diffstat (limited to 'src')
6 files changed, 245 insertions, 0 deletions
diff --git a/src/test/resources/META-INF/services/firrtl.options.RegisteredLibrary b/src/test/resources/META-INF/services/firrtl.options.RegisteredLibrary new file mode 100644 index 00000000..f28a6850 --- /dev/null +++ b/src/test/resources/META-INF/services/firrtl.options.RegisteredLibrary @@ -0,0 +1 @@ +firrtlTests.options.BarLibrary
\ No newline at end of file diff --git a/src/test/resources/META-INF/services/firrtl.options.RegisteredTransform b/src/test/resources/META-INF/services/firrtl.options.RegisteredTransform new file mode 100644 index 00000000..a9fd3bc5 --- /dev/null +++ b/src/test/resources/META-INF/services/firrtl.options.RegisteredTransform @@ -0,0 +1 @@ +firrtlTests.options.FooTransform
\ No newline at end of file diff --git a/src/test/scala/firrtlTests/options/OptionParserSpec.scala b/src/test/scala/firrtlTests/options/OptionParserSpec.scala new file mode 100644 index 00000000..ae4899d4 --- /dev/null +++ b/src/test/scala/firrtlTests/options/OptionParserSpec.scala @@ -0,0 +1,92 @@ +// See LICENSE for license details + +package firrtlTests.options + +import firrtl.{AnnotationSeq, FIRRTLException} +import firrtl.annotations.{Annotation, NoTargetAnnotation} +import firrtl.options.{DoNotTerminateOnExit, DuplicateHandling, OptionsException} + +import scopt.OptionParser + +import org.scalatest.{FlatSpec, Matchers} + +import java.security.Permission + +class OptionParserSpec extends FlatSpec with Matchers { + + case class IntAnnotation(x: Int) extends NoTargetAnnotation { + def extract: Int = x + } + + /* An option parser that prepends to a Seq[Int] */ + class IntParser extends OptionParser[AnnotationSeq]("Int Parser") { + opt[Int]("integer").abbr("n").unbounded.action( (x, c) => IntAnnotation(x) +: c ) + help("help") + } + + trait DuplicateShortOption { this: OptionParser[AnnotationSeq] => + opt[Int]("not-an-integer").abbr("n").unbounded.action( (x, c) => IntAnnotation(x) +: c ) + } + + trait DuplicateLongOption { this: OptionParser[AnnotationSeq] => + opt[Int]("integer").abbr("m").unbounded.action( (x, c) => IntAnnotation(x) +: c ) + } + + case class ExitException(status: Option[Int]) extends SecurityException("Found a sys.exit") + + /* Security manager that disallows calls to sys.exit */ + class ExceptOnExit extends SecurityManager { + override def checkPermission(perm: Permission): Unit = {} + override def checkPermission(perm: Permission, context: Object): Unit = {} + override def checkExit(status: Int): Unit = { + super.checkExit(status) + throw ExitException(Some(status)) + } + } + + /* Tell a parser to terminate in an environment where sys.exit throws an exception */ + def catchStatus(parser: OptionParser[_], exitState: Either[String, Unit]): Option[Int] = { + System.setSecurityManager(new ExceptOnExit()) + val status = try { + parser.terminate(exitState) + throw new ExitException(None) + } catch { + case ExitException(s) => s + } + System.setSecurityManager(null) + status + } + + behavior of "default OptionsParser" + + it should "terminate on exit" in { + val parser = new IntParser + + info("By default, exit statuses are reported") + catchStatus(parser, Left("some message")) should be (Some(1)) + catchStatus(parser, Right(Unit)) should be (Some(0)) + } + + behavior of "DoNotTerminateOnExit" + + it should "disable sys.exit for terminate method" in { + val parser = new IntParser with DoNotTerminateOnExit + catchStatus(parser, Left("some message")) should be (None) + catchStatus(parser, Right(Unit)) should be (None) + } + + behavior of "DuplicateHandling" + + it should "detect short duplicates" in { + val parser = new IntParser with DuplicateHandling with DuplicateShortOption + intercept[OptionsException] { parser.parse(Array[String](), Seq[Annotation]()) } + .getMessage should startWith ("Duplicate short option") + } + + it should "detect long duplicates" in { + val parser = new IntParser with DuplicateHandling with DuplicateLongOption + intercept[OptionsException] { parser.parse(Array[String](), Seq[Annotation]()) } + .getMessage should startWith ("Duplicate long option") + } + +} diff --git a/src/test/scala/firrtlTests/options/OptionsViewSpec.scala b/src/test/scala/firrtlTests/options/OptionsViewSpec.scala new file mode 100644 index 00000000..dec6a99f --- /dev/null +++ b/src/test/scala/firrtlTests/options/OptionsViewSpec.scala @@ -0,0 +1,75 @@ +// See LICENSE for license details + +package firrtlTests.options + +import org.scalatest.{FlatSpec, Matchers} + +import firrtl.options.OptionsView +import firrtl.AnnotationSeq +import firrtl.annotations.{Annotation,NoTargetAnnotation} + +class OptionsViewSpec extends FlatSpec with Matchers { + + /* Annotations */ + case class NameAnnotation(name: String) extends NoTargetAnnotation + case class ValueAnnotation(value: Int) extends NoTargetAnnotation + + /* The type we want to view the annotations as */ + case class Foo(name: Option[String] = None, value: Option[Int] = None) + case class Bar(name: String = "bar") + + /* An OptionsView that converts an AnnotationSeq to Option[Foo] */ + implicit object FooView extends OptionsView[Foo] { + private def append(foo: Foo, anno: Annotation): Foo = anno match { + case NameAnnotation(n) => foo.copy(name = Some(n)) + case ValueAnnotation(v) => foo.copy(value = Some(v)) + case _ => foo + } + + def view(options: AnnotationSeq): Option[Foo] = { + val annoSeq = options.foldLeft(Foo())(append) + Some(annoSeq) + } + } + + /* An OptionsView that converts an AnnotationSeq to Option[Bar] */ + implicit object BarView extends OptionsView[Bar] { + private def append(bar: Bar, anno: Annotation): Bar = anno match { + case NameAnnotation(n) => bar.copy(name = n) + case _ => bar + } + + def view(options: AnnotationSeq): Option[Bar] = { + val annoSeq = options.foldLeft(Bar())(append) + Some(annoSeq) + } + } + + behavior of "OptionsView" + + it should "convert annotations to one of two types" in { + /* Some default annotations */ + val annos = Seq(NameAnnotation("foo"), ValueAnnotation(42)) + + info("Foo conversion okay") + FooView.view(annos) should be (Some(Foo(Some("foo"), Some(42)))) + + info("Bar conversion okay") + BarView.view(annos) should be (Some(Bar("foo"))) + } + + behavior of "Viewer" + + it should "implicitly view annotations as the specified type" in { + import firrtl.options.Viewer._ + + /* Some empty annotations */ + val annos = Seq[Annotation]() + + info("Foo view okay") + view[Foo](annos) should be (Some(Foo(None, None))) + + info("Bar view okay") + view[Bar](annos) should be (Some(Bar())) + } +} diff --git a/src/test/scala/firrtlTests/options/RegistrationSpec.scala b/src/test/scala/firrtlTests/options/RegistrationSpec.scala new file mode 100644 index 00000000..c060341d --- /dev/null +++ b/src/test/scala/firrtlTests/options/RegistrationSpec.scala @@ -0,0 +1,54 @@ +// See LICENSE for license details. + +package firrtlTests.options + +import org.scalatest.{FlatSpec, Matchers} +import scopt.OptionParser +import java.util.ServiceLoader + +import firrtl.options.{RegisteredTransform, RegisteredLibrary} +import firrtl.passes.Pass +import firrtl.ir.Circuit +import firrtl.annotations.NoTargetAnnotation +import firrtl.AnnotationSeq + +case object HelloAnnotation extends NoTargetAnnotation + +class FooTransform extends Pass with RegisteredTransform { + def run(c: Circuit): Circuit = c + def addOptions(p: OptionParser[AnnotationSeq]): Unit = + p.opt[Unit]("hello") + .action( (_, c) => HelloAnnotation +: c ) +} + +class BarLibrary extends RegisteredLibrary { + def name: String = "Bar" + def addOptions(p: OptionParser[AnnotationSeq]): Unit = + p.opt[Unit]("world") + .action( (_, c) => HelloAnnotation +: c ) +} + +class RegistrationSpec extends FlatSpec with Matchers { + + behavior of "RegisteredTransform" + + it should "FooTransform should be discovered by Java.util.ServiceLoader" in { + val iter = ServiceLoader.load(classOf[RegisteredTransform]).iterator() + val transforms = scala.collection.mutable.ArrayBuffer[RegisteredTransform]() + while (iter.hasNext) { + transforms += iter.next() + } + transforms.map(_.getClass.getName) should contain ("firrtlTests.options.FooTransform") + } + + behavior of "RegisteredLibrary" + + it should "BarLibrary be discovered by Java.util.ServiceLoader" in { + val iter = ServiceLoader.load(classOf[RegisteredLibrary]).iterator() + val transforms = scala.collection.mutable.ArrayBuffer[RegisteredLibrary]() + while (iter.hasNext) { + transforms += iter.next() + } + transforms.map(_.getClass.getName) should contain ("firrtlTests.options.BarLibrary") + } +} diff --git a/src/test/scala/firrtlTests/options/ShellSpec.scala b/src/test/scala/firrtlTests/options/ShellSpec.scala new file mode 100644 index 00000000..d87a9a30 --- /dev/null +++ b/src/test/scala/firrtlTests/options/ShellSpec.scala @@ -0,0 +1,22 @@ +// See LICENSE for license details. + +package firrtlTests.options + +import org.scalatest._ + +import firrtl.options.Shell + +class ShellSpec extends FlatSpec with Matchers { + + behavior of "Shell" + + it should "detect all registered libraries and transforms" in { + val shell = new Shell("foo") + + info("Found FooTransform") + shell.registeredTransforms.map(_.getClass.getName) should contain ("firrtlTests.options.FooTransform") + + info("Found BarLibrary") + shell.registeredLibraries.map(_.getClass.getName) should contain ("firrtlTests.options.BarLibrary") + } +} |
