From 0b70ccac59d20e591b6ae763e53cfecbec8509e0 Mon Sep 17 00:00:00 2001
From: Manos Koukoutos <emmanouil.koukoutos@epfl.ch>
Date: Thu, 5 Mar 2015 20:28:05 +0100
Subject: [PATCH] Make CodeGenTests 7 times faster

---
 .../leon/test/codegen/CodeGenTests.scala      | 728 ++++++++----------
 1 file changed, 326 insertions(+), 402 deletions(-)

diff --git a/src/test/scala/leon/test/codegen/CodeGenTests.scala b/src/test/scala/leon/test/codegen/CodeGenTests.scala
index 4a0385307..607a6cd64 100644
--- a/src/test/scala/leon/test/codegen/CodeGenTests.scala
+++ b/src/test/scala/leon/test/codegen/CodeGenTests.scala
@@ -12,434 +12,358 @@ import EvaluationResults._
 
 import java.io._
 
-case class TestCase(
-  name : String, 
-  content : String,  
-  expected : Expr,
-  args : Seq[Expr] = Seq(),
-  functionToTest : String = "test"
-)
-
+/*
+ * To add a new test:
+ * - Add your test, preferably in a new module, in the test variable
+ * - Make sure the function under test is named "test" and has no parameters
+ * - Add the test name and expected result in the result variable.
+ *   Make sure the relative order of the tests matches that of code
+ */
 class CodeGenTests extends test.LeonTestSuite {
-  
-  val catchAll = true
-  
-  val pipeline = 
-    utils.TemporaryInputPhase andThen
-    frontends.scalac.ExtractionPhase andThen
-    utils.PreprocessingPhase
-
-  def compileTestFun(p : Program, toTest : String, ctx : LeonContext, requireMonitor : Boolean, doInstrument : Boolean) : ( Seq[Expr] => EvaluationResults.Result) = {
-    // We want to produce code that checks contracts
-    val evaluator = new CodeGenEvaluator(ctx, p, CodeGenParams(
-      maxFunctionInvocations = if (requireMonitor) 1000 else -1, // Monitor calls and abort execution if more than X calls
-      checkContracts = true,      // Generate calls that checks pre/postconditions
-      doInstrument = doInstrument // Instrument reads to case classes (mainly for vanuatoo)
-    ))
-   
-
-    val testFun =  p.definedFunctions.find(_.id.name == toTest).getOrElse {
-      ctx.reporter.fatalError("Test function not defined!")
-    }
-    val params = testFun.params map { _.id }
-    val body = testFun.body.get
-    // Will apply test a number of times with the help of compileRec
-    evaluator.compile(body, params).getOrElse {
-      ctx.reporter.fatalError("Failed to compile test function!")
-    }
-       
-  }
  
+  case class TestCase(
+    name : String,
+    expectedResult: Expr,
+    testFunction: FunDef
+  )
+
+  def runTests() = {
+    val catchAll = true
   
-  private def testCodeGen(prog : TestCase, requireMonitor : Boolean, doInstrument : Boolean) { test(prog.name) {
-    import prog._
+    val pipeline = 
+      utils.TemporaryInputPhase andThen
+      frontends.scalac.ExtractionPhase andThen
+      utils.PreprocessingPhase
+    
     val ctx = createLeonContext() 
+      
+    val ast = pipeline.run(ctx)( (code, List()) )
     
-    val ast = pipeline.run(ctx)( (content, List()) )
+    val testFuns = ast.definedFunctions.filter {
+      _.id.name == "test"
+    }.sortWith{ ( f1, f2 ) => f1.getPos < f2.getPos }
     
-    val compiled = compileTestFun(ast, functionToTest, ctx, requireMonitor, doInstrument)
-    try { compiled(args) match {
-      case Successful(res) if res == expected =>
-        // Success
-      case RuntimeError(_) | EvaluatorError(_) if expected.isInstanceOf[Error] => 
-        // Success
-      case Successful(res) => 
-        ctx.reporter.fatalError(s"""
-          Program $name produced wrong output.
-          Output was ${res.toString}
-          Expected was ${expected.toString}       
-        """.stripMargin)
-      case RuntimeError(mes) => 
-        ctx.reporter.fatalError(s"Program $name threw runtime error with message $mes")
-      case EvaluatorError(res) =>  
-        ctx.reporter.fatalError(s"Evaluator failed for program $name with message $res")
-    }} catch {
-      // Currently, this is what we would like to catch and still succeed, but there might be more
-      case _ : LeonFatalError | _ : StackOverflowError if expected.isInstanceOf[Error] =>
-        // Success
-      case th : Throwable => 
-        if (catchAll) {
-          // This is to be able to continue testing after an error
-          ctx.reporter.fatalError(s"Program $name failed\n${th.printStackTrace()}")// with message ${th.getMessage()}")
-        } else { throw th }
+    val testCases = results zip testFuns map { 
+      case ( (name, result), fd ) =>
+        TestCase(name, result, fd)
     }
-  }}
-  
 
-  val programs = Seq(
-    
-    TestCase("simple", """
-      object simple {
-        abstract class Abs
-        case class Conc(x : Int) extends Abs
-        def test = { 
-          val c = Conc(1)
-          c.x
-          
-        }
-      }""",
-      IntLiteral(1)
-    ),
+    assert(results.size == testFuns.size)
     
-    TestCase("simpleBigInt", """
-      object simple {
-        abstract class Abs
-        case class Conc(x : BigInt) extends Abs
-        def test = { 
-          val c = Conc(1)
-          c.x
-          
-        }
-      }""",
-      InfiniteIntegerLiteral(1)
-    ),
-      
-    TestCase("eager", """
-      object eager {
-        abstract class Abs() {
-          val foo = 42
-        }
-        case class Conc(x : Int) extends Abs()
-        def foo = { 
-          val c = Conc(1)
-          c.foo + c.x
-        }
-        def test = foo
-      }""",
-      IntLiteral(43)
-    ),
-    
-    TestCase("this", """ 
-      object thiss {
-        
-        case class Bar() {
-          def boo = this
-          def toRet = 42
-        }
-        
-        def test = Bar().boo.toRet
+    for {
+      requireMonitor <- Seq(false, true)
+      doInstrument <- Seq(false,true)
+      evaluator = new CodeGenEvaluator(createLeonContext(), ast, CodeGenParams(
+        maxFunctionInvocations = if (requireMonitor) 1000 else -1, // Monitor calls and abort execution if more than X calls
+        checkContracts = true,      // Generate calls that checks pre/postconditions
+        doInstrument = doInstrument // Instrument reads to case classes (mainly for vanuatoo)
+      ))
+      TestCase(name, expected, testFunction) <- testCases
+      suffix = (if (requireMonitor) "M" else "") + ( if (doInstrument) "I" else "")
+    } test(name + suffix) { 
+      val body = testFunction.body.get
+      val params = testFunction.params map { _.id }
+      val compiled = evaluator.compile(body, params).getOrElse {
+        ctx.reporter.fatalError("Failed to compile test function!")
       }
-      """,
-      IntLiteral(42)
-    ),
-    
-   TestCase("oldStuff", """ 
-      object oldStuff {
-        def test = 1
-        case class Bar() {
-          def boo = 2
-        }
-      }""",
-      IntLiteral(1)
-    ),
-    
-    TestCase("methSimple", """
-      object methSimple {
-        
-        sealed abstract class Ab { 
-          def f2(x : Int) = x + 5
-        }
-        case class Con() extends Ab { }
-        
-        def test = Con().f2(5)
-      }""",
-      IntLiteral(10)
-    ),
+      try { compiled(Nil) match {
+        case Successful(res) if res == expected =>
+          // Success
+        case RuntimeError(_) | EvaluatorError(_) if expected.isInstanceOf[Error] => 
+          // Success
+        case Successful(res) => 
+          ctx.reporter.fatalError(s"""
+            Program $name produced wrong output.
+            Output was ${res.toString}
+            Expected was ${expected.toString}       
+          """.stripMargin)
+        case RuntimeError(mes) => 
+          ctx.reporter.fatalError(s"Program $name threw runtime error with message $mes")
+        case EvaluatorError(res) =>  
+          ctx.reporter.fatalError(s"Evaluator failed for program $name with message $res")
+      }} catch {
+        // Currently, this is what we would like to catch and still succeed, but there might be more
+        case _ : LeonFatalError | _ : StackOverflowError if expected.isInstanceOf[Error] =>
+          // Success
+        case th : Throwable => 
+          if (catchAll) {
+            // This is to be able to continue testing after an error
+            println(s"Program $name failed!")
+            th.printStackTrace()
+            ctx.reporter.fatalError(s"Program $name failed\n${th.printStackTrace()}")// with message ${th.getMessage()}")
+          } else { throw th }
+      }
+    }
+  }
 
-    TestCase("methMakeBigInt", """
-      object methSimple {
-        
-        val x: BigInt = 42
+  val results = Seq(
+    ("simple",            IntLiteral(1)),
+    ("simpleBigInt",      InfiniteIntegerLiteral(1)),
+    ("eager",             IntLiteral(43)),
+    ("this",              IntLiteral(42) ),
+    ("oldStuff",          IntLiteral(1)),
+    ("methSimple",        IntLiteral(10)),
+    ("methMakeBigInt",    InfiniteIntegerLiteral(42)),
+    ("methSimpleBigInt",  InfiniteIntegerLiteral(10)),
+    ("BigIntNoOverflow",  InfiniteIntegerLiteral(BigInt("4000000000"))),
+    ("BigIntOps",         InfiniteIntegerLiteral(BigInt(7))),
+    ("BigIntComp0",       BooleanLiteral(true)),
+    ("BigIntComp1",       InfiniteIntegerLiteral(BigInt(17))),
+    ("BigIntComp2",       InfiniteIntegerLiteral(BigInt(12))),
+    ("BigIntComp3",       InfiniteIntegerLiteral(BigInt(-7))),
+    ("methods",           IntLiteral(15)),
+    ("lazy",              IntLiteral(1 + 5 + 1 + 6 + 2) ),
+    ("modules",           IntLiteral(1 + 2 + 0) ),
+    ("lazyISLazy" ,       IntLiteral(42) ),
+    ("ListWithSize" ,     IntLiteral(3) ),
+    ("ListWithSumMono" ,  IntLiteral(1 + 2 + 3) ),
+    ("poly" ,             IntLiteral(42) ),
+    ("ListHead" ,         IntLiteral(1)),
+    ("ListWithSum" ,      IntLiteral(1 + 2 + 3) ), 
+    // This one loops!    
+    ("lazyLoops" ,        Error(Untyped, "Looping") ),
+    ("Lazier" ,           IntLiteral(1 + 2 + 3) ),
+    ("SetToList",         BooleanLiteral(true) )
+  )
 
-        def test = x
 
-      }""",
-      InfiniteIntegerLiteral(42)
-    ),
-    
-    TestCase("methSimpleBigInt", """
-      object methSimple {
-        
-        sealed abstract class Ab { 
-          def f2(x : BigInt): BigInt = x + 5
-        }
-        case class Con() extends Ab { }
-        
-        def test = Con().f2(5)
-      }""",
-      InfiniteIntegerLiteral(10)
-    ),
 
-    TestCase("BigIntNoOverflow", """
-      object methSimple {
-        
-        sealed abstract class Ab { 
-          def f2(x : BigInt): BigInt = x + BigInt(2000000000)
-        }
-        case class Con() extends Ab { }
-        
-        def test = Con().f2(2000000000)
-      }""",
-      InfiniteIntegerLiteral(BigInt("4000000000"))
-    ),
+  val code = """
     
-
-    TestCase("BigIntOps", """
-      object methSimple {
+    object simple {
+      abstract class Abs
+      case class Conc(x : Int) extends Abs
+      def test = { 
+        val c = Conc(1)
+        c.x
         
-        def f(x: BigInt): BigInt = ((x * 2) - 10)/2
+      }
+    }
+    object simple2 {
+      abstract class Abs
+      case class Conc(x : BigInt) extends Abs
+      def test = { 
+        val c = Conc(1)
+        c.x
         
-        def test = f(12)
-      }""",
-      InfiniteIntegerLiteral(BigInt(7))
-    ),
+      }
+    }
+    object eager {
+      abstract class Abs() {
+        val foo = 42
+      }
+      case class Conc(x : Int) extends Abs()
+      def foo = { 
+        val c = Conc(1)
+        c.foo + c.x
+      }
+      def test = foo
+    }
+    object thiss {
+      
+      case class Bar() {
+        def boo = this
+        def toRet = 42
+      }
+      
+      def test = Bar().boo.toRet
+    }
+    object oldStuff {
+      def test = 1
+      case class Bar() {
+        def boo = 2
+      }
+    }
+    object methSimple {
+      
+      sealed abstract class Ab { 
+        def f2(x : Int) = x + 5
+      }
+      case class Con() extends Ab { }
+      
+      def test = Con().f2(5)
+    }
+    object methSimple2 {
+      
+      val x: BigInt = 42
 
+      def test = x
 
-    TestCase("BigIntComp0", """
-      object methSimple {
-        
-        def f(x: BigInt): Boolean = x == BigInt(17)
-        
-        def test = f(17)
-      }""",
-      BooleanLiteral(true)
-    ),
-
-    TestCase("BigIntComp1", """
-      object methSimple {
-        
-        def f(x: BigInt): BigInt = if(x <= 0) -x else x
-        
-        def test = f(-17)
-      }""",
-      InfiniteIntegerLiteral(BigInt(17))
-    ),
-    TestCase("BigIntComp2", """
-      object methSimple {
-        
-        def f(x: BigInt): BigInt = if(x < 0) -x else x
-        
-        def test = f(-12)
-      }""",
-      InfiniteIntegerLiteral(BigInt(12))
-    ),
-    TestCase("BigIntComp3", """
-      object methSimple {
-        
-        def f(x: BigInt): BigInt = if(x >= 0) -x else x
-        
-        def test = f(-7)
-      }""",
-      InfiniteIntegerLiteral(BigInt(-7))
-    ),
-    
-    TestCase("methods", """
-      object methods {
-        def f1 = 4 
-        sealed abstract class Ab { 
-          def f2(x : Int) = Cs().f3(1,2) + f1 + x + 5
-        }
-        case class Con() extends Ab {}
-        case class Cs() {
-          def f3(x : Int, y : Int) = x + y
-        }
-        def test = Con().f2(3) 
-      }""",
-      IntLiteral(15)
-    ),
-    
-    
-    TestCase("lazy", """
-      object lazyFields {
-        def foo = 1
-        sealed abstract class Ab {
-          lazy val x : Int = this match {
-            case Conc(t) => t + 1
-            case Conc2(t) => t+2
-          }
-        }
-        case class Conc(t : Int) extends Ab { }
-        case class Conc2(t : Int) extends Ab { } 
-        def test = foo + Conc(5).x + Conc2(6).x
+    }
+    object methSimple3 {
+      
+      sealed abstract class Ab { 
+        def f2(x : BigInt): BigInt = x + 5
       }
-      """, 
-      IntLiteral(1 + 5 + 1 + 6 + 2)
-    ),
-    
-    TestCase("modules", """
-      object modules {
-        def foo = 1
-        val bar = 2 
-        lazy val baz = 0
-        def test = foo + bar + baz 
+      case class Con() extends Ab { }
+      
+      def test = Con().f2(5)
+    }
+    object methSimple4 {
+      
+      sealed abstract class Ab { 
+        def f2(x : BigInt): BigInt = x + BigInt(2000000000)
       }
-      """, 
-      IntLiteral(1 + 2 + 0)
-    ),
-    
-    TestCase("lazyISLazy" , """ 
-      object lazyISLazy {
-        abstract class Ab { lazy val x : Int = foo; def foo : Int = foo }
-        case class Conc() extends Ab { }
-        def test = { val willNotLoop = Conc(); 42 }
-      }""",
-      IntLiteral(42)
-    ),
-    
-    TestCase("ListWithSize" , """ 
-      object list {
-        abstract class List[T] {
-           val length : Int = this match {
-            case Nil() => 0
-            case Cons (_, xs ) => 1 + xs.length
-          }
-          
-        }
-        case class Cons[T](hd : T, tl : List[T]) extends List[T]
-        case class Nil[T]() extends List[T]      
-       
-        
-        val l = Cons(1, Cons(2, Cons(3, Nil())))
-        
-        def test = l.length + Nil().length
-      }""",
-      IntLiteral(3 )
-    ),
-    
-    TestCase("ListWithSumMono" , """ 
-      object ListWithSumMono {
-        abstract class List
-        case class Cons(hd : Int, tl : List) extends List
-        case class Nil() extends List     
-        
-        def sum (l : List) : Int = l match {
-          case Nil() => 0
-          case Cons(x, xs) => x + sum(xs)
+      case class Con() extends Ab { }
+      
+      def test = Con().f2(2000000000)
+    }
+    object methSimple5 {
+      
+      def f(x: BigInt): BigInt = ((x * 2) - 10)/2
+      
+      def test = f(12)
+    }
+    object methSimple6 {
+      
+      def f(x: BigInt): Boolean = x == BigInt(17)
+      
+      def test = f(17)
+    }
+    object methSimple7 {
+      
+      def f(x: BigInt): BigInt = if(x <= 0) -x else x
+      
+      def test = f(-17)
+    }
+    object methSimple8 {
+      
+      def f(x: BigInt): BigInt = if(x < 0) -x else x
+      
+      def test = f(-12)
+    }
+    object methSimple9 {
+      
+      def f(x: BigInt): BigInt = if(x >= 0) -x else x
+      
+      def test = f(-7)
+    }
+    object methods {
+      def f1 = 4 
+      sealed abstract class Ab { 
+        def f2(x : Int) = Cs().f3(1,2) + f1 + x + 5
+      }
+      case class Con() extends Ab {}
+      case class Cs() {
+        def f3(x : Int, y : Int) = x + y
+      }
+      def test = Con().f2(3) 
+    }
+    object lazyFields {
+      def foo = 1
+      sealed abstract class Ab {
+        lazy val x : Int = this match {
+          case Conc(t) => t + 1
+          case Conc2(t) => t+2
         }
-        
-        val l = Cons(1, Cons(2, Cons(3, Nil())))
-        
-        def test =  sum(l)
-      }""",
-      IntLiteral(1 + 2 + 3)
-    ),
-    
-    TestCase("poly" , """ 
-      object poly {
-        case class Poly[T](poly : T) 
-        def ex = Poly(42)
-        def test =  ex.poly
-      }""",
-      IntLiteral(42)
-    ),
-    
-    TestCase("ListHead" , """ 
-      object ListHead {
-        abstract class List[T]
-        case class Cons[T](hd : T, tl : List[T]) extends List[T]
-        case class Nil[T]() extends List[T]      
-               
-        def l = Cons(1, Cons(2, Cons(3, Nil())))
-        
-        def test =  l.hd
-      }""",
-      IntLiteral(1)
-    ),
-    TestCase("ListWithSum" , """ 
-      object ListWithSum {
-        abstract class List[T]
-        case class Cons[T](hd : T, tl : List[T]) extends List[T]
-        case class Nil[T]() extends List[T]      
-        
-        def sum (l : List[Int]) : Int = l match {
+      }
+      case class Conc(t : Int) extends Ab { }
+      case class Conc2(t : Int) extends Ab { } 
+      def test = foo + Conc(5).x + Conc2(6).x
+    }
+    object modules {
+      def foo = 1
+      val bar = 2 
+      lazy val baz = 0
+      def test = foo + bar + baz 
+    }
+    object lazyISLazy {
+      abstract class Ab { lazy val x : Int = foo; def foo : Int = foo }
+      case class Conc() extends Ab { }
+      def test = { val willNotLoop = Conc(); 42 }
+    }
+    object list {
+      abstract class List[T] {
+         val length : Int = this match {
           case Nil() => 0
-          case Cons(x, xs) => x + sum(xs)
+          case Cons (_, xs ) => 1 + xs.length
         }
         
-        val l = Cons(1, Cons(2, Cons(3, Nil())))
-        
-        def test =  sum(l)
-      }""",
-      IntLiteral(1 + 2 + 3)
-    ), 
-     
-    // This one loops!    
-    TestCase("lazyLoops" , """ 
-        object lazyLoops {
-          abstract class Ab { lazy val x : Int = foo; def foo : Int = foo }
-          case class Conc() extends Ab { }
-          def test = Conc().x
-        }""",
-        Error(Untyped, "Looping")
-    ),
-    
-    TestCase("Lazier" , """
+      }
+      case class Cons[T](hd : T, tl : List[T]) extends List[T]
+      case class Nil[T]() extends List[T]      
+       
+      
+      val l = Cons(1, Cons(2, Cons(3, Nil())))
+      
+      def test = l.length + Nil().length
+    }
+    object ListWithSumMono {
+      abstract class List
+      case class Cons(hd : Int, tl : List) extends List
+      case class Nil() extends List     
+      
+      def sum (l : List) : Int = l match {
+        case Nil() => 0
+        case Cons(x, xs) => x + sum(xs)
+      }
+      
+      val l = Cons(1, Cons(2, Cons(3, Nil())))
+      
+      def test =  sum(l)
+    }
+    object poly {
+      case class Poly[T](poly : T) 
+      def ex = Poly(42)
+      def test =  ex.poly
+    }
+    object ListHead {
+      abstract class List[T]
+      case class Cons[T](hd : T, tl : List[T]) extends List[T]
+      case class Nil[T]() extends List[T]      
+             
+      def l = Cons(1, Cons(2, Cons(3, Nil())))
+      
+      def test =  l.hd
+    }
+    object ListWithSum {
+      abstract class List[T]
+      case class Cons[T](hd : T, tl : List[T]) extends List[T]
+      case class Nil[T]() extends List[T]      
+      
+      def sum (l : List[Int]) : Int = l match {
+        case Nil() => 0
+        case Cons(x, xs) => x + sum(xs)
+      }
+      
+      val l = Cons(1, Cons(2, Cons(3, Nil())))
+      
+      def test =  sum(l)
+    }
+    object lazyLoops {
+      abstract class Ab { lazy val x : Int = foo; def foo : Int = foo }
+      case class Conc() extends Ab { }
+      def test = Conc().x
+    }
+    object Lazier {
       import leon.lang._
-      object Lazier {
-        abstract class List[T] { 
-          lazy val tail = this match {
-            case Nil() => error[List[T]]("Nil.tail")
-            case Cons(_, tl) => tl
-          }
+      abstract class List[T] { 
+        lazy val tail = this match {
+          case Nil() => error[List[T]]("Nil.tail")
+          case Cons(_, tl) => tl
         }
-        case class Cons[T](hd : T, tl : List[T]) extends List[T]
-        case class Nil[T]() extends List[T]      
-        
-        def sum (l : List[Int]) : Int = l match {
-          case Nil() => 0
-          case c : Cons[Int] => c.hd + sum(c.tail)
-        }
-        
-        val l = Cons(1, Cons(2, Cons(3, Nil())))
-        
-        def test =  sum(l)
-      }""",
-      IntLiteral(1 + 2 + 3)
-    ),
-    
-    TestCase("SetToList", """
+      }
+      case class Cons[T](hd : T, tl : List[T]) extends List[T]
+      case class Nil[T]() extends List[T]      
+      
+      def sum (l : List[Int]) : Int = l match {
+        case Nil() => 0
+        case c : Cons[Int] => c.hd + sum(c.tail)
+      }
+      
+      val l = Cons(1, Cons(2, Cons(3, Nil())))
+      
+      def test =  sum(l)
+    }
+    object SetToList {
       import leon.collection._
-      object SetToList {
-        def test = {
-          val s = Set(1, 2, 3, 4, 5)
-          val s2 = setToList(s).content
-          s == s2
-        }
-      }""",
-      BooleanLiteral(true)
-    )
-    
-  )
-  
-  
-  
-  for ( prog <- programs ;
-        requireMonitor <- Seq(false ,true );
-        doInstrument <- Seq(false,true )
-        
-      ) {
-    testCodeGen(
-      prog.copy(name = prog.name + (if (requireMonitor)"_M_" else "" ) + (if (doInstrument)"_I_" else "" )), 
-      requireMonitor, doInstrument
-    )}
+      def test = {
+        val s = Set(1, 2, 3, 4, 5)
+        val s2 = setToList(s).content
+        s == s2
+      }
+    }
+  """
+
+  runTests()
 }
-- 
GitLab