From a4f00bf58d04cf55b177231edd1f0840427b52b4 Mon Sep 17 00:00:00 2001 From: Etienne Kneuss <colder@php.net> Date: Thu, 29 Aug 2013 16:34:31 +0200 Subject: [PATCH] Implement performance regression testing Ensures that tests run in an acceptable amount of time. Needs at least 5 runs. Can cause failures of test-only due to warm-up and class loading effects. --- .gitignore | 1 + build.sbt | 6 +- src/test/scala/leon/test/LeonTestSuite.scala | 86 +++++++++++++++++++ .../test/evaluators/EvaluatorsTests.scala | 4 +- .../scala/leon/test/purescala/DataGen.scala | 2 +- .../leon/test/purescala/LikelyEqSuite.scala | 7 +- .../purescala/TreeNormalizationsTests.scala | 4 +- .../leon/test/purescala/TreeOpsTests.scala | 4 +- .../scala/leon/test/purescala/TreeTests.scala | 7 +- .../test/solvers/TimeoutSolverTests.scala | 7 +- .../test/solvers/z3/FairZ3SolverTests.scala | 4 +- .../solvers/z3/FairZ3SolverTestsNewAPI.scala | 4 +- .../z3/UninterpretedZ3SolverTests.scala | 4 +- .../leon/test/synthesis/AlgebraSuite.scala | 7 +- .../test/synthesis/LinearEquationsSuite.scala | 7 +- .../leon/test/synthesis/SynthesisSuite.scala | 3 +- .../PureScalaVerificationRegression.scala | 4 +- .../XLangVerificationRegression.scala | 4 +- 18 files changed, 117 insertions(+), 48 deletions(-) create mode 100644 src/test/scala/leon/test/LeonTestSuite.scala diff --git a/.gitignore b/.gitignore index 12c115fd5..eac146feb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ target /leon /setupenv /leon-bench +/.test-history # synthesis derivation*.dot diff --git a/build.sbt b/build.sbt index 651417296..4fe1fed09 100644 --- a/build.sbt +++ b/build.sbt @@ -24,10 +24,14 @@ resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/release libraryDependencies ++= Seq( "org.scala-lang" % "scala-compiler" % "2.10.2", - "org.scalatest" %% "scalatest" % "1.9.1" excludeAll(ExclusionRule(organization="org.scala-lang")), + "org.scalatest" % "scalatest_2.10" % "2.0.M5b" % "test" excludeAll(ExclusionRule(organization="org.scala-lang")), "com.typesafe.akka" %% "akka-actor" % "2.2.0" excludeAll(ExclusionRule(organization="org.scala-lang")) ) fork in run := true fork in test := true + +logBuffered in Test := false + +testOptions in Test += Tests.Argument("-oD") diff --git a/src/test/scala/leon/test/LeonTestSuite.scala b/src/test/scala/leon/test/LeonTestSuite.scala new file mode 100644 index 000000000..2dd0a92eb --- /dev/null +++ b/src/test/scala/leon/test/LeonTestSuite.scala @@ -0,0 +1,86 @@ +package leon.test +import scala.io.Source +import org.scalatest._ + +trait LeonTestSuite extends FunSuite { + def now() = { + System.currentTimeMillis + } + + case class Statistics(values: List[Long]) { + val n = values.size + val avg = values.sum.toDouble/n + val stddev = Math.sqrt(Math.abs(values.map(_.toDouble - avg).sum/n)) + + def accountsFor(ms: Long) = { + if (n < 5) { + true + } else { + val msd = ms.toDouble + (msd < avg + 3*stddev + 20) + } + } + + def withValue(v: Long) = this.copy(v :: values) + } + + def testIdentifier(name: String): String = { + (this.getClass.getName+"-"+name).replaceAll("[^0-9a-zA-Z-]", "") + } + + def bookKeepingFile(id: String) = { + import java.io.File + + val f = new File(System.getProperty("user.dir")+"/.test-history/"+id+".log"); + + f.getParentFile.mkdirs() + + f + } + + def getStats(id: String): Statistics = { + val f = bookKeepingFile(id) + + if (f.canRead()) { + Statistics(Source.fromFile(f).getLines.flatMap{ line => + val l = line.trim + if (l.length > 0) { + Some(line.toLong) + } else { + None + } + }.toList) + } else { + Statistics(Nil) + } + } + + def storeStats(id: String, stats: Statistics) { + import java.io.FileWriter + + val f = bookKeepingFile(id) + + val fw = new FileWriter(f, true) + fw.write(stats.values.head+"\n") + fw.close + } + + override def test(name: String, tags: Tag*)(body: => Unit) { + super.test(name, tags: _*) { + val id = testIdentifier(name) + val ts = now() + + body + + val total = now()-ts + + val stats = getStats(id) + + if (!stats.accountsFor(total)) { + fail("Test took too long to run: "+total+"ms (avg: "+stats.avg+", stddev: "+stats.stddev+")") + } + + storeStats(id, stats.withValue(total)) + } + } +} diff --git a/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala b/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala index 9fd88e13b..edcc0c9b5 100644 --- a/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala +++ b/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala @@ -14,9 +14,7 @@ import leon.purescala.Definitions._ import leon.purescala.Trees._ import leon.purescala.TypeTrees._ -import org.scalatest.FunSuite - -class EvaluatorsTests extends FunSuite { +class EvaluatorsTests extends LeonTestSuite { private implicit lazy val leonContext = LeonContext( settings = Settings( synthesis = false, diff --git a/src/test/scala/leon/test/purescala/DataGen.scala b/src/test/scala/leon/test/purescala/DataGen.scala index b188291bb..99659fb69 100644 --- a/src/test/scala/leon/test/purescala/DataGen.scala +++ b/src/test/scala/leon/test/purescala/DataGen.scala @@ -16,7 +16,7 @@ import leon.evaluators._ import org.scalatest.FunSuite -class DataGen extends FunSuite { +class DataGen extends LeonTestSuite { private implicit lazy val leonContext = LeonContext( settings = Settings( synthesis = false, diff --git a/src/test/scala/leon/test/purescala/LikelyEqSuite.scala b/src/test/scala/leon/test/purescala/LikelyEqSuite.scala index 3a4fa9371..54531aa35 100644 --- a/src/test/scala/leon/test/purescala/LikelyEqSuite.scala +++ b/src/test/scala/leon/test/purescala/LikelyEqSuite.scala @@ -1,14 +1,13 @@ /* Copyright 2009-2013 EPFL, Lausanne */ -package leon.test.purescala - -import org.scalatest.FunSuite +package leon.test +package purescala import leon.purescala.Common._ import leon.purescala.Trees._ -class LikelyEqSuite extends FunSuite { +class LikelyEqSuite extends LeonTestSuite { def i(x: Int) = IntLiteral(x) val xId = FreshIdentifier("x") diff --git a/src/test/scala/leon/test/purescala/TreeNormalizationsTests.scala b/src/test/scala/leon/test/purescala/TreeNormalizationsTests.scala index 42ac12758..5d788ef2e 100644 --- a/src/test/scala/leon/test/purescala/TreeNormalizationsTests.scala +++ b/src/test/scala/leon/test/purescala/TreeNormalizationsTests.scala @@ -10,9 +10,7 @@ import leon.purescala.Trees._ import leon.purescala.TreeOps._ import leon.purescala.TreeNormalizations._ -import org.scalatest.FunSuite - -class TreeNormalizationsTests extends FunSuite { +class TreeNormalizationsTests extends LeonTestSuite { def i(x: Int) = IntLiteral(x) val xId = FreshIdentifier("x").setType(Int32Type) diff --git a/src/test/scala/leon/test/purescala/TreeOpsTests.scala b/src/test/scala/leon/test/purescala/TreeOpsTests.scala index 42ccca719..823383baf 100644 --- a/src/test/scala/leon/test/purescala/TreeOpsTests.scala +++ b/src/test/scala/leon/test/purescala/TreeOpsTests.scala @@ -11,9 +11,7 @@ import leon.purescala.Trees._ import leon.purescala.TypeTrees._ import leon.purescala.TreeOps._ -import org.scalatest.FunSuite - -class TreeOpsTests extends FunSuite { +class TreeOpsTests extends LeonTestSuite { private val silentContext = LeonContext(reporter = new TestSilentReporter) test("Path-aware simplifications") { diff --git a/src/test/scala/leon/test/purescala/TreeTests.scala b/src/test/scala/leon/test/purescala/TreeTests.scala index 45c341d49..0d46e8fb9 100644 --- a/src/test/scala/leon/test/purescala/TreeTests.scala +++ b/src/test/scala/leon/test/purescala/TreeTests.scala @@ -1,15 +1,14 @@ /* Copyright 2009-2013 EPFL, Lausanne */ -package leon.test.purescala +package leon.test +package purescala import leon.purescala.Common._ import leon.purescala.Definitions._ import leon.purescala.Trees._ import leon.purescala.TypeTrees._ -import org.scalatest.FunSuite - -class TreeTests extends FunSuite { +class TreeTests extends LeonTestSuite { test("And- and Or- simplifications") { val x = Variable(FreshIdentifier("x").setType(BooleanType)) diff --git a/src/test/scala/leon/test/solvers/TimeoutSolverTests.scala b/src/test/scala/leon/test/solvers/TimeoutSolverTests.scala index 462787790..954d30e65 100644 --- a/src/test/scala/leon/test/solvers/TimeoutSolverTests.scala +++ b/src/test/scala/leon/test/solvers/TimeoutSolverTests.scala @@ -1,8 +1,7 @@ /* Copyright 2009-2013 EPFL, Lausanne */ -package leon.test.solvers - -import org.scalatest.FunSuite +package leon.test +package solvers import leon._ import leon.solvers._ @@ -11,7 +10,7 @@ import leon.purescala.Definitions._ import leon.purescala.Trees._ import leon.purescala.TypeTrees._ -class TimeoutSolverTests extends FunSuite { +class TimeoutSolverTests extends LeonTestSuite { private class IdioticSolver(ctx : LeonContext) extends Solver(ctx) with NaiveIncrementalSolver { val name = "Idiotic" val description = "Loops when it doesn't know" diff --git a/src/test/scala/leon/test/solvers/z3/FairZ3SolverTests.scala b/src/test/scala/leon/test/solvers/z3/FairZ3SolverTests.scala index 4e479a835..68a41d0ce 100644 --- a/src/test/scala/leon/test/solvers/z3/FairZ3SolverTests.scala +++ b/src/test/scala/leon/test/solvers/z3/FairZ3SolverTests.scala @@ -14,9 +14,7 @@ import leon.purescala.TypeTrees._ import leon.solvers.Solver import leon.solvers.z3.FairZ3Solver -import org.scalatest.FunSuite - -class FairZ3SolverTests extends FunSuite { +class FairZ3SolverTests extends LeonTestSuite { private var testCounter : Int = 0 private def solverCheck(solver : Solver, expr : Expr, expected : Option[Boolean], msg : String) = { testCounter += 1 diff --git a/src/test/scala/leon/test/solvers/z3/FairZ3SolverTestsNewAPI.scala b/src/test/scala/leon/test/solvers/z3/FairZ3SolverTestsNewAPI.scala index 53bac4d7c..802bd13f0 100644 --- a/src/test/scala/leon/test/solvers/z3/FairZ3SolverTestsNewAPI.scala +++ b/src/test/scala/leon/test/solvers/z3/FairZ3SolverTestsNewAPI.scala @@ -14,9 +14,7 @@ import leon.purescala.TypeTrees._ import leon.solvers.Solver import leon.solvers.z3.FairZ3Solver -import org.scalatest.FunSuite - -class FairZ3SolverTestsNewAPI extends FunSuite { +class FairZ3SolverTestsNewAPI extends LeonTestSuite { private var testCounter : Int = 0 private def solverCheck(solver : Solver, expr : Expr, expected : Option[Boolean], msg : String) = { testCounter += 1 diff --git a/src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala b/src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala index e9d87d641..339d5bf2a 100644 --- a/src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala +++ b/src/test/scala/leon/test/solvers/z3/UninterpretedZ3SolverTests.scala @@ -14,9 +14,7 @@ import leon.purescala.TypeTrees._ import leon.solvers.Solver import leon.solvers.z3.UninterpretedZ3Solver -import org.scalatest.FunSuite - -class UninterpretedZ3SolverTests extends FunSuite { +class UninterpretedZ3SolverTests extends LeonTestSuite { private var testCounter : Int = 0 private def solverCheck(solver : Solver, expr : Expr, expected : Option[Boolean], msg : String) = { testCounter += 1 diff --git a/src/test/scala/leon/test/synthesis/AlgebraSuite.scala b/src/test/scala/leon/test/synthesis/AlgebraSuite.scala index 88fce1450..aef22779e 100644 --- a/src/test/scala/leon/test/synthesis/AlgebraSuite.scala +++ b/src/test/scala/leon/test/synthesis/AlgebraSuite.scala @@ -1,12 +1,11 @@ /* Copyright 2009-2013 EPFL, Lausanne */ -package leon.test.synthesis - -import org.scalatest.FunSuite +package leon.test +package synthesis import leon.synthesis.Algebra._ -class AlgebraSuite extends FunSuite { +class AlgebraSuite extends LeonTestSuite { test("remainder") { assert(remainder(1,1) === 0) diff --git a/src/test/scala/leon/test/synthesis/LinearEquationsSuite.scala b/src/test/scala/leon/test/synthesis/LinearEquationsSuite.scala index bd83fba3f..03b3e66a8 100644 --- a/src/test/scala/leon/test/synthesis/LinearEquationsSuite.scala +++ b/src/test/scala/leon/test/synthesis/LinearEquationsSuite.scala @@ -1,8 +1,7 @@ /* Copyright 2009-2013 EPFL, Lausanne */ -package leon.test.synthesis - -import org.scalatest.FunSuite +package leon.test +package synthesis import leon.purescala.Trees._ import leon.purescala.TypeTrees._ @@ -12,7 +11,7 @@ import leon.test.purescala.LikelyEq import leon.synthesis.LinearEquations._ -class LinearEquationsSuite extends FunSuite { +class LinearEquationsSuite extends LeonTestSuite { def i(x: Int) = IntLiteral(x) diff --git a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala index 0f512690d..d134c980e 100644 --- a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala +++ b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala @@ -12,12 +12,11 @@ import leon.solvers.Solver import leon.synthesis._ import leon.synthesis.utils._ -import org.scalatest.FunSuite import org.scalatest.matchers.ShouldMatchers._ import java.io.{BufferedWriter, FileWriter, File} -class SynthesisSuite extends FunSuite { +class SynthesisSuite extends LeonTestSuite { private var counter : Int = 0 private def nextInt() : Int = { counter += 1 diff --git a/src/test/scala/leon/test/verification/PureScalaVerificationRegression.scala b/src/test/scala/leon/test/verification/PureScalaVerificationRegression.scala index 5dd93cbed..45374ca5e 100644 --- a/src/test/scala/leon/test/verification/PureScalaVerificationRegression.scala +++ b/src/test/scala/leon/test/verification/PureScalaVerificationRegression.scala @@ -6,13 +6,11 @@ package verification import leon.verification.{AnalysisPhase,VerificationReport} -import org.scalatest.FunSuite - import java.io.File import TestUtils._ -class PureScalaVerificationRegression extends FunSuite { +class PureScalaVerificationRegression extends LeonTestSuite { private var counter : Int = 0 private def nextInt() : Int = { counter += 1 diff --git a/src/test/scala/leon/test/verification/XLangVerificationRegression.scala b/src/test/scala/leon/test/verification/XLangVerificationRegression.scala index d85a5dd6f..64b523de9 100644 --- a/src/test/scala/leon/test/verification/XLangVerificationRegression.scala +++ b/src/test/scala/leon/test/verification/XLangVerificationRegression.scala @@ -6,13 +6,11 @@ package verification import leon.verification.{AnalysisPhase,VerificationReport} -import org.scalatest.FunSuite - import java.io.File import TestUtils._ -class XLangVerificationRegression extends FunSuite { +class XLangVerificationRegression extends LeonTestSuite { private var counter : Int = 0 private def nextInt() : Int = { counter += 1 -- GitLab