Add scripted tests for classloader-cache

Normally I'd include these with the previous commit, but the diff is so
large that I put them in their own commit. The tests handle 5 scenarios:

1) akka-actor-system -- a project that has Akka as a dependency and a
   simple main method that creates and terminates an ActorSystem. What
   is interesting about this test is that if scriptedBufferLog := false,
   we notice that the first call to run is slow, but subsequent calls to
   run and test are fast. The test does at least ensure that recycling
   the runtime layer in test works ok.

2) jni -- verifies that a project with native libraries will be able to
   load the library with each run. It actually swaps out the underlying
   library so that the it really ensures that the library is reloaded
   between runs.

3) library-mismatch -- verifies that the layered classloaders can work
   when the test dependencies are incompatible with the runtime
   dependencies. In this test, the test dependencies use an api in a
   library called foo-lib that isn't available in the version used by
   the runtime dependencies. Because of this incompatibility, the test
   will not work if Test / layeringStrategy := LayeringStrategy.Full.

4) scalatest -- verifies that a test runs using the scalatest framework
5) utest -- verifies that a test runs using the utest framework

The reason for (4) and (5) is to ensure that both the in sourced test
frameworks and external frameworks work with the new loaders.
This commit is contained in:
Ethan Atkins
2019-01-30 08:55:22 -08:00
parent a06f5435c6
commit aca541898d
111 changed files with 653 additions and 2 deletions
+2 -2
View File
@@ -23,7 +23,7 @@ env:
- SBT_CMD="scripted source-dependencies/*1of3"
- SBT_CMD="scripted source-dependencies/*2of3"
- SBT_CMD="scripted source-dependencies/*3of3"
- SBT_CMD="scripted tests/* watch/*"
- SBT_CMD="scripted tests/* watch/* classloader-cache/*"
- SBT_CMD="repoOverrideTest:scripted dependency-management/*"
matrix:
@@ -53,7 +53,7 @@ install:
script:
# It doesn't need that much memory because compile and run are forked
- sbt -J-XX:ReservedCodeCacheSize=128m -J-Xmx800M -J-Xms800M -J-server "$SBT_CMD"
- sbt -Dsbt.version=1.2.6 -J-XX:ReservedCodeCacheSize=128m -J-Xmx800M -J-Xms800M -J-server "$SBT_CMD"
before_cache:
- find $HOME/.ivy2 -name "ivydata-*.properties" -delete
@@ -0,0 +1,9 @@
val akkaTest = (project in file(".")).settings(
name := "akka-test",
scalaVersion := "2.12.7",
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.5.16",
"com.lihaoyi" %% "utest" % "0.6.6" % "test"
),
testFrameworks := Seq(new TestFramework("utest.runner.Framework"))
)
@@ -0,0 +1,15 @@
package sbt.scripted
import akka.actor.ActorSystem
import scala.concurrent.Await
import scala.concurrent.duration._
object AkkaTest {
def main(args: Array[String]): Unit = {
val now = System.nanoTime
val system = ActorSystem("akka")
Await.result(system.terminate(), 5.seconds)
val elapsed = System.nanoTime - now
println(s"Run took ${elapsed / 1.0e6} ms")
}
}
@@ -0,0 +1,12 @@
package sbt.scripted
import utest._
object AkkaPerfTest extends TestSuite {
val tests: Tests = Tests {
'run - {
AkkaTest.main(Array.empty[String])
1 ==> 1
}
}
}
@@ -0,0 +1,7 @@
> set Test / layeringStrategy := LayeringStrategy.Full
> run
> test
> test
@@ -0,0 +1,38 @@
import java.nio.file._
import scala.collection.JavaConverters._
val copyTestResources = inputKey[Unit]("Copy the native libraries to the base directory")
val appendToLibraryPath = taskKey[Unit]("Append the base directory to the java.library.path system property")
val dropLibraryPath = taskKey[Unit]("Drop the last path from the java.library.path system property")
val wrappedRun = taskKey[Unit]("Run with modified java.library.path")
val wrappedTest = taskKey[Unit]("Test with modified java.library.path")
def wrap(task: InputKey[Unit]): Def.Initialize[Task[Unit]] =
Def.sequential(appendToLibraryPath, task.toTask(""), dropLibraryPath)
val root = (project in file(".")).settings(
scalaVersion := "2.12.7",
javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-h",
sourceDirectory.value.toPath.resolve("main/native/include").toString),
libraryDependencies += "com.lihaoyi" %% "utest" % "0.6.6" % "test",
testFrameworks := Seq(new TestFramework("utest.runner.Framework")),
copyTestResources := {
val key = Def.spaceDelimited().parsed.head
val base = baseDirectory.value.toPath
val resources = (baseDirectory.value / "src" / "main" / "resources" / key).toPath
Files.walk(resources).iterator.asScala.foreach { p =>
Files.copy(p, base.resolve(p.getFileName), StandardCopyOption.REPLACE_EXISTING)
}
},
appendToLibraryPath := {
val cp = System.getProperty("java.library.path", "").split(":")
val newCp = if (cp.contains(".")) cp else cp :+ "."
System.setProperty("java.library.path", newCp.mkString(":"))
},
dropLibraryPath := {
val cp = System.getProperty("java.library.path", "").split(":").dropRight(1)
System.setProperty("java.library.path", cp.mkString(":"))
},
wrappedRun := wrap(Runtime / run).value,
wrappedTest := wrap(Test / testOnly).value
)
@@ -0,0 +1,15 @@
package sbt
import java.nio.file._
import utest._
object JniLibraryTest extends TestSuite {
val tests = Tests {
'load - {
'native - {
System.loadLibrary("sbt-jni-library-test0")
new JniLibrary().getIntegerValue ==> 2
}
}
}
}
@@ -0,0 +1,5 @@
package sbt;
final class JniLibrary {
public native int getIntegerValue();
}
@@ -0,0 +1,61 @@
TARGET_DIR := ../../../target
BUILD_DIR := $(TARGET_DIR)/build
NATIVE_DIR := native/$(shell uname -sm | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]')
LIB_DIR := ../resources/$(NATIVE_DIR)
LIB_NAME := sbt-jni-library-test0
POSIX_LIB_NAME := lib$(LIB_NAME)
SOURCE := sbt_JniLibrary
CC := clang
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
JNI_INCLUDE := -I$(shell mdfind -name jni.h | grep jdk1.8 | tail -n 1 | xargs dirname)\
-I$(shell mdfind -name jni_md.h | grep jdk1.8 | tail -n 1 | xargs dirname)
LD := /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
OBJS := $(BUILD_DIR)/x86_64/darwin/$(SOURCE).o
LIBS := $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).dylib
endif
ifeq ($(UNAME_S), Linux)
BASE_INCLUDE := $(shell locate jni.h | tail -n 1 | xargs dirname)
JNI_INCLUDE := -I$(BASE_INCLUDE) -I$(BASE_INCLUDE)/linux
OBJS := $(BUILD_DIR)/x86_64/linux/$(SOURCE).o
LIBS := $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).so
endif
LINUX_CCFLAGS := -fPIC
LINUX_LDFLAGS := -fPIC -shared
CCFLAGS := -I./../include $(JNI_INCLUDE) -Wno-unused-command-line-argument -std=c++11 -O3
all: $(LIBS)
.PHONY: clean all
$(BUILD_DIR)/x86_64/linux/$(SOURCE).o: $(SOURCE).cc
mkdir -p $(BUILD_DIR)/x86_64/linux; \
$(CC) -c $< $(CCFLAGS) $(JNI_INCLUDE) -fPIC -o $@
$(BUILD_DIR)/x86_64/darwin/$(SOURCE).o: $(SOURCE).cc
mkdir -p $(BUILD_DIR)/x86_64/darwin; \
$(CC) -c $< $(CCFLAGS) -framework Carbon -o $@
$(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).dylib: $(BUILD_DIR)/x86_64/darwin/$(SOURCE).o
mkdir -p $(TARGET_DIR)/x86_64; \
$(LD) -dynamiclib -framework Carbon $(CCFLAGS) -Wl,-headerpad_max_install_names -install_name @rpath/$(POSIX_LIB_NAME) \
$(BUILD_DIR)/x86_64/darwin/$(SOURCE).o \
-o $@ ; \
mkdir -p ../../resources/1; \
cp $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).dylib ../../resources/1
$(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).so: $(BUILD_DIR)/x86_64/linux/$(SOURCE).o
mkdir -p $(TARGET_DIR)/x86_64; \
$(CC) -shared $< $(CCFLAGS) -Wl,-headerpad_max_install_names -o $@; \
mkdir -p ../../resources/1; \
cp $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).so ../../resources/1;
clean:
rm -rf $(TARGET_DIR)/build $(TARGET_DIR)/$(NATIVE)
@@ -0,0 +1,15 @@
#include <jni.h>
#include "sbt_JniLibrary.h"
extern "C" {
/*
* Class: sbt_JniLibrary
* Method: getIntegerValue
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_sbt_JniLibrary_getIntegerValue
(JNIEnv *env, jobject obj) {
return 1;
}
}
@@ -0,0 +1,61 @@
TARGET_DIR := ../../../target
BUILD_DIR := $(TARGET_DIR)/build
NATIVE_DIR := native/$(shell uname -sm | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]')
LIB_DIR := ../resources/$(NATIVE_DIR)
LIB_NAME := sbt-jni-library-test0
POSIX_LIB_NAME := lib$(LIB_NAME)
SOURCE := sbt_JniLibrary
CC := clang
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
JNI_INCLUDE := -I$(shell mdfind -name jni.h | grep jdk1.8 | tail -n 1 | xargs dirname)\
-I$(shell mdfind -name jni_md.h | grep jdk1.8 | tail -n 1 | xargs dirname)
LD := /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
OBJS := $(BUILD_DIR)/x86_64/darwin/$(SOURCE).o
LIBS := $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).dylib
endif
ifeq ($(UNAME_S), Linux)
BASE_INCLUDE := $(shell locate jni.h | tail -n 1 | xargs dirname)
JNI_INCLUDE := -I$(BASE_INCLUDE) -I$(BASE_INCLUDE)/linux
OBJS := $(BUILD_DIR)/x86_64/linux/$(SOURCE).o
LIBS := $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).so
endif
LINUX_CCFLAGS := -fPIC
LINUX_LDFLAGS := -fPIC -shared
CCFLAGS := -I./../include $(JNI_INCLUDE) -Wno-unused-command-line-argument -std=c++11 -O3
all: $(LIBS)
.PHONY: clean all
$(BUILD_DIR)/x86_64/linux/$(SOURCE).o: $(SOURCE).cc
mkdir -p $(BUILD_DIR)/x86_64/linux; \
$(CC) -c $< $(CCFLAGS) $(JNI_INCLUDE) -fPIC -o $@
$(BUILD_DIR)/x86_64/darwin/$(SOURCE).o: $(SOURCE).cc
mkdir -p $(BUILD_DIR)/x86_64/darwin; \
$(CC) -c $< $(CCFLAGS) -framework Carbon -o $@
$(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).dylib: $(BUILD_DIR)/x86_64/darwin/$(SOURCE).o
mkdir -p $(TARGET_DIR)/x86_64; \
$(LD) -dynamiclib -framework Carbon $(CCFLAGS) -Wl,-headerpad_max_install_names -install_name @rpath/$(POSIX_LIB_NAME) \
$(BUILD_DIR)/x86_64/darwin/$(SOURCE).o \
-o $@ ; \
mkdir -p ../../resources/2; \
cp $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).dylib ../../resources/2
$(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).so: $(BUILD_DIR)/x86_64/linux/$(SOURCE).o
mkdir -p $(TARGET_DIR)/x86_64; \
$(CC) -shared $< $(CCFLAGS) -Wl,-headerpad_max_install_names -o $@; \
mkdir -p ../../resources/2; \
cp $(TARGET_DIR)/x86_64/$(POSIX_LIB_NAME).so ../../resources/2;
clean:
rm -rf $(TARGET_DIR)/build $(TARGET_DIR)/$(NATIVE)
@@ -0,0 +1,15 @@
#include <jni.h>
#include "sbt_JniLibrary.h"
extern "C" {
/*
* Class: sbt_JniLibrary
* Method: getIntegerValue
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_sbt_JniLibrary_getIntegerValue
(JNIEnv *env, jobject obj) {
return 2;
}
}
@@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class sbt_JniLibrary */
#ifndef _Included_sbt_JniLibrary
#define _Included_sbt_JniLibrary
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: sbt_JniLibrary
* Method: getIntegerValue
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_sbt_JniLibrary_getIntegerValue
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,12 @@
package sbt
import java.nio.file._
import scala.collection.JavaConverters._
object TestMain {
def main(args: Array[String]): Unit = {
val libraryPath = System.getProperty("java.library.path")
System.loadLibrary("sbt-jni-library-test0")
println(s"Native value is ${new JniLibrary().getIntegerValue}")
}
}
@@ -0,0 +1,15 @@
package sbt
import java.nio.file._
import utest._
object JniLibraryTest extends TestSuite {
val tests = Tests {
'load - {
'native - {
System.loadLibrary("sbt-jni-library-test0")
new JniLibrary().getIntegerValue ==> 1
}
}
}
}
@@ -0,0 +1,6 @@
> copyTestResources 1
> wrappedRun
> wrappedTest
> copyTestResources 2
$ copy-file changes/JniLibraryTest.scala src/test/scala/sbt/JniLibraryTest.scala
> wrappedTest
@@ -0,0 +1,9 @@
val snapshot = (project in file(".")).settings(
name := "mismatched-libraries",
scalaVersion := "2.12.7",
libraryDependencies ++= Seq("com.lihaoyi" %% "utest" % "0.6.6" % "test"),
testFrameworks := Seq(TestFramework("utest.runner.Framework")),
resolvers += "Local Maven" at file("libraries/ivy").toURI.toURL.toString,
libraryDependencies += "sbt" % "transitive-lib" % "0.1.0",
libraryDependencies += "sbt" % "foo-lib" % "0.2.0" % "test",
)
@@ -0,0 +1,11 @@
name := "foo-lib"
organization := "sbt"
publishTo := Some(Resolver.file("test-resolver", file("..").getCanonicalFile / "ivy"))
version := "0.1.0"
crossPaths := false
autoScalaLibrary := false
@@ -0,0 +1,7 @@
package sbt.foo;
public class Foo {
static public int x() {
return 1;
}
}
@@ -0,0 +1,11 @@
name := "foo-lib"
organization := "sbt"
publishTo := Some(Resolver.file("test-resolver", file("..").getCanonicalFile / "ivy"))
version := "0.2.0"
crossPaths := false
autoScalaLibrary := false
@@ -0,0 +1,10 @@
package sbt.foo;
public class Foo {
static public int x() {
return 2;
}
static public int y() {
return 3;
}
}
@@ -0,0 +1 @@
ca2193d86495f120496370cf2e75355f2b10b2a3
@@ -0,0 +1 @@
86ce6f4d12e075e8ef4ba80b671783673a8a0443
@@ -0,0 +1 @@
4cd195abad8639ba6df345c125068058
@@ -0,0 +1 @@
e18f4c7a4b52d288733f123008d98985610da4f3
@@ -0,0 +1,13 @@
<?xml version='1.0' encoding='UTF-8'?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>sbt</groupId>
<artifactId>foo-lib</artifactId>
<packaging>jar</packaging>
<description>foo-lib</description>
<version>0.1.0</version>
<name>foo-lib</name>
<organization>
<name>sbt</name>
</organization>
</project>
@@ -0,0 +1 @@
22ebca3d8b32a4c0cb40341501b798e2
@@ -0,0 +1 @@
575258d8c9fecff7e8beefee2637c2928328e0dc
@@ -0,0 +1 @@
cc1a6b548cb9efd6e43c7fb5fa44728fc3f50d45
@@ -0,0 +1 @@
af0ac149e4c810884bb921bb3826e495faa5854b
@@ -0,0 +1 @@
fcadc4b2d6965cec0f5e5efb0a59226d
@@ -0,0 +1 @@
585422ad5196b83f83bbca644e62a39a2e4171e0
@@ -0,0 +1,13 @@
<?xml version='1.0' encoding='UTF-8'?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>sbt</groupId>
<artifactId>foo-lib</artifactId>
<packaging>jar</packaging>
<description>foo-lib</description>
<version>0.2.0</version>
<name>foo-lib</name>
<organization>
<name>sbt</name>
</organization>
</project>
@@ -0,0 +1 @@
790acd1d77316ff2c0310bb88d01dc72
@@ -0,0 +1 @@
94eec0571d936004eb5682b8b12268d1bcb2be40
@@ -0,0 +1 @@
f0fcc50b65e83f42210bbd3f366e2cc439177aa8
@@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>sbt</groupId>
<artifactId>transitive-lib</artifactId>
<packaging>jar</packaging>
<description>transitive-lib</description>
<version>0.1.0</version>
<name>transitive-lib</name>
<organization>
<name>sbt</name>
</organization>
<dependencies>
<dependency>
<groupId>sbt</groupId>
<artifactId>foo-lib</artifactId>
<version>0.1.0</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1 @@
4cb84c1daf7152544065849db0c1ce2d8d47c334
@@ -0,0 +1,15 @@
name := "transitive-lib"
organization := "sbt"
resolvers += "Local Maven" at file("../ivy").getCanonicalFile.toURI.toURL.toString
publishTo := Some(Resolver.file("test-resolver", file("..").getCanonicalFile / "ivy"))
version := "0.1.0"
libraryDependencies += "sbt" % "foo-lib" % "0.1.0"
crossPaths := false
autoScalaLibrary := false
@@ -0,0 +1,7 @@
package sbt.transitive;
public class Transitive {
public static int x() {
return sbt.foo.Foo.x();
}
}
@@ -0,0 +1,7 @@
package sbt
object TestMain {
def main(args: Array[String]) {
println(transitive.Transitive.x)
}
}
@@ -0,0 +1,11 @@
package sbt
import utest._
object MismatchedLibrariesTest extends TestSuite {
val tests: Tests = Tests {
'check - {
assert(foo.Foo.y == 3)
}
}
}
@@ -0,0 +1,15 @@
> set Test / layeringStrategy := LayeringStrategy.Full
> run
# This fails because the runtime layer includes an old version of the foo-lib library that doesn't
# have the sbt.foo.Foo.y method defined.
> test
> set Test / layeringStrategy := LayeringStrategy.TestDependencies
> run
> test
> test
@@ -0,0 +1,3 @@
val test = (project in file(".")).settings(
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test"
)
@@ -0,0 +1,9 @@
package sbt
import org.scalatest.{ FlatSpec, Matchers }
class ScalatestTest extends FlatSpec with Matchers {
"scalatest" should "fail" in {
1 shouldBe 2
}
}
@@ -0,0 +1,9 @@
package sbt
import org.scalatest.{ FlatSpec, Matchers }
class ScalatestTest extends FlatSpec with Matchers {
"scalatest" should "work" in {
1 shouldBe 1
}
}
@@ -0,0 +1,5 @@
> test
$ copy-file changes/ScalatestTest.scala src/test/scala/sbt/ScalatestTest.scala
-> test
@@ -0,0 +1,24 @@
import java.nio.file.Files
import java.nio.file.attribute.FileTime
import scala.collection.JavaConverters._
val rewriteIvy = inputKey[Unit]("Rewrite ivy directory")
val snapshot = (project in file(".")).settings(
name := "akka-test",
scalaVersion := "2.12.7",
libraryDependencies ++= Seq(
"com.lihaoyi" %% "utest" % "0.6.6" % "test"
),
testFrameworks += TestFramework("utest.runner.Framework"),
resolvers += "Local Maven" at file("ivy").toURI.toURL.toString,
libraryDependencies += "sbt" %% "foo-lib" % "0.1.0-SNAPSHOT",
rewriteIvy := {
val dir = Def.spaceDelimited().parsed.head
sbt.IO.delete(file("ivy"))
sbt.IO.copyDirectory(file(s"libraries/library-$dir/ivy"), file("ivy"))
Files.walk(file("ivy").getCanonicalFile.toPath).iterator.asScala.foreach { f =>
Files.setLastModifiedTime(f, FileTime.fromMillis(System.currentTimeMillis + 3000))
}
}
)
@@ -0,0 +1,5 @@
name := "foo-lib"
organization := "sbt"
publishTo := Some(Resolver.file("test-resolver", file("").getCanonicalFile / "ivy"))
@@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>sbt</groupId>
<artifactId>foo-lib_2.12</artifactId>
<packaging>jar</packaging>
<description>foo-lib</description>
<version>0.1.0-SNAPSHOT</version>
<name>foo-lib</name>
<organization>
<name>sbt</name>
</organization>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.12.7</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1 @@
sbt.version=1.2.6
@@ -0,0 +1,5 @@
package sbt
object Foo {
def x: Int = 1
}
@@ -0,0 +1,5 @@
name := "foo-lib"
organization := "sbt"
publishTo := Some(Resolver.file("test-resolver", file("").getCanonicalFile / "ivy"))

Some files were not shown because too many files have changed in this diff Show More