Skip to content

Commit

Permalink
java9-modularity#72: added test-project-mixed
Browse files Browse the repository at this point in the history
tests the "modularity" extension (incl. verifying generated class file formats)
  • Loading branch information
tlinkowski committed Mar 21, 2019
1 parent dd341b2 commit 3df9122
Show file tree
Hide file tree
Showing 40 changed files with 461 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

Expand Down Expand Up @@ -49,6 +50,44 @@ void smokeTest(String projectName) {
assertTasksSuccessful(result, "greeter.runner", "build", "run");
}

@Test
void smokeTestMixed() throws IOException {
var result = GradleRunner.create()
.withProjectDir(new File("test-project-mixed"))
.withPluginClasspath(pluginClasspath)
.withGradleVersion(GRADLE_VERSION)
.withArguments("-c", "smoke_test_settings.gradle", "clean", "build", "--stacktrace")
.forwardOutput()
.build();

verifyMixedTestResult(result, "greeter.api-jdk8", 8, 9);

verifyMixedTestResult(result, "greeter.provider-jdk8", 8, 9);
verifyMixedTestResult(result, "greeter.provider-jdk8.test-jdk8", 8, 9);
verifyMixedTestResult(result, "greeter.provider-jdk8.test-jdk11", 11, 11);

verifyMixedTestResult(result, "greeter.provider-jdk11", 11, 11);
verifyMixedTestResult(result, "greeter.provider-jdk11.test-jdk11", 11, 11);
}

private static void verifyMixedTestResult(
BuildResult result, String subprojectName,
int mainJavaRelease, int moduleInfoJavaRelease) throws IOException {
assertTasksSuccessful(result, subprojectName, "build");
assertExpectedClassFileFormats(subprojectName, mainJavaRelease, moduleInfoJavaRelease);
}

private static void assertExpectedClassFileFormats(
String subprojectName, int mainJavaRelease, int moduleInfoJavaRelease) throws IOException {
Path classesDir = Path.of("test-project-mixed").resolve(subprojectName).resolve("build/classes/java/main");

Path moduleInfoClassPath = classesDir.resolve("module-info.class");
SmokeTestHelper.assertClassFileJavaVersion(moduleInfoJavaRelease, moduleInfoClassPath);

Path nonModuleInfoClassPath = SmokeTestHelper.anyNonModuleInfoClassFilePath(classesDir);
SmokeTestHelper.assertClassFileJavaVersion(mainJavaRelease, nonModuleInfoClassPath);
}

