Skip to content

Commit 9cfe6a7

Browse files
authored
Merge pull request #150 from szeiger/wip/integration-tests
Add integration tests
2 parents 2052ff2 + 6894d4e commit 9cfe6a7

File tree

12 files changed

+235
-23
lines changed

12 files changed

+235
-23
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ env:
2121
- TEST_COMMAND="testFunctional"
2222
- TEST_COMMAND="-Dmima.testScalaVersion=2.11.8 testFunctional"
2323
- TEST_COMMAND="-Dmima.testScalaVersion=2.12.0 testFunctional"
24+
- TEST_COMMAND="it:test"
2425

2526
script:
2627
- sbt $TEST_COMMAND

project/Build.scala

Lines changed: 88 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sbt._
22
import Keys._
3+
import com.typesafe.config.ConfigFactory
34

45
// I need to make these imported by default
56
import Project.inConfig
@@ -136,7 +137,8 @@ object MimaBuild {
136137
settings(myAssemblySettings:_*)
137138
settings(
138139
// add task functional-tests that depends on all functional tests
139-
functionalTests := runAllTests.value,
140+
functionalTests := allTests(functionalTests in _).value,
141+
test in IntegrationTest := allTests(test in IntegrationTest in _).value,
140142
mainClass in assembly := Some("com.typesafe.tools.mima.cli.Main")
141143
)
142144
)
@@ -163,20 +165,28 @@ object MimaBuild {
163165
// select all testN directories.
164166
val bases = (file("reporter") / "functional-tests" / "src" / "test") *
165167
(DirectoryFilter && new SimpleFileFilter(_.list.contains("problems.txt")))
168+
val integrationTestBases = (file("reporter") / "functional-tests" / "src" / "it") *
169+
(DirectoryFilter && new SimpleFileFilter(_.list.contains("test.conf")))
166170

167171
// make the Project for each discovered directory
168172
lazy val tests = bases.get map testProject
173+
lazy val integrationTests = integrationTestBases.get map integrationTestProject
169174

170175
// defines a Project for the given base directory (for example, functional-tests/test1)
171176
// Its name is the directory name (test1) and it has compile+package tasks for sources in v1/ and v2/
172177
def testProject(base: File) = project("test-" + base.name, base, settings = testProjectSettings).configs(v1Config, v2Config)
178+
def integrationTestProject(base: File) = project("it-" + base.name, base, settings = integrationTestProjectSettings)
173179

174180
lazy val testProjectSettings =
175181
commonSettings ++ // normal project defaults; can be trimmed later- test and run aren't needed, for example.
176182
Seq(scalaVersion := (testScalaVersion in Global).value) ++
177183
inConfig(v1Config)(perConfig) ++ // add compile/package for the v1 sources
178184
inConfig(v2Config)(perConfig) :+ // add compile/package for the v2 sources
179185
(functionalTests := runTest.value) // add the functional-tests task
186+
lazy val integrationTestProjectSettings =
187+
commonSettings ++
188+
Seq(scalaVersion := (testScalaVersion in Global).value) ++
189+
(test in IntegrationTest := runIntegrationTest.value)
180190

181191
// this is the key for the task that runs the reporter's functional tests
182192
lazy val functionalTests = TaskKey[Unit]("test-functional")
@@ -200,44 +210,84 @@ object MimaBuild {
200210
lazy val shortSourceDir = scalaSource := baseDirectory.value / configuration.value.name
201211

202212
lazy val runTest = Def.task {
203-
val cp = (fullClasspath in (reporterFunctionalTests, Compile)).value // the test classpath from the functionalTest project for the test
204213
val proj = thisProjectRef.value // gives us the ProjectRef this task is defined in
205-
val si = (scalaInstance in core).value // get a reference to the already loaded Scala classes so we get the advantage of a warm jvm
206-
val v1 = (packageBin in v1Config).value // package the v1 sources and get the configuration used
207-
val v2 = (packageBin in v2Config).value // same for v2
208-
val scalaV = scalaVersion.value
209-
val streams = Keys.streams.value
214+
runCollectProblemsTest(
215+
(fullClasspath in (reporterFunctionalTests, Compile)).value, // the test classpath from the functionalTest project for the test
216+
(scalaInstance in core).value, // get a reference to the already loaded Scala classes so we get the advantage of a warm jvm
217+
streams.value,
218+
proj.project,
219+
(packageBin in v1Config).value, // package the v1 sources and get the configuration used
220+
(packageBin in v2Config).value, // same for v2
221+
baseDirectory.value,
222+
scalaVersion.value,
223+
null)
224+
}
225+
226+
lazy val runIntegrationTest = Def.task {
227+
val proj = thisProjectRef.value // gives us the ProjectRef this task is defined in
228+
val confFile = baseDirectory.value / "test.conf"
229+
val conf = ConfigFactory.parseFile(confFile).resolve()
230+
val moduleBase = conf.getString("groupId") % conf.getString("artifactId")
231+
val jar1 = getArtifact(moduleBase % conf.getString("v1"), ivySbt.value, streams.value)
232+
val jar2 = getArtifact(moduleBase % conf.getString("v2"), ivySbt.value, streams.value)
233+
streams.value.log.info(s"Comparing $jar1 -> $jar2")
234+
runCollectProblemsTest(
235+
(fullClasspath in (reporterFunctionalTests, Compile)).value, // the test classpath from the functionalTest project for the test
236+
(scalaInstance in core).value, // get a reference to the already loaded Scala classes so we get the advantage of a warm jvm
237+
streams.value,
238+
proj.project,
239+
jar1,
240+
jar2,
241+
baseDirectory.value,
242+
scalaVersion.value,
243+
confFile.getAbsolutePath)
244+
}
245+
246+
def runCollectProblemsTest(cp: Keys.Classpath, si: ScalaInstance, streams: Keys.TaskStreams, testName: String, v1: File, v2: File, projectPath: File, scalaV: String, filterPath: String): Unit = {
210247
val urls = Attributed.data(cp).map(_.toURI.toURL).toArray
211248
val loader = new java.net.URLClassLoader(urls, si.loader)
212249

213250
val testClass = loader.loadClass("com.typesafe.tools.mima.lib.CollectProblemsTest")
214251
val testRunner = testClass.newInstance().asInstanceOf[{
215-
def runTest(testClasspath: Array[String], testName: String, oldJarPath: String, newJarPath: String, oraclePath: String): Unit
252+
def runTest(testClasspath: Array[String], testName: String, oldJarPath: String, newJarPath: String, oraclePath: String, filterPath: String): Unit
216253
}]
217254

218255
// Add the scala-library to the MiMa classpath used to run this test
219256
val testClasspath = Attributed.data(cp).filter(_.getName endsWith "scala-library.jar").map(_.getAbsolutePath).toArray
220257

221-
val projectPath = proj.build.getPath + "reporter" + "/" + "functional-tests" + "/" + "src" + "/" + "test" + "/" + proj.project.stripPrefix("test-")
222-
223-
val oraclePath = {
224-
val p = projectPath + "/problems.txt"
225-
val p212 = projectPath + "/problems-2.12.txt"
226-
if(!(scalaV.startsWith("2.10.") || scalaV.startsWith("2.11.")) && new java.io.File(p212).exists) p212
258+
val oracleFile = {
259+
val p = projectPath / "problems.txt"
260+
val p212 = projectPath / "problems-2.12.txt"
261+
if(!(scalaV.startsWith("2.10.") || scalaV.startsWith("2.11.")) && p212.exists) p212
227262
else p
228263
}
229264

230265
try {
231266
import scala.language.reflectiveCalls
232-
testRunner.runTest(testClasspath, proj.project, v1.getAbsolutePath, v2.getAbsolutePath, oraclePath)
233-
streams.log.info("Test '" + proj.project + "' succeeded.")
267+
testRunner.runTest(testClasspath, testName, v1.getAbsolutePath, v2.getAbsolutePath, oracleFile.getAbsolutePath, filterPath)
268+
streams.log.info("Test '" + testName + "' succeeded.")
234269
} catch {
235270
case e: Exception => sys.error(e.toString)
236271
}
237-
()
238272
}
239273

240-
lazy val runAllTests = Def.taskDyn {
274+
def getArtifact(m: ModuleID, ivy: IvySbt, s: TaskStreams): File = {
275+
val moduleSettings = InlineConfiguration(
276+
"dummy" % "test" % "version",
277+
ModuleInfo("dummy-test-project-for-resolving"),
278+
dependencies = Seq(m))
279+
val module = new ivy.Module(moduleSettings)
280+
val report = Deprecated.Inner.ivyUpdate(ivy)(module, s)
281+
val optFile = (for {
282+
config <- report.configurations
283+
module <- config.modules
284+
(artifact, file) <- module.artifacts
285+
if artifact.name == m.name
286+
} yield file).headOption
287+
optFile getOrElse sys.error("Could not resolve artifact: " + m)
288+
}
289+
290+
def allTests(f: ProjectRef => TaskKey[Unit]) = Def.taskDyn {
241291
val s = state.value // this is how we access all defined projects from a task
242292
val proj = thisProjectRef.value // gives us the ProjectRef this task is defined in
243293
val _ = (test in Test).value // requires unit tests to run first
@@ -247,7 +297,7 @@ object MimaBuild {
247297
val allProjects = structure.units(proj.build).defined.values filter (_.id != proj.project)
248298

249299
// get the fun-tests task in each project
250-
val allTests = allProjects.toSeq flatMap { p => functionalTests in ProjectRef(proj.build, p.id) get structure.data }
300+
val allTests = allProjects.toSeq flatMap { p => f(ProjectRef(proj.build, p.id)) get structure.data }
251301

252302
// depend on all fun-tests
253303
Def.task {
@@ -260,5 +310,23 @@ object MimaBuild {
260310
}
261311

262312
object DefineTestProjectsPlugin extends AutoPlugin {
263-
override def extraProjects = MimaBuild.tests
313+
override def extraProjects = MimaBuild.tests ++ MimaBuild.integrationTests
314+
}
315+
316+
// use the SI-7934 workaround to silence a deprecation warning on an sbt API
317+
// we have no choice but to call. on the lack of any suitable alternative,
318+
// see https://gitter.im/sbt/sbt-dev?at=5616e2681b0e279854bd74a4 :
319+
// "it's my intention to eventually come up with a public API" says Eugene Y
320+
object Deprecated {
321+
@deprecated("", "") class Inner {
322+
def ivyUpdate(ivy: IvySbt)(module: ivy.Module, s: TaskStreams) =
323+
IvyActions.update(
324+
module,
325+
new UpdateConfiguration(
326+
retrieve = None,
327+
missingOk = false,
328+
logging = UpdateLogging.DownloadOnly),
329+
s.log)
330+
}
331+
object Inner extends Inner
264332
}

project/plugins.sbt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5")
1414
scalacOptions ++= Seq("-feature", "-deprecation")
1515

1616
libraryDependencies += "org.scala-sbt" % "scripted-plugin" % sbtVersion.value
17+
18+
libraryDependencies += "com.typesafe" % "config" % "1.3.0"

reporter/functional-tests/src/it/scala-library-2-10/problems.txt

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
groupId = "org.scala-lang"
2+
artifactId = "scala-library"
3+
v1 = "2.10.0"
4+
v2 = "2.10.6"

reporter/functional-tests/src/it/scala-library-2-11/problems.txt

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
groupId = "org.scala-lang"
2+
artifactId = "scala-library"
3+
v1 = "2.11.0"
4+
v2 = "2.11.8"

reporter/functional-tests/src/it/scala-reflect-2-10/problems.txt

Whitespace-only changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
groupId = "org.scala-lang"
2+
artifactId = "scala-reflect"
3+
v1 = "2.10.0"
4+
v2 = "2.10.6"
5+
6+
filter {
7+
packages = [
8+
"scala.reflect.internal"
9+
]
10+
problems=[
11+
{
12+
matchName="scala.reflect.macros.Attachments$NonemptyAttachments"
13+
problemName=MissingClassProblem
14+
},
15+
{
16+
matchName="scala.reflect.runtime.JavaUniverse.isInvalidClassName"
17+
problemName=MissingMethodProblem
18+
},
19+
{
20+
matchName="scala.reflect.runtime.SymbolLoaders.isInvalidClassName"
21+
problemName=MissingMethodProblem
22+
}
23+
]
24+
}

reporter/functional-tests/src/it/scala-reflect-2-11/problems.txt

Whitespace-only changes.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
groupId = "org.scala-lang"
2+
artifactId = "scala-reflect"
3+
v1 = "2.11.0"
4+
v2 = "2.11.8"
5+
6+
filter {
7+
packages = [
8+
"scala.reflect.internal"
9+
]
10+
problems=[
11+
{
12+
matchName="scala.reflect.api.StandardLiftables#StandardLiftableInstances.liftTree"
13+
problemName=MissingMethodProblem
14+
},
15+
{
16+
matchName="scala.reflect.api.Internals#ReificationSupportApi#SyntacticTypeAppliedExtractor.unapply"
17+
problemName=IncompatibleResultTypeProblem
18+
},
19+
{
20+
matchName="scala.reflect.api.Internals#ReificationSupportApi#SyntacticTypeAppliedExtractor.unapply"
21+
problemName=MissingMethodProblem
22+
},
23+
{
24+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticSelectType"
25+
problemName=MissingMethodProblem
26+
},
27+
{
28+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticAppliedType"
29+
problemName=MissingMethodProblem
30+
},
31+
{
32+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticSelectTerm"
33+
problemName=MissingMethodProblem
34+
},
35+
{
36+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticPartialFunction"
37+
problemName=MissingMethodProblem
38+
},
39+
{
40+
matchName="scala.reflect.api.Mirror.symbolOf"
41+
problemName=MissingMethodProblem
42+
},
43+
{
44+
matchName="scala.reflect.api.Mirror.typeOf"
45+
problemName=MissingMethodProblem
46+
},
47+
{
48+
matchName="scala.reflect.api.Mirror.weakTypeOf"
49+
problemName=MissingMethodProblem
50+
},
51+
{
52+
matchName="scala.reflect.api.Internals$ReificationSupportApi$SyntacticIdentExtractor"
53+
problemName=MissingClassProblem
54+
},
55+
{
56+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticIdent"
57+
problemName=MissingMethodProblem
58+
},
59+
{
60+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticSingletonType"
61+
problemName=MissingMethodProblem
62+
},
63+
{
64+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticTermIdent"
65+
problemName=MissingMethodProblem
66+
},
67+
{
68+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticTypeIdent"
69+
problemName=MissingMethodProblem
70+
},
71+
{
72+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticCompoundType"
73+
problemName=MissingMethodProblem
74+
},
75+
{
76+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticAnnotatedType"
77+
problemName=MissingMethodProblem
78+
},
79+
{
80+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticTypeProjection"
81+
problemName=MissingMethodProblem
82+
},
83+
{
84+
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticExistentialType"
85+
problemName=MissingMethodProblem
86+
},
87+
{
88+
matchName="scala.reflect.runtime.SynchronizedOps.newNestedScope"
89+
problemName=MissingMethodProblem
90+
},
91+
{
92+
matchName="scala.reflect.runtime.ThreadLocalStorage#MyThreadLocalStorage.values"
93+
problemName=MissingMethodProblem
94+
},
95+
{
96+
matchName="scala.reflect.io.ZipArchive.scala$reflect$io$ZipArchive$$walkIterator"
97+
problemName=MissingMethodProblem
98+
}
99+
]
100+
}

reporter/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.typesafe.tools.mima.lib
22

3-
import java.io.{BufferedInputStream, FileInputStream}
3+
import java.io.{BufferedInputStream, File, FileInputStream}
44

5+
import com.typesafe.config.ConfigFactory
6+
import com.typesafe.tools.mima.cli.ProblemFiltersConfig
57
import com.typesafe.tools.mima.core.{Config, PathResolver, Settings}
68

79
import scala.io.Source
@@ -11,7 +13,7 @@ case class TestFailed(msg: String) extends Exception(msg)
1113

1214
class CollectProblemsTest {
1315

14-
def runTest(testClasspath: Array[String])(testName: String, oldJarPath: String, newJarPath: String, oraclePath: String) {
16+
def runTest(testClasspath: Array[String])(testName: String, oldJarPath: String, newJarPath: String, oraclePath: String, filterPath: String) {
1517
// load test setup
1618
Config.setup("scala com.typesafe.tools.mima.MiMaLibUI <old-dir> <new-dir>", Array(oldJarPath, newJarPath))
1719
val cp = testClasspath ++ ClassPath.split(Config.baseClassPath.asClasspathString)
@@ -21,7 +23,14 @@ class CollectProblemsTest {
2123
val mima = new MiMaLib(Config.baseClassPath)
2224

2325
// SUT
24-
val problems = mima.collectProblems(oldJarPath, newJarPath).map(_.description("new"))
26+
val allProblems = mima.collectProblems(oldJarPath, newJarPath)
27+
28+
val problems = (if(filterPath ne null) {
29+
val fallback = ConfigFactory.parseString("filter { problems = []\npackages=[] }")
30+
val config = ConfigFactory.parseFile(new File(filterPath)).withFallback(fallback).resolve()
31+
val filters = ProblemFiltersConfig.parseProblemFilters(config)
32+
allProblems.filter(p => filters.forall(_.apply(p)))
33+
} else allProblems).map(_.description("new"))
2534

2635
// load oracle
2736
val inputStream = new BufferedInputStream(new FileInputStream(oraclePath))

0 commit comments

Comments
 (0)