You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by js...@apache.org on 2017/08/10 16:28:50 UTC
geode git commit: GEODE-3313: Test utility supports building jar
files with multiple classes
Repository: geode
Updated Branches:
refs/heads/develop 1efbf58f3 -> 06b839c2b
GEODE-3313: Test utility supports building jar files with multiple classes
Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/06b839c2
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/06b839c2
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/06b839c2
Branch: refs/heads/develop
Commit: 06b839c2b458c47eb26f42de65ca82947b8988c2
Parents: 1efbf58
Author: Jared Stewart <js...@pivotal.io>
Authored: Tue Aug 8 10:11:07 2017 -0700
Committer: Jared Stewart <js...@pivotal.io>
Committed: Thu Aug 10 09:27:39 2017 -0700
----------------------------------------------------------------------
geode-junit/build.gradle | 6 +-
.../geode/test/compiler/ClassNameExtractor.java | 33 +++++
.../geode/test/compiler/CompiledSourceCode.java | 35 +++++
.../apache/geode/test/compiler/JarBuilder.java | 122 ++++++++++++++++++
.../geode/test/compiler/JavaCompiler.java | 123 ++++++++++++++++++
.../test/compiler/UncompiledSourceCode.java | 71 +++++++++++
.../test/compiler/ClassNameExtractorTest.java | 54 ++++++++
.../geode/test/compiler/JarBuilderTest.java | 127 +++++++++++++++++++
.../geode/test/compiler/JavaCompilerTest.java | 79 ++++++++++++
.../test/compiler/UncompiledSourceCodeTest.java | 43 +++++++
.../geode/test/compiler/AbstractClass.java | 18 +++
.../geode/test/compiler/ConcreteClass.java | 19 +++
12 files changed, 729 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/build.gradle
----------------------------------------------------------------------
diff --git a/geode-junit/build.gradle b/geode-junit/build.gradle
index e47095f..7c533ad 100755
--- a/geode-junit/build.gradle
+++ b/geode-junit/build.gradle
@@ -19,7 +19,11 @@ dependencies {
compile 'com.jayway.jsonpath:json-path:' + project.'json-path.version'
testCompile 'commons-lang:commons-lang:' + project.'commons-lang.version'
testCompile 'com.google.guava:guava:' + project.'guava.version'
- testCompile 'org.assertj:assertj-core:' + project.'assertj-core.version'
+ compile 'org.assertj:assertj-core:' + project.'assertj-core.version'
+ compile 'commons-io:commons-io:' + project.'commons-io.version'
+ compile 'commons-lang:commons-lang:' + project.'commons-lang.version'
+ compile 'com.google.guava:guava:' + project.'guava.version'
+
compile('junit:junit:' + project.'junit.version') {
exclude module: 'hamcrest-core'
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassNameExtractor.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassNameExtractor.java b/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassNameExtractor.java
new file mode 100644
index 0000000..c48224c
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassNameExtractor.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ClassNameExtractor {
+ private static final Pattern EXTRACT_CLASS_NAME_REGEX =
+ Pattern.compile("(?:public|private|protected)* *(?:abstract)* *(?:class|interface) +(\\w+)");
+
+ public String extractFromSourceCode(String sourceCode) {
+ Matcher m = EXTRACT_CLASS_NAME_REGEX.matcher(sourceCode);
+ if (m.find()) {
+ return m.group(1);
+ } else {
+ throw new IllegalArgumentException(
+ String.format("Unable to parse class or interface name from: \n'%s'", sourceCode));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/main/java/org/apache/geode/test/compiler/CompiledSourceCode.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/main/java/org/apache/geode/test/compiler/CompiledSourceCode.java b/geode-junit/src/main/java/org/apache/geode/test/compiler/CompiledSourceCode.java
new file mode 100644
index 0000000..c1bfa3d
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/test/compiler/CompiledSourceCode.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+public class CompiledSourceCode {
+
+ /**
+ * Fully qualified classname in a format suitable for Class.forName
+ */
+ public String className;
+
+ public byte[] compiledBytecode;
+
+ public CompiledSourceCode(String className, byte[] aBytes) {
+ this.className = className.replace('/', '.');
+ this.compiledBytecode = aBytes;
+ }
+
+ @Override
+ public String toString() {
+ return className;
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/main/java/org/apache/geode/test/compiler/JarBuilder.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/main/java/org/apache/geode/test/compiler/JarBuilder.java b/geode-junit/src/main/java/org/apache/geode/test/compiler/JarBuilder.java
new file mode 100644
index 0000000..beea476
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/test/compiler/JarBuilder.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+import org.assertj.core.api.Assertions;
+
+
+/**
+ * This class accepts java source code in the format of .java source files or strings containing the
+ * contents of .java source files, and compiles the given source code into a jar file specified by
+ * the user.
+ *
+ * <p>
+ * Example of usage:
+ *
+ * <pre>
+ * @Rule
+ * public TemporaryFolder temporaryFolder= new TemporaryFolder();
+ *
+ * @Test
+ * public void buildJarUsingStrings() {
+ * File tempDir = temporaryFolder.getRoot()
+ * JarBuilder jarBuilder = new JarBuilder(tempDir);
+ * File outputJar = new File("output.jar");
+ *
+ * String classInFooBarPackage = "package foo.bar; public class ClassA {int n = 10;}";
+ * String classInDefaultPackage = "public class ClassB {}";
+ * jarBuilder.buildJar(outputJar, classInFooBarPackage, classInDefaultPackage);
+ * }
+ *
+ * @Test
+ * public void buildJarUsingFiles() {
+ * File tempDir = temporaryFolder.getRoot()
+ * JarBuilder jarBuilder = new JarBuilder(tempDir);
+ * File outputJar = new File("output.jar");
+ *
+ * File sourceFileOne = new File("ClassA.java");
+ * File sourceFileTwo = new File("ClassB.java");
+ * jarBuilder.buildJar(outputJar, sourceFileOne, sourceFileTwo);
+ * }
+ *
+ * @Test
+ * public void buildJarUsingClassNames() {
+ * File tempDir = temporaryFolder.getRoot()
+ * JarBuilder jarBuilder = new JarBuilder(tempDir);
+ * File outputJar = new File("output.jar");
+ *
+ * String classInFooBarPackage = "foo.bar.ClassInFooBarPackage";
+ * String classInDefaultPackage = "ClassInDefaultPackage";
+ * jarBuilder.buildJar(outputJar, classInFooBarPackage, classInDefaultPackage);
+ * }
+ * </pre>
+ **/
+public class JarBuilder {
+ private final JavaCompiler javaCompiler = new JavaCompiler();
+
+ public void buildJarFromClassNames(File outputJarFile, String... classNames) throws IOException {
+ UncompiledSourceCode[] uncompiledSourceCodes = Arrays.stream(classNames)
+ .map(UncompiledSourceCode::fromClassName).toArray(UncompiledSourceCode[]::new);
+
+ List<CompiledSourceCode> compiledSourceCodes = javaCompiler.compile(uncompiledSourceCodes);
+
+ buildJar(outputJarFile, compiledSourceCodes);
+ }
+
+ public void buildJar(File outputJarFile, String... sourceFileContents) throws IOException {
+ List<CompiledSourceCode> compiledSourceCodes = javaCompiler.compile(sourceFileContents);
+
+ buildJar(outputJarFile, compiledSourceCodes);
+ }
+
+ public void buildJar(File outputJarFile, File... sourceFiles) throws IOException {
+ List<CompiledSourceCode> compiledSourceCodes = javaCompiler.compile(sourceFiles);
+
+ buildJar(outputJarFile, compiledSourceCodes);
+ }
+
+ private void buildJar(File outputJarFile, List<CompiledSourceCode> compiledSourceCodes)
+ throws IOException {
+ assertThat(outputJarFile).doesNotExist();
+
+ try (FileOutputStream outputStream = new FileOutputStream(outputJarFile)) {
+ JarOutputStream jarOutputStream = new JarOutputStream(outputStream);
+ for (CompiledSourceCode compiledSource : compiledSourceCodes) {
+
+ String formattedName = compiledSource.className.replace(".", "/");
+ if (!formattedName.endsWith(".class")) {
+ formattedName = formattedName.concat(".class");
+ }
+
+ JarEntry entry = new JarEntry(formattedName);
+ entry.setTime(System.currentTimeMillis());
+ jarOutputStream.putNextEntry(entry);
+ jarOutputStream.write(compiledSource.compiledBytecode);
+ jarOutputStream.closeEntry();
+ }
+ jarOutputStream.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/main/java/org/apache/geode/test/compiler/JavaCompiler.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/main/java/org/apache/geode/test/compiler/JavaCompiler.java b/geode-junit/src/main/java/org/apache/geode/test/compiler/JavaCompiler.java
new file mode 100644
index 0000000..8449605
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/test/compiler/JavaCompiler.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import static java.util.stream.Collectors.toList;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+import javax.tools.ToolProvider;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.apache.commons.io.FileUtils;
+
+
+public class JavaCompiler {
+ private File tempDir;
+
+ public JavaCompiler() {
+ this.tempDir = Files.createTempDir();
+ tempDir.deleteOnExit();
+ }
+
+ public List<CompiledSourceCode> compile(File... sourceFiles) throws IOException {
+ String[] sourceFileContents =
+ Arrays.stream(sourceFiles).map(this::readFileToString).toArray(String[]::new);
+
+ return compile(sourceFileContents);
+ }
+
+ public List<CompiledSourceCode> compile(String... sourceFileContents) throws IOException {
+ UncompiledSourceCode[] uncompiledSourceCodes = Arrays.stream(sourceFileContents)
+ .map(UncompiledSourceCode::fromSourceCode).toArray(UncompiledSourceCode[]::new);
+
+ return compile(uncompiledSourceCodes);
+ }
+
+ public List<CompiledSourceCode> compile(UncompiledSourceCode... uncompiledSources)
+ throws IOException {
+ File temporarySourcesDirectory = createSubdirectory(tempDir, "sources");
+ File temporaryClassesDirectory = createSubdirectory(tempDir, "classes");
+
+ List<String> options = Stream.of("-d", temporaryClassesDirectory.getAbsolutePath(),
+ "-classpath", System.getProperty("java.class.path")).collect(toList());
+
+ try {
+ for (UncompiledSourceCode sourceCode : uncompiledSources) {
+ File sourceFile = new File(temporarySourcesDirectory, sourceCode.simpleClassName + ".java");
+ FileUtils.writeStringToFile(sourceFile, sourceCode.sourceCode, Charsets.UTF_8);
+ options.add(sourceFile.getAbsolutePath());
+ }
+
+ int exitCode = ToolProvider.getSystemJavaCompiler().run(System.in, System.out, System.err,
+ options.toArray(new String[] {}));
+
+ if (exitCode != 0) {
+ throw new RuntimeException(
+ "Unable to compile the given source code. See System.err for details.");
+ }
+
+ List<CompiledSourceCode> compiledSourceCodes = new ArrayList<>();
+ addCompiledClasses(compiledSourceCodes, "", temporaryClassesDirectory);
+ return compiledSourceCodes;
+ } finally {
+ FileUtils.deleteDirectory(temporaryClassesDirectory);
+ }
+ }
+
+ private static void addCompiledClasses(List<CompiledSourceCode> ret, String pkgName, File dir)
+ throws IOException {
+ for (File file : dir.listFiles()) {
+ String filename = file.getName();
+
+ if (file.isDirectory()) {
+ String qname = pkgName + filename + ".";
+ addCompiledClasses(ret, qname, file);
+ } else if (filename.endsWith(".class")) {
+ String qname = pkgName + filename.substring(0, filename.length() - 6);
+ ret.add(new CompiledSourceCode(qname, FileUtils.readFileToByteArray(file)));
+ } else {
+ System.err.println("Unexpected file : " + file.getAbsolutePath());
+ }
+ }
+ }
+
+ private File createSubdirectory(File parent, String directoryName) {
+ File subdirectory = parent.toPath().resolve(directoryName).toFile();
+ if (!subdirectory.exists()) {
+ subdirectory.mkdirs();
+ }
+
+ if (!subdirectory.exists() || !subdirectory.isDirectory()) {
+ throw new IllegalArgumentException("Invalid directory" + subdirectory.getAbsolutePath());
+ }
+
+ return subdirectory;
+ }
+
+ private String readFileToString(File file) {
+ try {
+ return FileUtils.readFileToString(file, Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/main/java/org/apache/geode/test/compiler/UncompiledSourceCode.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/main/java/org/apache/geode/test/compiler/UncompiledSourceCode.java b/geode-junit/src/main/java/org/apache/geode/test/compiler/UncompiledSourceCode.java
new file mode 100644
index 0000000..4161f5e
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/test/compiler/UncompiledSourceCode.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.SystemUtils;
+
+public class UncompiledSourceCode {
+ public String simpleClassName;
+ public String sourceCode;
+
+ private UncompiledSourceCode(String simpleClassName, String sourceCode) {
+ this.simpleClassName = simpleClassName;
+ this.sourceCode = sourceCode;
+ }
+
+ public static UncompiledSourceCode fromSourceCode(String sourceCode) {
+ String simpleClassName = new ClassNameExtractor().extractFromSourceCode(sourceCode);
+ return new UncompiledSourceCode(simpleClassName, sourceCode);
+ }
+
+ public static UncompiledSourceCode fromClassName(String fullyQualifiedClassName) {
+ ClassNameWithPackage classNameWithPackage = ClassNameWithPackage.of(fullyQualifiedClassName);
+ boolean isPackageSpecified = StringUtils.isNotBlank(classNameWithPackage.packageName);
+
+ StringBuilder sourceCode = new StringBuilder();
+ if (isPackageSpecified) {
+ sourceCode.append(String.format("package %s;", classNameWithPackage.packageName));
+ sourceCode.append(SystemUtils.LINE_SEPARATOR);
+ }
+
+ sourceCode.append(String.format("public class %s {}", classNameWithPackage.simpleClassName));
+
+ return new UncompiledSourceCode(classNameWithPackage.simpleClassName, sourceCode.toString());
+ }
+
+ private static class ClassNameWithPackage {
+ String packageName;
+ String simpleClassName;
+
+ static ClassNameWithPackage of(String fqClassName) {
+ int indexOfLastDot = fqClassName.lastIndexOf(".");
+
+ if (indexOfLastDot == -1) {
+ return new ClassNameWithPackage("", fqClassName);
+ } else {
+ String specifiedPackage = fqClassName.substring(0, indexOfLastDot);
+ String simpleClassName = fqClassName.substring(indexOfLastDot + 1);
+
+ return new ClassNameWithPackage(specifiedPackage, simpleClassName);
+ }
+ }
+
+ private ClassNameWithPackage(String packageName, String simpleClassName) {
+ this.packageName = packageName;
+ this.simpleClassName = simpleClassName;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/test/java/org/apache/geode/test/compiler/ClassNameExtractorTest.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/test/java/org/apache/geode/test/compiler/ClassNameExtractorTest.java b/geode-junit/src/test/java/org/apache/geode/test/compiler/ClassNameExtractorTest.java
new file mode 100644
index 0000000..db060f8
--- /dev/null
+++ b/geode-junit/src/test/java/org/apache/geode/test/compiler/ClassNameExtractorTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class ClassNameExtractorTest {
+ private static final String SPACE = " ";
+ private static final String CLASS_NAME_TO_FIND = "MyClassNameToFind";
+
+ @Test
+ public void extractsClassNames() throws Exception {
+ SoftAssertions softAssertions = new SoftAssertions();
+ ClassNameExtractor classNameExtractor = new ClassNameExtractor();
+
+ Set<List<String>> permutationsToTest = Sets.cartesianProduct(
+ ImmutableSet.of("public ", "private ", "protected ", ""), ImmutableSet.of("abstract ", ""),
+ ImmutableSet.of("static ", ""), ImmutableSet.of("class ", "interface "),
+ ImmutableSet.of("extends Foo ", ""), ImmutableSet.of("implements Bar ", ""));
+
+ for (List<String> permutation : permutationsToTest) {
+ String firstLineOfSource =
+ permutation.get(0) + permutation.get(1) + permutation.get(2) + permutation.get(3)
+ + CLASS_NAME_TO_FIND + SPACE + permutation.get(4) + permutation.get(5) + " {";
+
+ String className = classNameExtractor.extractFromSourceCode(firstLineOfSource);
+ softAssertions.assertThat(className).isEqualTo(CLASS_NAME_TO_FIND);
+ }
+
+ softAssertions.assertAll();
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/test/java/org/apache/geode/test/compiler/JarBuilderTest.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/test/java/org/apache/geode/test/compiler/JarBuilderTest.java b/geode-junit/src/test/java/org/apache/geode/test/compiler/JarBuilderTest.java
new file mode 100644
index 0000000..58adc02
--- /dev/null
+++ b/geode-junit/src/test/java/org/apache/geode/test/compiler/JarBuilderTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import static java.util.stream.Collectors.toSet;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.test.junit.categories.IntegrationTest;
+
+@Category(IntegrationTest.class)
+public class JarBuilderTest {
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private JarBuilder jarBuilder;
+ private File outputJar;
+
+ @Before
+ public void setup() {
+ jarBuilder = new JarBuilder();
+ outputJar = new File(temporaryFolder.getRoot(), "output.jar");
+ }
+
+ @Test
+ public void jarWithSingleClass() throws Exception {
+ File classContents = loadTestResource("AbstractClass.java");
+ jarBuilder.buildJar(outputJar, classContents);
+
+ Set<String> jarEntryNames = jarEntryNamesFromFile(outputJar);
+ assertThat(jarEntryNames)
+ .containsExactlyInAnyOrder("org/apache/geode/test/compiler/AbstractClass.class");
+ }
+
+ @Test
+ public void jarWithTwoDependentClasses() throws Exception {
+ File sourceFileOne = loadTestResource("AbstractClass.java");
+ File sourceFileTwo = loadTestResource("ConcreteClass.java");
+
+ jarBuilder.buildJar(outputJar, sourceFileOne, sourceFileTwo);
+
+ Set<String> jarEntryNames = jarEntryNamesFromFile(outputJar);
+
+ assertThat(jarEntryNames).containsExactlyInAnyOrder(
+ "org/apache/geode/test/compiler/AbstractClass.class",
+ "org/apache/geode/test/compiler/ConcreteClass.class");
+ }
+
+ @Test
+ public void jarWithClassInDefaultPackage() throws Exception {
+ String classInFooBarPackage = "package foo.bar; public class ClassInFooBarPackage {}";
+ String classInDefaultPackage = "public class ClassInDefaultPackage {}";
+ jarBuilder.buildJar(outputJar, classInFooBarPackage, classInDefaultPackage);
+
+ Set<String> jarEntryNames = jarEntryNamesFromFile(outputJar);
+ assertThat(jarEntryNames).containsExactlyInAnyOrder("ClassInDefaultPackage.class",
+ "foo/bar/ClassInFooBarPackage.class");
+ }
+
+
+ @Test
+ public void jarFromOnlyClassNames() throws Exception {
+ String defaultPackageClassName = "DefaultClass";
+ String otherPackageClassName = "foo.bar.OtherClass";
+ jarBuilder.buildJarFromClassNames(outputJar, defaultPackageClassName, otherPackageClassName);
+
+ Set<String> jarEntryNames = jarEntryNamesFromFile(outputJar);
+ assertThat(jarEntryNames).containsExactlyInAnyOrder("DefaultClass.class",
+ "foo/bar/OtherClass.class");
+ }
+
+ @Test
+ public void canLoadClassesFromJar() throws Exception {
+ String defaultPackageClassName = "DefaultClass";
+ String otherPackageClassName = "foo.bar.OtherClass";
+ jarBuilder.buildJarFromClassNames(outputJar, defaultPackageClassName, otherPackageClassName);
+
+ URLClassLoader jarClassLoader = new URLClassLoader(new URL[] {outputJar.toURL()});
+
+ jarClassLoader.loadClass("DefaultClass");
+ jarClassLoader.loadClass("foo.bar.OtherClass");
+ }
+
+ private Set<String> jarEntryNamesFromFile(File jarFile) throws Exception {
+ assertThat(jarFile).exists();
+
+ Enumeration<JarEntry> jarEntries = new JarFile(jarFile).entries();
+ return Collections.list(jarEntries).stream().map(JarEntry::getName).collect(toSet());
+ }
+
+ private File loadTestResource(String fileName) throws URISyntaxException {
+ URL resourceFileURL = this.getClass().getResource(fileName);
+ assertThat(resourceFileURL).isNotNull();
+
+ URI resourceUri = resourceFileURL.toURI();
+ return new File(resourceUri);
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/test/java/org/apache/geode/test/compiler/JavaCompilerTest.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/test/java/org/apache/geode/test/compiler/JavaCompilerTest.java b/geode-junit/src/test/java/org/apache/geode/test/compiler/JavaCompilerTest.java
new file mode 100644
index 0000000..425169d
--- /dev/null
+++ b/geode-junit/src/test/java/org/apache/geode/test/compiler/JavaCompilerTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.assertj.core.api.Assertions;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.test.junit.categories.IntegrationTest;
+
+@Category(IntegrationTest.class)
+public class JavaCompilerTest {
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Test
+ public void compileSingleClass() throws Exception {
+ File implementsFunctionSourceFile = getFileFromTestResources("AbstractClass.java");
+ String classContents = FileUtils.readFileToString(implementsFunctionSourceFile, "UTF-8");
+
+ List<CompiledSourceCode> compiledSourceCodes = new JavaCompiler().compile(classContents);
+
+ assertThat(compiledSourceCodes).hasSize(1);
+ }
+
+ @Test
+ public void compileTwoDependentClasses() throws Exception {
+ File sourceFileOne = getFileFromTestResources("AbstractClass.java");
+ File sourceFileTwo = getFileFromTestResources("ConcreteClass.java");
+
+ List<CompiledSourceCode> compiledSourceCodes =
+ new JavaCompiler().compile(sourceFileOne, sourceFileTwo);
+
+ assertThat(compiledSourceCodes).hasSize(2);
+ }
+
+ @Test
+ public void invalidSourceThrowsException() throws Exception {
+ JavaCompiler javaCompiler = new JavaCompiler();
+ String sourceCode = "public class foo {this is not valid java source code}";
+ assertThatThrownBy(() -> javaCompiler.compile(sourceCode)).isInstanceOf(Exception.class);
+ }
+
+ private File getFileFromTestResources(String fileName) throws URISyntaxException {
+ URL resourceFileURL = this.getClass().getResource(fileName);
+ assertThat(resourceFileURL).isNotNull();
+
+ URI resourceUri = resourceFileURL.toURI();
+ File file = new File(resourceUri);
+
+ assertThat(file).exists();
+ return file;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/test/java/org/apache/geode/test/compiler/UncompiledSourceCodeTest.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/test/java/org/apache/geode/test/compiler/UncompiledSourceCodeTest.java b/geode-junit/src/test/java/org/apache/geode/test/compiler/UncompiledSourceCodeTest.java
new file mode 100644
index 0000000..3db8a72
--- /dev/null
+++ b/geode-junit/src/test/java/org/apache/geode/test/compiler/UncompiledSourceCodeTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.apache.commons.lang.SystemUtils;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class UncompiledSourceCodeTest {
+ @Test
+ public void fromClassNameWithNoPackage() throws Exception {
+ UncompiledSourceCode uncompiledSourceCode = UncompiledSourceCode.fromClassName("NoPackage");
+ assertThat(uncompiledSourceCode.simpleClassName).isEqualTo("NoPackage");
+ assertThat(uncompiledSourceCode.sourceCode).isEqualTo("public class NoPackage {}");
+ }
+
+ @Test
+ public void fromClassNameWithPackage() throws Exception {
+ UncompiledSourceCode uncompiledSourceCode =
+ UncompiledSourceCode.fromClassName("foo.bar.ClassName");
+ assertThat(uncompiledSourceCode.simpleClassName).isEqualTo("ClassName");
+ assertThat(uncompiledSourceCode.sourceCode)
+ .isEqualTo("package foo.bar;" + SystemUtils.LINE_SEPARATOR + "public class ClassName {}");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/test/resources/org/apache/geode/test/compiler/AbstractClass.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/test/resources/org/apache/geode/test/compiler/AbstractClass.java b/geode-junit/src/test/resources/org/apache/geode/test/compiler/AbstractClass.java
new file mode 100644
index 0000000..24ed0c6
--- /dev/null
+++ b/geode-junit/src/test/resources/org/apache/geode/test/compiler/AbstractClass.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+public abstract class AbstractClass {
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/06b839c2/geode-junit/src/test/resources/org/apache/geode/test/compiler/ConcreteClass.java
----------------------------------------------------------------------
diff --git a/geode-junit/src/test/resources/org/apache/geode/test/compiler/ConcreteClass.java b/geode-junit/src/test/resources/org/apache/geode/test/compiler/ConcreteClass.java
new file mode 100644
index 0000000..bf98672
--- /dev/null
+++ b/geode-junit/src/test/resources/org/apache/geode/test/compiler/ConcreteClass.java
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.compiler;
+
+
+public class ConcreteClass extends AbstractClass {
+}