From 076f7ee12059aeb08c7f4c007f948d90ee526353 Mon Sep 17 00:00:00 2001 From: Philippe Suter <philippe.suter@gmail.com> Date: Mon, 29 Oct 2012 20:48:27 +0100 Subject: [PATCH] Integrated testing. --- src/main/scala/leon/Reporter.scala | 59 +++++----- .../purescala/invalid/InsertionSort.scala | 42 +++++++ .../purescala/invalid/ListOperations.scala | 107 ++++++++++++++++++ .../invalid/PropositionalLogic.scala | 86 ++++++++++++++ .../verification/purescala/invalid/README | 2 + .../purescala/invalid/RedBlackTree.scala | 102 +++++++++++++++++ .../purescala/valid/AssociativeList.scala | 4 +- .../purescala/valid/InsertionSort.scala | 13 --- .../purescala/valid/ListOperations.scala | 4 - .../purescala/valid/PropositionalLogic.scala | 11 -- .../purescala/valid/RedBlackTree.scala | 19 ---- .../scala/leon/test/PureScalaPrograms.scala | 84 ++++++++++++++ src/test/scala/leon/test/ValidPrograms.scala | 48 -------- 13 files changed, 456 insertions(+), 125 deletions(-) create mode 100644 src/test/resources/regression/verification/purescala/invalid/InsertionSort.scala create mode 100644 src/test/resources/regression/verification/purescala/invalid/ListOperations.scala create mode 100644 src/test/resources/regression/verification/purescala/invalid/PropositionalLogic.scala create mode 100644 src/test/resources/regression/verification/purescala/invalid/README create mode 100644 src/test/resources/regression/verification/purescala/invalid/RedBlackTree.scala create mode 100644 src/test/scala/leon/test/PureScalaPrograms.scala delete mode 100644 src/test/scala/leon/test/ValidPrograms.scala diff --git a/src/main/scala/leon/Reporter.scala b/src/main/scala/leon/Reporter.scala index 814c4352a..81ed31be8 100644 --- a/src/main/scala/leon/Reporter.scala +++ b/src/main/scala/leon/Reporter.scala @@ -5,20 +5,31 @@ import purescala.Trees.Expr import purescala.PrettyPrinter abstract class Reporter { - def error(msg: Any) : Unit - def warning(msg: Any) : Unit - def info(msg: Any) : Unit - def fatalError(msg: Any) : Nothing + def infoFunction(msg: Any) : Unit + def warningFunction(msg: Any) : Unit + def errorFunction(msg: Any) : Unit + def fatalErrorFunction(msg: Any) : Nothing - def error(definition: Definition, msg: Any) : Unit = error(msg) - def warning(definition: Definition, msg: Any) : Unit = warning(msg) - def info(definition: Definition, msg: Any) : Unit = info(msg) - def fatalError(definition: Definition, msg: Any) : Nothing = fatalError(msg) + // This part of the implementation is non-negociable. + private var _errorCount : Int = 0 + private var _warningCount : Int = 0 - def error(expr: Expr, msg: Any) : Unit = error(msg) - def warning(expr: Expr, msg: Any) : Unit = warning(msg) - def info(expr: Expr, msg: Any) : Unit = info(msg) - def fatalError(expr: Expr, msg: Any) : Nothing = fatalError(msg) + final def errorCount : Int = _errorCount + final def warningCount : Int = _warningCount + + final def info(msg: Any) = infoFunction(msg) + final def warning(msg: Any) = { + _warningCount += 1 + warningFunction(msg) + } + final def error(msg: Any) = { + _errorCount += 1 + errorFunction(msg) + } + final def fatalError(msg: Any) = { + _errorCount += 1 + fatalErrorFunction(msg) + } } class DefaultReporter extends Reporter { @@ -39,26 +50,18 @@ class DefaultReporter extends Reporter { msg.trim.replaceAll("\n", "\n" + (" " * (pfx.size))) } - def error(msg: Any) = output(reline(errorPfx, msg.toString)) - def warning(msg: Any) = output(reline(warningPfx, msg.toString)) - def info(msg: Any) = output(reline(infoPfx, msg.toString)) - def fatalError(msg: Any) = { output(reline(fatalPfx, msg.toString)); sys.exit(0) } - override def error(definition: Definition, msg: Any) = output(reline(errorPfx, PrettyPrinter(definition) + "\n" + msg.toString)) - override def warning(definition: Definition, msg: Any) = output(reline(warningPfx, PrettyPrinter(definition) + "\n" + msg.toString)) - override def info(definition: Definition, msg: Any) = output(reline(infoPfx, PrettyPrinter(definition) + "\n" + msg.toString)) - override def fatalError(definition: Definition, msg: Any) = { output(reline(fatalPfx, PrettyPrinter(definition) + "\n" + msg.toString)); sys.exit(0) } - override def error(expr: Expr, msg: Any) = output(reline(errorPfx, PrettyPrinter(expr) + "\n" + msg.toString)) - override def warning(expr: Expr, msg: Any) = output(reline(warningPfx, PrettyPrinter(expr) + "\n" + msg.toString)) - override def info(expr: Expr, msg: Any) = output(reline(infoPfx, PrettyPrinter(expr) + "\n" + msg.toString)) - override def fatalError(expr: Expr, msg: Any) = { output(reline(fatalPfx, PrettyPrinter(expr) + "\n" + msg.toString)); sys.exit(0) } + def errorFunction(msg: Any) = output(reline(errorPfx, msg.toString)) + def warningFunction(msg: Any) = output(reline(warningPfx, msg.toString)) + def infoFunction(msg: Any) = output(reline(infoPfx, msg.toString)) + def fatalErrorFunction(msg: Any) = { output(reline(fatalPfx, msg.toString)); sys.exit(0) } } class QuietReporter extends DefaultReporter { - override def warning(msg : Any) = {} - override def info(msg : Any) = {} + override def warningFunction(msg : Any) = {} + override def infoFunction(msg : Any) = {} } class SilentReporter extends QuietReporter { - override def error(msg : Any) = {} - override def fatalError(msg : Any) = throw new Exception("Fatal error: " + msg.toString) + override def errorFunction(msg : Any) = {} + override def fatalErrorFunction(msg : Any) = throw new Exception("Fatal error: " + msg.toString) } diff --git a/src/test/resources/regression/verification/purescala/invalid/InsertionSort.scala b/src/test/resources/regression/verification/purescala/invalid/InsertionSort.scala new file mode 100644 index 000000000..fd9d8d7b6 --- /dev/null +++ b/src/test/resources/regression/verification/purescala/invalid/InsertionSort.scala @@ -0,0 +1,42 @@ +import scala.collection.immutable.Set +import leon.Annotations._ +import leon.Utils._ + +object InsertionSort { + sealed abstract class List + case class Cons(head:Int,tail:List) extends List + case class Nil() extends List + + sealed abstract class OptInt + case class Some(value: Int) extends OptInt + case class None() extends OptInt + + def size(l : List) : Int = (l match { + case Nil() => 0 + case Cons(_, xs) => 1 + size(xs) + }) ensuring(_ >= 0) + + def contents(l: List): Set[Int] = l match { + case Nil() => Set.empty + case Cons(x,xs) => contents(xs) ++ Set(x) + } + + def isSorted(l: List): Boolean = l match { + case Nil() => true + case Cons(x, Nil()) => true + case Cons(x, Cons(y, ys)) => x <= y && isSorted(Cons(y, ys)) + } + + /* Inserting element 'e' into a sorted list 'l' produces a sorted list with + * the expected content and size */ + def buggySortedIns(e: Int, l: List): List = { + // require(isSorted(l)) + l match { + case Nil() => Cons(e,Nil()) + case Cons(x,xs) => if (x <= e) Cons(x,buggySortedIns(e, xs)) else Cons(e, l) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) + && isSorted(res) + && size(res) == size(l) + 1 + ) +} diff --git a/src/test/resources/regression/verification/purescala/invalid/ListOperations.scala b/src/test/resources/regression/verification/purescala/invalid/ListOperations.scala new file mode 100644 index 000000000..a4fc4f8dc --- /dev/null +++ b/src/test/resources/regression/verification/purescala/invalid/ListOperations.scala @@ -0,0 +1,107 @@ +import scala.collection.immutable.Set +import leon.Annotations._ +import leon.Utils._ + +object ListOperations { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + sealed abstract class IntPairList + case class IPCons(head: IntPair, tail: IntPairList) extends IntPairList + case class IPNil() extends IntPairList + + sealed abstract class IntPair + case class IP(fst: Int, snd: Int) extends IntPair + + def size(l: List) : Int = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def iplSize(l: IntPairList) : Int = (l match { + case IPNil() => 0 + case IPCons(_, xs) => 1 + iplSize(xs) + }) ensuring(_ >= 0) + + def zip(l1: List, l2: List) : IntPairList = { + // try to comment this and see how pattern-matching becomes + // non-exhaustive and post-condition fails + require(size(l1) == size(l2)) + + l1 match { + case Nil() => IPNil() + case Cons(x, xs) => l2 match { + case Cons(y, ys) => IPCons(IP(x, y), zip(xs, ys)) + } + } + } ensuring(iplSize(_) == size(l1)) + + def sizeTailRec(l: List) : Int = sizeTailRecAcc(l, 0) + def sizeTailRecAcc(l: List, acc: Int) : Int = { + require(acc >= 0) + l match { + case Nil() => acc + case Cons(_, xs) => sizeTailRecAcc(xs, acc+1) + } + } ensuring(res => res == size(l) + acc) + + def sizesAreEquiv(l: List) : Boolean = { + size(l) == sizeTailRec(l) + } holds + + def content(l: List) : Set[Int] = l match { + case Nil() => Set.empty[Int] + case Cons(x, xs) => Set(x) ++ content(xs) + } + + def sizeAndContent(l: List) : Boolean = { + size(l) == 0 || content(l) != Set.empty[Int] + } holds + + def drunk(l : List) : List = (l match { + case Nil() => Nil() + case Cons(x,l1) => Cons(x,Cons(x,drunk(l1))) + }) ensuring (size(_) == 2 * size(l)) + + def reverse(l: List) : List = reverse0(l, Nil()) ensuring(content(_) == content(l)) + def reverse0(l1: List, l2: List) : List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => reverse0(xs, Cons(x, l2)) + }) ensuring(content(_) == content(l1) ++ content(l2)) + + def append(l1 : List, l2 : List) : List = (l1 match { + case Nil() => l2 + case Cons(x,xs) => Cons(x, append(xs, l2)) + }) ensuring(content(_) == content(l1) ++ content(l2)) + + @induct + def nilAppend(l : List) : Boolean = (append(l, Nil()) == l) holds + + @induct + def appendAssoc(xs : List, ys : List, zs : List) : Boolean = + (append(append(xs, ys), zs) == append(xs, append(ys, zs))) holds + + def revAuxBroken(l1 : List, e : Int, l2 : List) : Boolean = { + (append(reverse(l1), Cons(e,l2)) == reverse0(l1, l2)) + } holds + + @induct + def sizeAppend(l1 : List, l2 : List) : Boolean = + (size(append(l1, l2)) == size(l1) + size(l2)) holds + + @induct + def concat(l1: List, l2: List) : List = + concat0(l1, l2, Nil()) ensuring(content(_) == content(l1) ++ content(l2)) + + @induct + def concat0(l1: List, l2: List, l3: List) : List = (l1 match { + case Nil() => l2 match { + case Nil() => reverse(l3) + case Cons(y, ys) => { + concat0(Nil(), ys, Cons(y, l3)) + } + } + case Cons(x, xs) => concat0(xs, l2, Cons(x, l3)) + }) ensuring(content(_) == content(l1) ++ content(l2) ++ content(l3)) +} diff --git a/src/test/resources/regression/verification/purescala/invalid/PropositionalLogic.scala b/src/test/resources/regression/verification/purescala/invalid/PropositionalLogic.scala new file mode 100644 index 000000000..a35c3ef9b --- /dev/null +++ b/src/test/resources/regression/verification/purescala/invalid/PropositionalLogic.scala @@ -0,0 +1,86 @@ +import scala.collection.immutable.Set +import leon.Utils._ +import leon.Annotations._ + +object PropositionalLogic { + + sealed abstract class Formula + case class And(lhs: Formula, rhs: Formula) extends Formula + case class Or(lhs: Formula, rhs: Formula) extends Formula + case class Implies(lhs: Formula, rhs: Formula) extends Formula + case class Not(f: Formula) extends Formula + case class Literal(id: Int) extends Formula + + def simplify(f: Formula): Formula = (f match { + case And(lhs, rhs) => And(simplify(lhs), simplify(rhs)) + case Or(lhs, rhs) => Or(simplify(lhs), simplify(rhs)) + case Implies(lhs, rhs) => Or(Not(simplify(lhs)), simplify(rhs)) + case Not(f) => Not(simplify(f)) + case Literal(_) => f + }) ensuring(isSimplified(_)) + + def isSimplified(f: Formula): Boolean = f match { + case And(lhs, rhs) => isSimplified(lhs) && isSimplified(rhs) + case Or(lhs, rhs) => isSimplified(lhs) && isSimplified(rhs) + case Implies(_,_) => false + case Not(f) => isSimplified(f) + case Literal(_) => true + } + + def nnf(formula: Formula): Formula = (formula match { + case And(lhs, rhs) => And(nnf(lhs), nnf(rhs)) + case Or(lhs, rhs) => Or(nnf(lhs), nnf(rhs)) + case Implies(lhs, rhs) => Implies(nnf(lhs), nnf(rhs)) + case Not(And(lhs, rhs)) => Or(nnf(Not(lhs)), nnf(Not(rhs))) + case Not(Or(lhs, rhs)) => And(nnf(Not(lhs)), nnf(Not(rhs))) + case Not(Implies(lhs, rhs)) => And(nnf(lhs), nnf(Not(rhs))) + case Not(Not(f)) => nnf(f) + case Not(Literal(_)) => formula + case Literal(_) => formula + }) ensuring(isNNF(_)) + + def isNNF(f: Formula): Boolean = f match { + case And(lhs, rhs) => isNNF(lhs) && isNNF(rhs) + case Or(lhs, rhs) => isNNF(lhs) && isNNF(rhs) + case Implies(lhs, rhs) => isNNF(lhs) && isNNF(rhs) + case Not(Literal(_)) => true + case Not(_) => false + case Literal(_) => true + } + + def vars(f: Formula): Set[Int] = { + require(isNNF(f)) + f match { + case And(lhs, rhs) => vars(lhs) ++ vars(rhs) + case Or(lhs, rhs) => vars(lhs) ++ vars(rhs) + case Implies(lhs, rhs) => vars(lhs) ++ vars(rhs) + case Not(Literal(i)) => Set[Int](i) + case Literal(i) => Set[Int](i) + } + } + + def fv(f : Formula) = { vars(nnf(f)) } + + // @induct + // def wrongCommutative(f: Formula) : Boolean = { + // nnf(simplify(f)) == simplify(nnf(f)) + // } holds + + @induct + def simplifyBreaksNNF(f: Formula) : Boolean = { + require(isNNF(f)) + isNNF(simplify(f)) + } holds + + @induct + def nnfIsStable(f: Formula) : Boolean = { + require(isNNF(f)) + nnf(f) == f + } holds + + @induct + def simplifyIsStable(f: Formula) : Boolean = { + require(isSimplified(f)) + simplify(f) == f + } holds +} diff --git a/src/test/resources/regression/verification/purescala/invalid/README b/src/test/resources/regression/verification/purescala/invalid/README new file mode 100644 index 000000000..3b276ff54 --- /dev/null +++ b/src/test/resources/regression/verification/purescala/invalid/README @@ -0,0 +1,2 @@ +This particular directory contains PureScala programs that have *at least* one +failing verification condition. diff --git a/src/test/resources/regression/verification/purescala/invalid/RedBlackTree.scala b/src/test/resources/regression/verification/purescala/invalid/RedBlackTree.scala new file mode 100644 index 000000000..3baf9d015 --- /dev/null +++ b/src/test/resources/regression/verification/purescala/invalid/RedBlackTree.scala @@ -0,0 +1,102 @@ +import scala.collection.immutable.Set +import leon.Annotations._ +import leon.Utils._ + +object RedBlackTree { + sealed abstract class Color + case class Red() extends Color + case class Black() extends Color + + sealed abstract class Tree + case class Empty() extends Tree + case class Node(color: Color, left: Tree, value: Int, right: Tree) extends Tree + + sealed abstract class OptionInt + case class Some(v : Int) extends OptionInt + case class None() extends OptionInt + + def content(t: Tree) : Set[Int] = t match { + case Empty() => Set.empty + case Node(_, l, v, r) => content(l) ++ Set(v) ++ content(r) + } + + def size(t: Tree) : Int = (t match { + case Empty() => 0 + case Node(_, l, v, r) => size(l) + 1 + size(r) + }) ensuring(_ >= 0) + + def isBlack(t: Tree) : Boolean = t match { + case Empty() => true + case Node(Black(),_,_,_) => true + case _ => false + } + + def redNodesHaveBlackChildren(t: Tree) : Boolean = t match { + case Empty() => true + case Node(Black(), l, _, r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + case Node(Red(), l, _, r) => isBlack(l) && isBlack(r) && redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + } + + def redDescHaveBlackChildren(t: Tree) : Boolean = t match { + case Empty() => true + case Node(_,l,_,r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + } + + def blackBalanced(t : Tree) : Boolean = t match { + case Node(_,l,_,r) => blackBalanced(l) && blackBalanced(r) && blackHeight(l) == blackHeight(r) + case Empty() => true + } + + def blackHeight(t : Tree) : Int = t match { + case Empty() => 1 + case Node(Black(), l, _, _) => blackHeight(l) + 1 + case Node(Red(), l, _, _) => blackHeight(l) + } + + def ins(x: Int, t: Tree): Tree = { + require(redNodesHaveBlackChildren(t) && blackBalanced(t)) + t match { + case Empty() => Node(Red(),Empty(),x,Empty()) + case Node(c,a,y,b) => + if (x < y) balance(c, ins(x, a), y, b) + else if (x == y) Node(c,a,y,b) + else balance(c,a,y,ins(x, b)) + } + } ensuring (res => content(res) == content(t) ++ Set(x) + && size(t) <= size(res) && size(res) <= size(t) + 1 + && redDescHaveBlackChildren(res) + && blackBalanced(res)) + + def balance(c: Color, a: Tree, x: Int, b: Tree): Tree = { + Node(c,a,x,b) match { + case Node(Black(),Node(Red(),Node(Red(),a,xV,b),yV,c),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),Node(Red(),a,xV,Node(Red(),b,yV,c)),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),Node(Red(),b,yV,c),zV,d)) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),b,yV,Node(Red(),c,zV,d))) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(c,a,xV,b) => Node(c,a,xV,b) + } + } ensuring (res => content(res) == content(Node(c,a,x,b)))// && redDescHaveBlackChildren(res)) + + def buggyAdd(x: Int, t: Tree): Tree = { + require(redNodesHaveBlackChildren(t)) + ins(x, t) + } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res)) + + def buggyBalance(c: Color, a: Tree, x: Int, b: Tree): Tree = { + Node(c,a,x,b) match { + case Node(Black(),Node(Red(),Node(Red(),a,xV,b),yV,c),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),Node(Red(),a,xV,Node(Red(),b,yV,c)),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),Node(Red(),b,yV,c),zV,d)) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),b,yV,Node(Red(),c,zV,d))) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + // case Node(c,a,xV,b) => Node(c,a,xV,b) + } + } ensuring (res => content(res) == content(Node(c,a,x,b))) +} diff --git a/src/test/resources/regression/verification/purescala/valid/AssociativeList.scala b/src/test/resources/regression/verification/purescala/valid/AssociativeList.scala index f5a2fc041..0356d8ec0 100644 --- a/src/test/resources/regression/verification/purescala/valid/AssociativeList.scala +++ b/src/test/resources/regression/verification/purescala/valid/AssociativeList.scala @@ -29,9 +29,9 @@ object AssociativeList { case Cons(KeyValuePair(k, v), xs) => find(xs, k) == None() && noDuplicates(xs) } - def update(l1: List, l2: List): List = (l2 match { + def updateAll(l1: List, l2: List): List = (l2 match { case Nil() => l1 - case Cons(x, xs) => update(updateElem(l1, x), xs) + case Cons(x, xs) => updateAll(updateElem(l1, x), xs) }) ensuring(domain(_) == domain(l1) ++ domain(l2)) def updateElem(l: List, e: KeyValuePairAbs): List = (l match { diff --git a/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala b/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala index d2ae67b3b..a4fffee35 100644 --- a/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala +++ b/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala @@ -48,19 +48,6 @@ object InsertionSort { && size(res) == size(l) + 1 ) - /* Inserting element 'e' into a sorted list 'l' produces a sorted list with - * the expected content and size */ - def buggySortedIns(e: Int, l: List): List = { - // require(isSorted(l)) - l match { - case Nil() => Cons(e,Nil()) - case Cons(x,xs) => if (x <= e) Cons(x,buggySortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) - && isSorted(res) - && size(res) == size(l) + 1 - ) - /* Insertion sort yields a sorted list of same size and content as the input * list */ def sort(l: List): List = (l match { diff --git a/src/test/resources/regression/verification/purescala/valid/ListOperations.scala b/src/test/resources/regression/verification/purescala/valid/ListOperations.scala index a4fc4f8dc..27541d9bd 100644 --- a/src/test/resources/regression/verification/purescala/valid/ListOperations.scala +++ b/src/test/resources/regression/verification/purescala/valid/ListOperations.scala @@ -82,10 +82,6 @@ object ListOperations { def appendAssoc(xs : List, ys : List, zs : List) : Boolean = (append(append(xs, ys), zs) == append(xs, append(ys, zs))) holds - def revAuxBroken(l1 : List, e : Int, l2 : List) : Boolean = { - (append(reverse(l1), Cons(e,l2)) == reverse0(l1, l2)) - } holds - @induct def sizeAppend(l1 : List, l2 : List) : Boolean = (size(append(l1, l2)) == size(l1) + size(l2)) holds diff --git a/src/test/resources/regression/verification/purescala/valid/PropositionalLogic.scala b/src/test/resources/regression/verification/purescala/valid/PropositionalLogic.scala index a35c3ef9b..5dd7d5057 100644 --- a/src/test/resources/regression/verification/purescala/valid/PropositionalLogic.scala +++ b/src/test/resources/regression/verification/purescala/valid/PropositionalLogic.scala @@ -61,17 +61,6 @@ object PropositionalLogic { def fv(f : Formula) = { vars(nnf(f)) } - // @induct - // def wrongCommutative(f: Formula) : Boolean = { - // nnf(simplify(f)) == simplify(nnf(f)) - // } holds - - @induct - def simplifyBreaksNNF(f: Formula) : Boolean = { - require(isNNF(f)) - isNNF(simplify(f)) - } holds - @induct def nnfIsStable(f: Formula) : Boolean = { require(isNNF(f)) diff --git a/src/test/resources/regression/verification/purescala/valid/RedBlackTree.scala b/src/test/resources/regression/verification/purescala/valid/RedBlackTree.scala index bc2de6ba9..65d873ad4 100644 --- a/src/test/resources/regression/verification/purescala/valid/RedBlackTree.scala +++ b/src/test/resources/regression/verification/purescala/valid/RedBlackTree.scala @@ -82,11 +82,6 @@ object RedBlackTree { makeBlack(ins(x, t)) } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res) && blackBalanced(res)) - def buggyAdd(x: Int, t: Tree): Tree = { - require(redNodesHaveBlackChildren(t)) - ins(x, t) - } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res)) - def balance(c: Color, a: Tree, x: Int, b: Tree): Tree = { Node(c,a,x,b) match { case Node(Black(),Node(Red(),Node(Red(),a,xV,b),yV,c),zV,d) => @@ -100,18 +95,4 @@ object RedBlackTree { case Node(c,a,xV,b) => Node(c,a,xV,b) } } ensuring (res => content(res) == content(Node(c,a,x,b)))// && redDescHaveBlackChildren(res)) - - def buggyBalance(c: Color, a: Tree, x: Int, b: Tree): Tree = { - Node(c,a,x,b) match { - case Node(Black(),Node(Red(),Node(Red(),a,xV,b),yV,c),zV,d) => - Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) - case Node(Black(),Node(Red(),a,xV,Node(Red(),b,yV,c)),zV,d) => - Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) - case Node(Black(),a,xV,Node(Red(),Node(Red(),b,yV,c),zV,d)) => - Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) - case Node(Black(),a,xV,Node(Red(),b,yV,Node(Red(),c,zV,d))) => - Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) - // case Node(c,a,xV,b) => Node(c,a,xV,b) - } - } ensuring (res => content(res) == content(Node(c,a,x,b)))// && redDescHaveBlackChildren(res)) } diff --git a/src/test/scala/leon/test/PureScalaPrograms.scala b/src/test/scala/leon/test/PureScalaPrograms.scala new file mode 100644 index 000000000..92fbb68eb --- /dev/null +++ b/src/test/scala/leon/test/PureScalaPrograms.scala @@ -0,0 +1,84 @@ +package leon +package test + +import leon.verification.{AnalysisPhase,VerificationReport} + +import org.scalatest.FunSuite + +import java.io.File + +class PureScalaPrograms extends FunSuite { + private var counter : Int = 0 + private def nextInt() : Int = { + counter += 1 + counter + } + private case class Output(report : VerificationReport, reporter : Reporter) + + private def mkPipeline : Pipeline[List[String],VerificationReport] = + leon.plugin.ExtractionPhase andThen leon.verification.AnalysisPhase + + private def mkTest(file : File)(block: Output=>Unit) = { + val fullName = file.getPath() + val start = fullName.indexOf("regression") + val displayName = if(start != -1) { + fullName.substring(start, fullName.length) + } else { + fullName + } + + test("PureScala program %3d: [%s]".format(nextInt(), displayName)) { + assert(file.exists && file.isFile && file.canRead, + "Benchmark [%s] is not a readable file".format(displayName)) + + val ctx = LeonContext( + settings = Settings( + synthesis = false, + xlang = false, + verify = true + ), + files = List(file), + reporter = new SilentReporter + ) + + val pipeline = mkPipeline + + val report = pipeline.run(ctx)("--timeout=2" :: file.getPath :: Nil) + + block(Output(report, ctx.reporter)) + } + } + + private def forEachFileIn(dirName : String)(block : Output=>Unit) { + import scala.collection.JavaConversions._ + + val dir = this.getClass.getClassLoader.getResource(dirName) + + if(dir == null || dir.getProtocol != "file") { + assert(false, "Tests have to be run from within `sbt`, for otherwise " + + "the test files will be harder to access (and we dislike that).") + } + + for(f <- (new File(dir.toURI())).listFiles() if f.getPath().endsWith(".scala")) { + mkTest(f)(block) + } + } + + forEachFileIn("regression/verification/purescala/valid") { output => + val Output(report, reporter) = output + assert(report.totalConditions === report.totalValid, + "All verification conditions should be valid.") + assert(reporter.errorCount === 0) + assert(reporter.warningCount === 0) + } + + forEachFileIn("regression/verification/purescala/invalid") { output => + val Output(report, reporter) = output + assert(report.totalInvalid > 0, + "There should be at least one invalid verification condition.") + assert(report.totalUnknown === 0, + "There should not be unknown verification conditions.") + assert(reporter.errorCount >= report.totalInvalid) + assert(reporter.warningCount === 0) + } +} diff --git a/src/test/scala/leon/test/ValidPrograms.scala b/src/test/scala/leon/test/ValidPrograms.scala deleted file mode 100644 index 37df60f22..000000000 --- a/src/test/scala/leon/test/ValidPrograms.scala +++ /dev/null @@ -1,48 +0,0 @@ -package leon -package test - -import org.scalatest.FunSuite - -import java.io.File - -class ValidPrograms extends FunSuite { - def runBasicLeonOnFile(filename : String) : Unit = { - val file = new File(filename) - - assert(file.exists && file.isFile && file.canRead, - "Benchmark [%s] is not a readable file".format(filename)) - - val ctx = LeonContext( - settings = Settings( - synthesis = false, - xlang = false, - verify = true - ), - files = List(file), - reporter = new SilentReporter - ) - - val pipeline = Main.computePipeline(ctx.settings) - pipeline.run(ctx)("--timeout=2" :: file.getPath :: Nil) - } - - def mkTest(filename : String) = { - test("Valid PureScala program: [%s]".format(filename)) { - runBasicLeonOnFile(filename) - assert(true) - } - } - - test("List files") { - import scala.collection.JavaConversions._ - - val ress = this.getClass.getClassLoader.getResources("/regression/verification/purescala/valid/") - - for(res <- ress) { - println(res) - } - } - - // Sorry for leaving this there... - // mkTest("/home/psuter/Test.scala") -} -- GitLab