diff --git a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala index 686a968c450e2e2b0899634e571a416da0696f46..f60808312fab6302522fa412564f28a0f0f0f502 100644 --- a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala +++ b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala @@ -61,6 +61,51 @@ class SynthesisSuite extends FunSuite { } } + case class Apply(desc: String, andThen: List[Apply] = Nil) + + def synthesizeWith(sctx: SynthesisContext, p: Problem, ss: Apply): Solution = { + val (normRules, otherRules) = sctx.options.rules.partition(_.isInstanceOf[NormalizingRule]) + val normApplications = normRules.flatMap(_.instantiateOn(sctx, p)) + + val apps = if (!normApplications.isEmpty) { + normApplications + } else { + otherRules.flatMap { r => r.instantiateOn(sctx, p) } + } + + apps.find(_.toString == ss.desc) match { + case Some(app) => + app.apply(sctx) match { + case RuleSuccess(sol) => + assert(ss.andThen.isEmpty) + sol + + case RuleDecomposed(sub) => + app.onSuccess((sub zip ss.andThen) map { case (p, ss) => synthesizeWith(sctx, p, ss) }).get + + case _ => + throw new AssertionError("Failed to apply "+app+" on "+p) + } + + case None => + throw new AssertionError("Failed to find "+ss.desc+". Available: "+apps.mkString(", ")) + } + } + + def synthesize(title: String)(program: String)(strategies: PartialFunction[String, Apply]) { + forProgram(title)(program) { + case (sctx, fd, p) => + strategies.lift.apply(fd.id.toString) match { + case Some(ss) => + synthesizeWith(sctx, p, ss) + + case None => + assert(false, "Function "+fd.id.toString+" not found") + } + } + } + + def assertAllAlternativesSucceed(sctx: SynthesisContext, rr: Traversable[RuleInstantiation]) { assert(!rr.isEmpty) @@ -156,4 +201,70 @@ object Injection { assert(false, "Woot?") } } + + + synthesize("Lists")(""" +import leon.Utils._ + +object SortedList { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + def size(l: List) : Int = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[Int] = l match { + case Nil() => Set.empty[Int] + case Cons(i, t) => Set(i) ++ content(t) + } + + 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)) + } + + def concat(in1: List, in2: List) = choose { + (out : List) => + content(out) == content(in1) ++ content(in2) + } + + def insert(in1: List, v: Int) = choose { + (out : List) => + content(out) == content(in1) ++ Set(v) + } + + def insertSorted(in1: List, v: Int) = choose { + (out : List) => + isSorted(in1) && content(out) == content(in1) ++ Set(v) && isSorted(out) + } +} + """) { + case "concat" => + Apply("ADT Induction on 'in1'", List( + Apply("CEGIS"), + Apply("CEGIS") + )) + + case "insert" => + Apply("ADT Induction on 'in1'", List( + Apply("CEGIS"), + Apply("CEGIS") + )) + + case "insertSorted" => + Apply("Assert isSorted(in1)", List( + Apply("ADT Induction on 'in1'", List( + Apply("Ineq. Split on 'head16' and 'v4'", List( + Apply("CEGIS"), + Apply("CEGIS"), + Apply("CEGIS") + )), + Apply("CEGIS") + )) + )) + } }