aboutsummaryrefslogtreecommitdiff
path: root/libraries/eval/test
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/eval/test')
-rw-r--r--libraries/eval/test/EvalTest.scala268
-rw-r--r--libraries/eval/test/resources/Base.scala4
-rw-r--r--libraries/eval/test/resources/Deprecated.scala6
-rw-r--r--libraries/eval/test/resources/Derived.scala1
-rw-r--r--libraries/eval/test/resources/DerivedWithInclude.scala3
-rw-r--r--libraries/eval/test/resources/HelloJoe.scala3
-rw-r--r--libraries/eval/test/resources/IncludeInclude.scala7
-rw-r--r--libraries/eval/test/resources/OnePlusOne.scala1
-rw-r--r--libraries/eval/test/resources/RubyInclude.scala3
-rw-r--r--libraries/eval/test/resources/file-with-dash.scala1
10 files changed, 297 insertions, 0 deletions
diff --git a/libraries/eval/test/EvalTest.scala b/libraries/eval/test/EvalTest.scala
new file mode 100644
index 0000000..0489fba
--- /dev/null
+++ b/libraries/eval/test/EvalTest.scala
@@ -0,0 +1,268 @@
+package cbt.eval.test
+
+import java.io.{File, FileWriter}
+import org.scalatest.WordSpec
+import scala.io.Source
+import scala.language.reflectiveCalls
+import scala.reflect.internal.util.Position
+import scala.tools.nsc.Settings
+import scala.tools.nsc.reporters.{AbstractReporter, Reporter}
+import java.nio.file._
+
+class EvalTest extends WordSpec {
+ class Eval(f: Option[File] = None) extends cbt.eval.Eval(f){
+ override def classLoader = EvalTest.this.getClass.getClassLoader
+ }
+ object Eval extends Eval(None)
+ def fromResourcePath(path: String): File = {
+ assert(path.endsWith(".scala"))
+ val tmpFile = File.createTempFile(path.stripSuffix(".scala"),"scala")
+ Files.copy(
+ Paths.get( getClass.getResource(path).getFile),
+ tmpFile.toPath,
+ StandardCopyOption.REPLACE_EXISTING
+ )
+ tmpFile.deleteOnExit()
+ tmpFile
+ }
+ "Evaluator" should {
+
+ "apply('expression')" in {
+ assert((new Eval).apply[Int]("1 + 1") == 2)
+ }
+
+ "apply(new File(...))" in {
+ assert((new Eval).apply[Int](fromResourcePath("/OnePlusOne.scala")) == 2)
+ }
+
+ "apply(new File(...), new File(...))" in {
+ val derived = (new Eval).apply[() => String](
+ fromResourcePath("/Base.scala"),
+ fromResourcePath("/Derived.scala"))
+ assert(derived() == "hello")
+ }
+
+ "apply(new File(...) with a dash in the name with target" in {
+ val f = File.createTempFile("eval", "target")
+ f.delete()
+ f.mkdir()
+ val e = new Eval(Some(f))
+ val sourceFile = fromResourcePath("/file-with-dash.scala")
+ val res: String = e(sourceFile)
+ assert(res == "hello")
+ val className = e.fileToClassName(sourceFile)
+ val processedSource = e.sourceForString(Source.fromFile(sourceFile).getLines().mkString("\n"))
+ val fullClassName = "Evaluator__%s_%s.class".format(
+ className, e.uniqueId(processedSource, None))
+ val targetFileName = f.getAbsolutePath() + File.separator + fullClassName
+ val targetFile = new File(targetFileName)
+ assert(targetFile.exists)
+ }
+
+ "apply(new File(...) with target" in {
+ val f = File.createTempFile("eval", "target")
+ f.delete()
+ f.mkdir()
+ val e = new Eval(Some(f))
+ val sourceFile = fromResourcePath("/OnePlusOne.scala")
+ val res: Int = e(sourceFile)
+ assert(res == 2)
+
+ // make sure it created a class file with the expected name
+ val className = e.fileToClassName(sourceFile)
+ val processedSource = e.sourceForString(Source.fromFile(sourceFile).getLines().mkString("\n"))
+ val fullClassName = "Evaluator__%s_%s.class".format(
+ className, e.uniqueId(processedSource, None))
+ val targetFileName = f.getAbsolutePath() + File.separator + fullClassName
+ val targetFile = new File(targetFileName)
+ assert(targetFile.exists)
+ val targetMod = targetFile.lastModified
+
+ // eval again, make sure it works
+ val res2: Int = e(sourceFile)
+ // and make sure it didn't create a new file (1 + checksum)
+ assert(f.listFiles.length == 2)
+ // and make sure it didn't update the file
+ val targetFile2 = new File(targetFileName)
+ assert(targetFile2.lastModified == targetMod)
+
+ // touch source, ensure no-recompile (checksum hasn't changed)
+ sourceFile.setLastModified(System.currentTimeMillis())
+ val res3: Int = e(sourceFile)
+ assert(res3 == 2)
+ // and make sure it didn't create a different file
+ assert(f.listFiles.length == 2)
+ // and make sure it updated the file
+ val targetFile3 = new File(targetFileName)
+ assert(targetFile3.lastModified == targetMod)
+
+ // append a newline, altering checksum, verify recompile
+ val writer = new FileWriter(sourceFile)
+ writer.write("//a comment\n2\n")
+ writer.close()
+ val res4: Int = e(sourceFile)
+ assert(res4 == 2)
+ // and make sure it created a new file
+ val targetFile4 = new File(targetFileName)
+ assert(!targetFile4.exists)
+ }
+
+ "apply(InputStream)" in {
+ assert((new Eval).apply[Int](getClass.getResourceAsStream("/OnePlusOne.scala")) == 2)
+ }
+
+ "uses deprecated" in {
+ val deprecated = (new Eval).apply[() => String](
+ fromResourcePath("/Deprecated.scala"))
+ assert(deprecated() == "hello")
+ }
+
+ "inPlace('expression')" in {
+ // Old object API works
+ Eval.compile("object Doubler { def apply(n: Int) = n * 2 }")
+ assert(Eval.inPlace[Int]("Doubler(2)") == 4)
+ assert(Eval.inPlace[Int]("Doubler(14)") == 28)
+ // New class API fails
+ // val eval = new Eval
+ // eval.compile("object Doubler { def apply(n: Int) = n * 2 }")
+ // assert(eval.inPlace[Int]("Doubler(2)") == 4)
+ // assert(eval.inPlace[Int]("Doubler(14)") == 28)
+ }
+
+ "check" in {
+ (new Eval).check("23")
+ intercept[Eval.CompilerException] {
+ (new Eval).check("invalid")
+ }
+ }
+
+ "#include" in {
+ val derived = Eval[() => String](
+ fromResourcePath("/Base.scala"),
+ fromResourcePath("/DerivedWithInclude.scala"))
+ assert(derived() == "hello")
+ assert(derived.toString == "hello, joe")
+ }
+
+ "recursive #include" in {
+ val derived = Eval[() => String](
+ fromResourcePath("/Base.scala"),
+ fromResourcePath("/IncludeInclude.scala"))
+ assert(derived() == "hello")
+ assert(derived.toString == "hello, joe; hello, joe")
+ }
+
+ "toSource returns post-processed code" in {
+ val derived = Eval.toSource(fromResourcePath("/DerivedWithInclude.scala"))
+ assert(derived.contains("hello, joe"))
+ assert(derived.contains("new Base"))
+ }
+
+ "throws a compilation error when Ruby is #included" in {
+ intercept[Throwable] {
+ Eval[() => String](
+ fromResourcePath("RubyInclude.scala")
+ )
+ }
+ }
+
+ "clean class names" in {
+ val e = new Eval()
+ // regular old scala file
+ assert(e.fileToClassName(new File("foo.scala")) == "foo")
+ // without an extension
+ assert(e.fileToClassName(new File("foo")) == "foo")
+ // with lots o dots
+ assert(e.fileToClassName(new File("foo.bar.baz")) == "foo$2ebar")
+ // with dashes
+ assert(e.fileToClassName(new File("foo-bar-baz.scala")) == "foo$2dbar$2dbaz")
+ // with crazy things
+ assert(e.fileToClassName(new File("foo$! -@@@")) == "foo$24$21$20$2d$40$40$40")
+ }
+
+ "allow custom error reporting" when {
+ class Ctx {
+ val eval = new Eval {
+ @volatile var errors: Seq[(String, String)] = Nil
+
+ override lazy val compilerMessageHandler: Option[Reporter] = Some(new AbstractReporter {
+ override val settings: Settings = compilerSettings
+ override def displayPrompt(): Unit = ()
+ override def display(pos: Position, msg: String, severity: this.type#Severity): Unit = {
+ errors = errors :+ ((msg, severity.toString))
+ }
+ override def reset() = {
+ super.reset()
+ errors = Nil
+ }
+ })
+ }
+ }
+
+ "not report errors on success" in {
+ val ctx = new Ctx
+ import ctx._
+
+ assert(eval[Int]("val a = 3; val b = 2; a + b", true) == 5)
+ assert(eval.errors.isEmpty)
+ }
+
+ "report errors on bad code" in {
+ val ctx = new Ctx
+ import ctx._
+
+ intercept[Throwable] {
+ eval[Int]("val a = 3; val b = q; a + b", true)
+ }
+ assert(eval.errors.nonEmpty)
+ }
+
+ "reset state between invocations" in {
+ val ctx = new Ctx
+ import ctx._
+
+ intercept[Throwable] {
+ eval[Int]("val a = 3; val b = q; a + b", true)
+ }
+ assert(eval.errors.nonEmpty)
+ assert(eval[Int]("val d = 3; val e = 2; d + e", true) == 5)
+ assert(eval.errors.isEmpty)
+ }
+
+ "reset reporter between inPlace invocations" in {
+ val ctx = new Ctx
+ import ctx._
+
+ intercept[Throwable] {
+ eval.inPlace[Int]("val a = 3; val b = q; a + b")
+ }
+ assert(eval.errors.nonEmpty)
+ assert(eval.inPlace[Int]("val d = 3; val e = 2; d + e") == 5)
+ assert(eval.errors.isEmpty)
+ }
+
+ "reporter should be reset between checks, but loaded class should remain" in {
+ val ctx = new Ctx
+ import ctx._
+
+ // compile and load compiled class
+ eval.compile("class A()")
+
+ intercept[Throwable] {
+ eval.check("new B()")
+ }
+ assert(eval.errors.nonEmpty)
+
+ eval.check("new A()")
+ assert(eval.errors.isEmpty)
+ }
+
+ "eval with overriden classloader" in {
+ val eval = new Eval{
+ override def classLoader = this.getClass.getClassLoader
+ }
+ assert(eval.apply[Int]("1 + 1") == 2)
+ }
+ }
+ }
+}
diff --git a/libraries/eval/test/resources/Base.scala b/libraries/eval/test/resources/Base.scala
new file mode 100644
index 0000000..ec8ae22
--- /dev/null
+++ b/libraries/eval/test/resources/Base.scala
@@ -0,0 +1,4 @@
+trait Base extends (() => String) {
+ def apply() = "hello"
+}
+
diff --git a/libraries/eval/test/resources/Deprecated.scala b/libraries/eval/test/resources/Deprecated.scala
new file mode 100644
index 0000000..979affb
--- /dev/null
+++ b/libraries/eval/test/resources/Deprecated.scala
@@ -0,0 +1,6 @@
+new (() => String) {
+ @deprecated("don't use hello")
+ def hello() = "hello"
+
+ def apply() = hello()
+}
diff --git a/libraries/eval/test/resources/Derived.scala b/libraries/eval/test/resources/Derived.scala
new file mode 100644
index 0000000..1d8e0d8
--- /dev/null
+++ b/libraries/eval/test/resources/Derived.scala
@@ -0,0 +1 @@
+new Base { }
diff --git a/libraries/eval/test/resources/DerivedWithInclude.scala b/libraries/eval/test/resources/DerivedWithInclude.scala
new file mode 100644
index 0000000..476d60e
--- /dev/null
+++ b/libraries/eval/test/resources/DerivedWithInclude.scala
@@ -0,0 +1,3 @@
+new Base {
+#include HelloJoe.scala
+}
diff --git a/libraries/eval/test/resources/HelloJoe.scala b/libraries/eval/test/resources/HelloJoe.scala
new file mode 100644
index 0000000..c6ea42e
--- /dev/null
+++ b/libraries/eval/test/resources/HelloJoe.scala
@@ -0,0 +1,3 @@
+/* real-time declarative programming now */
+override def toString = "hello, joe"
+
diff --git a/libraries/eval/test/resources/IncludeInclude.scala b/libraries/eval/test/resources/IncludeInclude.scala
new file mode 100644
index 0000000..cc1f966
--- /dev/null
+++ b/libraries/eval/test/resources/IncludeInclude.scala
@@ -0,0 +1,7 @@
+val b1 =
+#include DerivedWithInclude.scala
+val b2 =
+#include DerivedWithInclude.scala
+new Base {
+ override def toString = b1 + "; " + b2
+}
diff --git a/libraries/eval/test/resources/OnePlusOne.scala b/libraries/eval/test/resources/OnePlusOne.scala
new file mode 100644
index 0000000..8d2f097
--- /dev/null
+++ b/libraries/eval/test/resources/OnePlusOne.scala
@@ -0,0 +1 @@
+1 + 1
diff --git a/libraries/eval/test/resources/RubyInclude.scala b/libraries/eval/test/resources/RubyInclude.scala
new file mode 100644
index 0000000..a763d52
--- /dev/null
+++ b/libraries/eval/test/resources/RubyInclude.scala
@@ -0,0 +1,3 @@
+object CodeThatIncludesSomeRuby {
+#include hello.rb
+}
diff --git a/libraries/eval/test/resources/file-with-dash.scala b/libraries/eval/test/resources/file-with-dash.scala
new file mode 100644
index 0000000..3580093
--- /dev/null
+++ b/libraries/eval/test/resources/file-with-dash.scala
@@ -0,0 +1 @@
+"hello"