From b232716878173851851cd7cd3f24922efbd4ac28 Mon Sep 17 00:00:00 2001
From: Philippe Suter <philippe.suter@gmail.com>
Date: Fri, 26 Oct 2012 19:32:56 +0200
Subject: [PATCH] First (sbt) test to make it into master.

---
 .gitignore                                    |  4 +
 build.sbt                                     |  5 ++
 src/main/scala/leon/Reporter.scala            |  9 +-
 .../solvers/z3/UninterpretedZ3Solver.scala    | 27 +++---
 .../z3/UninterpretedZ3SolverTests.scala       | 85 +++++++++++++++++++
 5 files changed, 112 insertions(+), 18 deletions(-)
 create mode 100644 src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala

diff --git a/.gitignore b/.gitignore
index 8230049b1..a35dc06b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
+# Vim
+*.swp
+
+# sbt
 target
 /project/build
 /leon
diff --git a/build.sbt b/build.sbt
index 9c65cad86..817f553b6 100644
--- a/build.sbt
+++ b/build.sbt
@@ -12,5 +12,10 @@ scalacOptions += "-unchecked"
 
 libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.9.1-1"
 
+libraryDependencies += "org.scalatest" %% "scalatest" % "1.8" % "test"
+
 unmanagedBase <<= baseDirectory { base => base / "unmanaged" }
 
+fork in run := true
+
+fork in test := true
diff --git a/src/main/scala/leon/Reporter.scala b/src/main/scala/leon/Reporter.scala
index 2cdea3d02..03512770f 100644
--- a/src/main/scala/leon/Reporter.scala
+++ b/src/main/scala/leon/Reporter.scala
@@ -57,6 +57,11 @@ class DefaultReporter extends Reporter {
 }
 
 class QuietReporter extends DefaultReporter {
-  override def warning(msg: Any) = {}
-  override def info(msg: Any) = {}
+  override def warning(msg : Any) = {}
+  override def info(msg : Any) = {}
+}
+
+class SilentReporter extends QuietReporter {
+  override def error(msg : Any) = {}
+  override def fatalError(msg : Any) = throw new Exception("Fatal error: " + msg.toString)
 }
diff --git a/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala b/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala
index 10162c266..1ad2ed1fc 100644
--- a/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala
+++ b/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala
@@ -52,20 +52,25 @@ class UninterpretedZ3Solver(reporter : Reporter) extends Solver(reporter) with A
 
   // Where the solving occurs
   override def solveOrGetCounterexample(expression : Expr) : (Option[Boolean],Map[Identifier,Expr]) = {
+    restartZ3
+
     val emptyModel    = Map.empty[Identifier,Expr]
     val unknownResult = (None, emptyModel)
     val validResult   = (Some(true), emptyModel)
 
-    containedInvocations = false
     val result = toZ3Formula(expression).map { asZ3 => 
       z3.assertCnstr(z3.mkNot(asZ3))
       z3.checkAndGetModel() match {
         case (Some(false), _) => validResult
-        case (Some(true), model) if !containedInvocations => {
-          val variables = variablesOf(expression)
-          val r = (Some(false), modelToMap(model, variables))
-          model.delete
-          r
+        case (Some(true), model) => {
+          if(containsFunctionCalls(expression)) {
+            unknownResult
+          } else { 
+            val variables = variablesOf(expression)
+            val r = (Some(false), modelToMap(model, variables))
+            model.delete
+            r
+          }
         }
         case _ => unknownResult
       }
@@ -73,14 +78,4 @@ class UninterpretedZ3Solver(reporter : Reporter) extends Solver(reporter) with A
 
     result
   }
-
-  private var containedInvocations : Boolean = _
-
-  override def toZ3Formula(expr : Expr, m : Map[Identifier,Z3AST] = Map.empty) : Option[Z3AST] = {
-    expr match {
-      case FunctionInvocation(_, _) => containedInvocations = true
-      case _ => ;
-    }
-    super.toZ3Formula(expr,m)
-  }
 }
diff --git a/src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala b/src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala
new file mode 100644
index 000000000..168205440
--- /dev/null
+++ b/src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala
@@ -0,0 +1,85 @@
+package leon.test.solvers.z3
+
+import leon.purescala.Common._
+import leon.purescala.Definitions._
+import leon.purescala.Trees._
+import leon.purescala.TreeOps._
+import leon.purescala.TypeTrees._
+
+import leon.SilentReporter
+
+import leon.Extensions.Solver
+import leon.solvers.z3.UninterpretedZ3Solver
+
+import org.scalatest.FunSuite
+
+class UninterpretedZ3SolverTests extends FunSuite {
+  private var testCounter : Int = 0
+  private def solverCheck(solver : Solver, expr : Expr, expected : Option[Boolean], msg : String) = {
+    testCounter += 1
+
+    test("Solver test #" + testCounter) {
+      assert(solver.solve(expr) === expected, msg)
+    }
+  }
+
+  private def assertValid(solver : Solver, expr : Expr) = solverCheck(
+    solver, expr, Some(true),
+    "Solver should prove the formula " + expr + " valid."
+  )
+
+  private def assertInvalid(solver : Solver, expr : Expr) = solverCheck(
+    solver, expr, Some(false),
+    "Solver should prove the formula " + expr + " invalid."
+  )
+
+  private def assertUnknown(solver : Solver, expr : Expr) = solverCheck(
+    solver, expr, None,
+    "Solver should not be able to decide the formula " + expr + "."
+  )
+
+  private val silentReporter = new SilentReporter
+
+  // def f(fx : Int) : Int = fx + 1
+  private val fx   : Identifier = FreshIdentifier("x").setType(Int32Type)
+  private val fDef : FunDef = new FunDef(FreshIdentifier("f"), Int32Type, VarDecl(fx, Int32Type) :: Nil)
+  fDef.body = Some(Plus(Variable(fx), IntLiteral(1)))
+
+  private val minimalProgram = Program(
+    FreshIdentifier("Minimal"), 
+    ObjectDef(FreshIdentifier("Minimal"), Seq(
+      fDef
+    ), Seq.empty)
+  )
+
+  private val x : Expr = Variable(FreshIdentifier("x").setType(Int32Type))
+  private val y : Expr = Variable(FreshIdentifier("y").setType(Int32Type))
+  private def f(e : Expr) : Expr = FunctionInvocation(fDef, e :: Nil)
+
+  private val solver = new UninterpretedZ3Solver(silentReporter)
+  solver.setProgram(minimalProgram)
+
+  private val tautology1 : Expr = BooleanLiteral(true)
+  assertValid(solver, tautology1)
+
+  private val tautology2 : Expr = Equals(Plus(x, x), Times(IntLiteral(2), x))
+  assertValid(solver, tautology2)
+
+  // This one contains a function invocation but is valid regardless of its
+  // interpretation, so should be proved anyway.
+  private val tautology3 : Expr = Implies(
+    Not(Equals(f(x), f(y))),
+    Not(Equals(x, y))
+  )
+  assertValid(solver, tautology3)
+
+  private val wrong1 : Expr = BooleanLiteral(false)
+  assertInvalid(solver, wrong1)
+
+  private val wrong2 : Expr = Equals(Plus(x, x), Times(IntLiteral(3), x))
+  assertInvalid(solver, wrong2)
+
+  // This is true, but that solver shouldn't know it.
+  private val unknown1 : Expr = Equals(f(x), Plus(x, IntLiteral(1)))
+  assertUnknown(solver, unknown1)
+}
-- 
GitLab