Skip to content

Commit

Permalink
Merge branch 'donbot-tests' into donbot
Browse files Browse the repository at this point in the history
* donbot-tests:
  Test clean-up
  Cleaned up and documented Gradle+JUnit+OSGi
  Committing RC of Gradle+JUnit+MD OSGi. Intentionally before clean-up for historical record of attempts.
  Initial method clearing, tab -> space conversion. MD_Tests_JUnit4 runs
  initial test dump
  • Loading branch information
Gomes, Ivan (397G) authored and Gomes, Ivan (397G) committed Nov 2, 2016
2 parents 381c094 + 1d06cb2 commit f45e7bc
Show file tree
Hide file tree
Showing 35 changed files with 5,247 additions and 3 deletions.
64 changes: 61 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ dependencies {
compile fileTree(dir: 'lib', include: ['*.jar'])

// Test Dependencies
testCompile 'junit:junit:4.10'
//testCompile 'junit:junit:4.11'
//compile 'junit:junit:4.11'
}

task extractDependencies {
Expand Down Expand Up @@ -216,6 +217,30 @@ task sourcesJar(type: Jar, dependsOn: classes) {
from sourceSets.main.allSource
}

task testsJar(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output

exclude 'gov/nasa/jpl/mbee/mdk/test/framework/**'
exclude '**/org/gradle/**'
include 'gov/nasa/jpl/mbee/mdk/test/tests/**'
}

task testsHackJar(type: Jar, dependsOn: testClasses) {
classifier = 'tests-hack'
from sourceSets.test.output

include 'gov/nasa/jpl/mbee/mdk/test/framework/**'
include '**/org/gradle/**'
exclude 'gov/nasa/jpl/mbee/mdk/test/tests/**'

manifest {
attributes(
'Main-Class': 'gov.nasa.jpl.mbee.mdk.test.framework.GradleMagicDrawLauncher'
)
}
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
Expand All @@ -235,6 +260,9 @@ publishing {
artifact sourcesJar {
classifier "sources"
}
artifact testsJar {
classifier "tests"
}
artifact javadocJar {
classifier "javadoc"
}
Expand Down Expand Up @@ -290,7 +318,7 @@ task runJava(type: JavaExec) {
errorOutput = System.err

main = 'com.nomagic.osgi.launcher.ProductionFrameworkLauncher'
jvmArgs = ['-Xmx4000M', '-DLOCALCONFIG=true', '-DWINCONFIG=true', '-Djsse.enableSNIExtension=false', '-Djava.net.preferIPv4Stack=true', '-Dcom.sun.media.imageio.disableCodecLib=true', '-Xss1024K', '-noverify', '-Dlocal.config.dir.ext=-dev', '-splash:data/splash.png', '-Dmd.class.path=$java.class.path', '-Dcom.nomagic.osgi.config.dir=configuration', '-Desi.system.config=data/application.conf', '-Dlogback.configurationFile=data/logback.xml', '-Dsun.locale.formatasdefault=true']
jvmArgs = ['-Xmx4000M', '-Xss1024M', '-DLOCALCONFIG=true', '-DWINCONFIG=true', '-Djsse.enableSNIExtension=false', '-Djava.net.preferIPv4Stack=true', '-Dcom.sun.media.imageio.disableCodecLib=true', '-noverify', '-Dlocal.config.dir.ext=-dev', '-splash:data/splash.png', '-Dmd.class.path=$java.class.path', '-Dcom.nomagic.osgi.config.dir=configuration', '-Desi.system.config=data/application.conf', '-Dlogback.configurationFile=data/logback.xml', '-Dsun.locale.formatasdefault=true']
// arguments to pass to the application
args 'DEVELOPER'
}
Expand All @@ -309,4 +337,34 @@ task runScript(type: Exec) {
}
}

runScript.dependsOn installDist
runScript.dependsOn installDist

test {
testLogging.showStandardStreams = true
forkEvery 1

workingDir 'build/install'

/**
* The short version of the rationale is that with 18.4, MagicDraw introduced the OSGi framework as part of its implementation.
* Gradle and JUnit work very well together out of the box (generated reports in XML and HTML, tight IDE/Jenkins integration, etc.), and Gradle and OSGi was made to work with the runJava task.
* However, Gradle had to implement a number of hacks to overcome OS limitations and achieve features that can be applied to a broad range of software.
* Those hacks work well with most applications but not OSGi (commentary: OSGi doesn't play well with anyone), so we implemented a number of hacks ourselves.
* They involve classloading order, classpath redirection using the jar argument, reflection, and dynamic classloading.
* Ultimately, this enabled the use of the well-supported and feature-rich Gradle test task, but at the cost of an implementation that could break in the unlikely, albeit possible, case of either Gradle or MagicDraw significantly changing their implementations of the test and OSGi frameworks, respectively.
*
* Clears the default classpath since all the necessary libraries are being passed as arguments to GradleMagicDrawLauncher to be added to the OSGi classpath.
* Then it utilizes the -jar JVM argument to override the default Gradle command and pass the original command as arguments to the main class of the jar, GradleMagicDrawLauncher,
* including the classpath arguments (one of our own and one of Gradle's). It additionally parses and sets the system properties that are passed.
*
* Example command: .../java -Dcom.nomagic.osgi.config.dir=configuration -Desi.system.config=data/application.conf -Djava.security.manager=worker.org.gradle.process.internal.worker.child.BootstrapSecurityManager -Dlogback.configurationFile=data/logback.xml -Dmd.class.path=$java.class.path -jar /Users/igomes/mdk/build/libs/mdk-*-tests-hack.jar -cp ... -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -ea -cp .../.gradle/caches/.../workerMain/gradle-worker.jar worker.org.gradle.process.internal.worker.GradleWorkerMain 'Gradle Test Executor 1'
*
* @author igomes
*/
classpath = files()
jvmArgs = ['-jar', testsHackJar.outputs.files.singleFile, '-cp', fileTree(dir: 'build/install', include: ['lib/**/*.jar']).asPath + File.pathSeparator + testsJar.outputs.files.singleFile, '-Dmd.class.path=$java.class.path', '-Dcom.nomagic.osgi.config.dir=configuration', '-Desi.system.config=data/application.conf', '-Dlogback.configurationFile=data/logback.xml']
}

test.dependsOn testsJar
test.dependsOn testsHackJar
test.dependsOn installDist
18 changes: 18 additions & 0 deletions src/main/java/gov/nasa/jpl/mbee/mdk/api/MagicDrawHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,24 @@ public static ProjectDescriptor createProjectDescriptor(String projectName, Proj
return ProjectDescriptorsFactory.createRemoteProjectDescriptor(remoteID, projectName, version);
}

@Deprecated
public static void renameElement(NamedElement targetElement, String targetString) {
}

@Deprecated
public static void twcCommitReleaseLocks(String teamworkUsername, String targetString) {
}

@Deprecated
public static Element copyAndPaste(Element sourceElement, Element targetElement) {
return null;
}

@Deprecated
public static void createDiagrams(int i, Package testElementHolder) {
}

@Deprecated
private static class SimpleErrorHandler implements ErrorHandler<Exception> {
@Override
public void error(Exception ex) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package gov.nasa.jpl.mbee.mdk.test.framework;

import com.nomagic.runtime.ApplicationExitedException;
import org.gradle.process.internal.streams.EncodedStream;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;
import java.util.*;
import java.util.stream.Collectors;

/**
* Created by igomes on 10/22/16.
*/
public class GradleMagicDrawLauncher {
private static final String FRAMEWORK_LAUNCHER_CLASS = System.getProperty("com.nomagic.osgi.launcher", "com.nomagic.osgi.launcher.ProductionFrameworkLauncher");

private URLClassLoader urlClassLoader;
private String mainClass;
private String[] mainClassArgs = new String[]{};

public static void main(String... args) throws Exception {
new GradleMagicDrawLauncher().run(args);
}

public void run(String... args) throws Exception {
urlClassLoader = (URLClassLoader) getClass().getClassLoader();
parseArgs(args);
invokeMainMethod(FRAMEWORK_LAUNCHER_CLASS);
}

private void invokeMainMethod(String clazzName) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApplicationExitedException {
Class<?> clazz = Class.forName(clazzName, true, urlClassLoader);
Method mainMethod = clazz.getMethod("main", String[].class);
System.setProperty("com.nomagic.magicdraw.launcher", mainClass);
mainMethod.invoke(null, new Object[]{mainClassArgs});
}

private void parseArgs(String... args) throws IOException {
int mainClassIndex = -1;
List<URL> cpElements = new ArrayList<>();
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if ((arg.equals("-cp") || arg.equals("-classpath")) && i + 1 < args.length) {
String newClassPath = args[++i];
StringTokenizer tokenizer = new StringTokenizer(newClassPath, File.pathSeparator);
while(tokenizer.hasMoreTokens()) {
String cpElement = tokenizer.nextToken();
try {
cpElements.add((new File(".")).toURI().resolve((new File(cpElement)).toURI()).toURL());
} catch (MalformedURLException ignored) {
}
String currentClassPath = System.getProperty("java.class.path");
System.setProperty("java.class.path", (currentClassPath != null ? currentClassPath + File.pathSeparatorChar + cpElement : cpElement));
}
continue;
}
if (arg.startsWith("-D")) {
arg = arg.substring(2);
String[] parts = arg.split("=");
String key = parts[0];
String value = parts.length > 1 ? "" : null;
for (int j = 1; j < parts.length; j++) {
value += parts[j];
}
String ignored = value != null ? System.setProperty(key, value) : System.clearProperty(key);
continue;
}
if (arg.contains("org.gradle")) {
mainClass = arg;
mainClassIndex = i;
break;
}
}
if (mainClassIndex >= 0) {
mainClassArgs = Arrays.copyOfRange(args, mainClassIndex + 1, args.length);
}

/**
* This is used to load all the necessary libraries into the classpath and is usually done in GradleWorkerMain.
* However, we need them added to the classpath before launching the OSGi framework and additionally need to add them to java.class.path.
* To achieve that we've overridden the usual GradleWorkerMain with one that excludes the classloading and done it here.
* {@link worker.org.gradle.process.internal.worker.GradleWorkerMain}
* Gradle likes it's class overriding hacks, so to avoid issues caused by ones we haven't discovered we're loading Gradle libraries first.
*/

DataInputStream instr = new DataInputStream(new EncodedStream.EncodedInput(System.in));

// Read shared packages
int sharedPackagesCount = instr.readInt();
List<String> sharedPackages = new ArrayList<>(sharedPackagesCount);
for (int i = 0; i < sharedPackagesCount; i++) {
sharedPackages.add(instr.readUTF());
}

// Read worker implementation classpath
int classPathLength = instr.readInt();
List<String> files = new ArrayList<>(classPathLength);
List<URL> moreCpElements = new ArrayList<>(1 + cpElements.size() + classPathLength);
for (int i = 0; i < classPathLength; i++) {
String cpElement = instr.readUTF();
files.add(cpElement);
moreCpElements.add(new URL(cpElement));
}
moreCpElements.addAll(cpElements);
cpElements = moreCpElements;

if (!files.isEmpty()) {
String currentClassPath = System.getProperty("java.class.path");
String newClassPath = files.stream().collect(Collectors.joining(File.pathSeparator));
System.setProperty("java.class.path", (currentClassPath != null ? newClassPath + File.pathSeparatorChar + currentClassPath : newClassPath));
}

/**
* Adds our tests-hack.jar to the front of the classloader (and java.class.path) so our patches to Gradle's hacks can do their jobs.
* For an example see {@link org.gradle.internal.logging.slf4j.Slf4jLoggingConfigurer}.
*/

URL runtimeJar = getClass().getProtectionDomain().getCodeSource().getLocation();
if (runtimeJar != null) {
cpElements.add(0, runtimeJar);
String currentClassPath = System.getProperty("java.class.path");
try {
String runtimeJarPath = runtimeJar.toURI().getPath();
System.setProperty("java.class.path", (currentClassPath != null ? runtimeJarPath + File.pathSeparatorChar + currentClassPath : runtimeJarPath));
} catch (URISyntaxException e) {
e.printStackTrace();
}
}


if (!cpElements.isEmpty()) {
urlClassLoader = new URLClassLoader(cpElements.toArray(new URL[cpElements.size()]));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package gov.nasa.jpl.mbee.mdk.test.tests;

import com.nomagic.magicdraw.core.Application;
import com.nomagic.magicdraw.tests.MagicDrawTestRunner;
import com.nomagic.runtime.ApplicationExitedException;
import org.junit.Test;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
* Created by igomes on 10/21/16.
*/

public class ApplicationStartClassRunner extends BlockJUnit4ClassRunner {
//private static final String FRAMEWORK_LAUNCHER_CLASS = System.getProperty("com.nomagic.osgi.launcher", "com.nomagic.osgi.launcher.ProductionFrameworkLauncher");

private final Class<?> testClass;

public ApplicationStartClassRunner(Class<?> clazz) throws InitializationError {
super(clazz);
testClass = clazz;

try {
Application.getInstance().start(true, true, false, new String[]{"TESTER"}, null);
} catch (ApplicationExitedException e) {
e.printStackTrace();
System.exit(1);
}
}
}
Loading

0 comments on commit f45e7bc

Please sign in to comment.