@ParameterizedTest
@ValueSource(strings = "test-project")
void smokeTestDist(String projectName) {
Expand Down
37 changes: 37 additions & 0 deletions src/test/java/org/javamodularity/moduleplugin/SmokeTestHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.gradle.testkit.runner.TaskOutcome;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
Expand All @@ -19,6 +21,41 @@ static void assertTaskSuccessful(BuildResult result, String subprojectName, Stri
assertEquals(TaskOutcome.SUCCESS, task.getOutcome(), () -> fullTaskName + " failed!");
}

//region CLASS FILE FORMAT
static void assertClassFileJavaVersion(int expectedJavaVersion, Path classFilePath) throws IOException {
int actualJavaVersion = classFileJavaVersion(classFilePath);
assertEquals(expectedJavaVersion, actualJavaVersion, classFilePath::toString);
}

static Path anyNonModuleInfoClassFilePath(Path classesDir) throws IOException {
return Files.walk(classesDir)
.filter(SmokeTestHelper::isClassFile)
.filter(path -> !path.endsWith("module-info.class"))
.findAny()
.orElseThrow(() -> new IllegalStateException("Main class file not found in " + classesDir));
}

private static int classFileJavaVersion(Path classFilePath) throws IOException {
// https://en.wikipedia.org/wiki/Java_class_file#General_layout
return classFileFormat(classFilePath) - 44;
}

private static int classFileFormat(Path classFilePath) throws IOException {
if (!isClassFile(classFilePath)) {
throw new IllegalArgumentException(classFilePath.toString());
}

try (InputStream inputStream = Files.newInputStream(classFilePath)) {
// https://en.wikipedia.org/wiki/Java_class_file#General_layout
return inputStream.readNBytes(8)[7]; // 8th byte: major version number
}
}

private static boolean isClassFile(Path classFilePath) {
return classFilePath.toString().endsWith(".class");
}
//endregion

//region APP OUTPUT
static String getAppOutput(String binDirPath, String appName) {
boolean windows = System.getProperty("os.name").toLowerCase().contains("windows");
Expand Down
48 changes: 48 additions & 0 deletions test-project-mixed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Introduction
===

This mixed test project can be used as a standalone test project to verify the published plugin.
It is also used as an internal test project for testing unpublished plugin changes.

This project is "mixed" in two ways:

1. It produces classes targeting mixed JDKs (JDK 8 and JDK 11). The project makes use of the
[`ModularityExtension`](../src/main/java/org/javamodularity/moduleplugin/extensions/ModularityExtension.java),
which in turn makes use of the Java compiler
[`--release`](https://docs.oracle.com/en/java/javase/11/tools/javac.html) option.
2. It contains mixed build files: `build.gradle` (Groovy DSL) and `build.gradle.kts` (Kotlin DSL).

Standalone test product
===
To run this product as a standalone test product use this command (launched from `test-project-mixed` directory):
```
../gradlew clean build
```

It will use the most recent plugin version from Gradle maven repository to compile the test project with
modules and run the unit tests.

Testing locally published plugin
===

You can publish the plugin locally by running this command from the root directory:

`./gradlew publishToMavenLocal`

You can test the locally published plugin by running the following command from `test-project-mixed` directory.

`../gradlew -c local_maven_settings.gradle clean build`

It will use the latest locally published version of the plugin to compile the test project with
modules and run the unit tests.


Internal test project
===

This mode is enabled in `ModulePluginSmokeTest` by passing an extra parameter (`-c smoke_test_settings.gradle`).
`smoke_test_settings.gradle` script configures plugin management so that the plugin cannot be resolved from
a Gradle plugin repository. Instead, it relies on the smoke test to make the plugin under development available
to the test project by sharing a classpath (using Gradle TestKit).

__CAUTION:__ This approach won't work outside of the smoke test, it will break the build because the plugin jar won't be resolved.
28 changes: 28 additions & 0 deletions test-project-mixed/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
id 'org.javamodularity.moduleplugin' version '1.4.1' apply false
}

subprojects {
apply plugin: 'java'
apply plugin: 'org.javamodularity.moduleplugin'

repositories {
mavenCentral()
}

test {
useJUnitPlatform()

testLogging {
events 'PASSED', 'FAILED', 'SKIPPED'
}
}

dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:$jUnitVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$jUnitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$jUnitVersion"
}

build.dependsOn javadoc
}
1 change: 1 addition & 0 deletions test-project-mixed/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jUnitVersion = 5.3.1
17 changes: 17 additions & 0 deletions test-project-mixed/gradle/shared/greeter.provider.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
dependencies {
implementation project(':greeter.api-jdk8')
testImplementation('org.hamcrest:hamcrest:2.1+')

compile "javax.annotation:javax.annotation-api:1.3.2"
compile "com.google.code.findbugs:jsr305:3.0.2"
}

patchModules.config = [
"java.annotation=jsr305-3.0.2.jar"
]

javadoc {
moduleOptions {
addModules = ['java.sql']
}
}
7 changes: 7 additions & 0 deletions test-project-mixed/gradle/shared/greeter.provider.test.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencies {
implementation project(':greeter.api-jdk8')
}

patchModules.config = [
"java.annotation=jsr305-3.0.2.jar"
]
1 change: 1 addition & 0 deletions test-project-mixed/greeter.api-jdk8/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
modularity.mixedJavaRelease(8)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package examples.greeter.api;

public interface Greeter {
String hello();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module greeter.api {
exports examples.greeter.api;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
modularity.standardJavaRelease(11)

dependencies {
runtimeOnly(project(":greeter.provider-jdk11"))
}

apply(from = "$rootDir/gradle/shared/greeter.provider.test.gradle")
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import examples.greeter.api.Greeter;

module greeter.provider.test {
requires greeter.api;

uses Greeter;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package tests;

import examples.greeter.api.Greeter;

import java.util.ServiceLoader;

public class GreeterLocator {
public Greeter findGreeter() {
return ServiceLoader.load(Greeter.class).findFirst().orElseThrow(() -> new RuntimeException("No Greeter found"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package tests;

import examples.greeter.api.Greeter;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;

class GreeterTest {
@Test
void testLocate() {
Greeter greeter = new GreeterLocator().findGreeter();
assertFalse(greeter.hello().isBlank());
}
}
3 changes: 3 additions & 0 deletions test-project-mixed/greeter.provider-jdk11/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
modularity.standardJavaRelease(11)

apply(from = "$rootDir/gradle/shared/greeter.provider.gradle")
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package examples.greeter;

import examples.greeter.api.Greeter;
import java.io.*;
import java.util.*;
import javax.annotation.Nonnull;

public class Friendly implements Greeter {
@Override @Nonnull
public String hello() {
var stream = this.getClass().getResourceAsStream("/greeting.txt");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "utf-8"))) {
return reader.readLine();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import examples.greeter.api.Greeter;

module greeter.provider {
requires greeter.api;
requires java.annotation;

provides Greeter with examples.greeter.Friendly;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
welcome
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package examples.greeter;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class FriendlyTest {
@Test
void testGreeting() {
String greeting = new Friendly().hello();
assertTrue(greeting.contains("welcome"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package examples.greeter;

import javax.script.*;
import org.junit.jupiter.api.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

class ScriptingTest {

@Test
void testScripting() {
ScriptEngineManager manager = new ScriptEngineManager();
assertThat(manager.getEngineFactories(), not(nullValue()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// make module visible
--add-modules
java.scripting,org.hamcrest

// "requires java.scripting"
--add-reads
greeter.provider=java.scripting,org.hamcrest
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
modularity.standardJavaRelease 11

dependencies {
runtimeOnly project(':greeter.provider-jdk8')
}

apply from: "$rootDir/gradle/shared/greeter.provider.test.gradle"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import examples.greeter.api.Greeter;

module greeter.provider.test {
requires greeter.api;

uses Greeter;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package tests;

import examples.greeter.api.Greeter;

import java.util.ServiceLoader;

public class GreeterLocator {
public Greeter findGreeter() {
return ServiceLoader.load(Greeter.class).findFirst().orElseThrow(() -> new RuntimeException("No Greeter found"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package tests;

import examples.greeter.api.Greeter;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;

class GreeterTest {
@Test
void testLocate() {
Greeter greeter = new GreeterLocator().findGreeter();
assertFalse(greeter.hello().isBlank());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
modularity.mixedJavaRelease 8

dependencies {
runtimeOnly project(':greeter.provider-jdk8')
}

apply from: "$rootDir/gradle/shared/greeter.provider.test.gradle"

0 comments on commit 3df9122

Please sign in to comment.