My program gets as input a jar file and a directory contains Java source code. The jar file contains test cases that should be run on the input java source codes. If I define java input directory as source folder, then I can load test cases and run them as below:
URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/D:/workspace/tests.jar")});
//the name of my test case class that I want to load
Class cls = clsLoader.loadClass("temp.testMulDiv");
Result result = new JUnitCore().run(cls);
for (Failure failure : result.getFailures())
System.out.println(failure.toString());
However, while I put the source code as an input directory, then test cases can not find the code. I know that I should in someway tell test cases to search for source code in the input directory, but I do not know how to do that. Any suggestion?
I also check this piece of code, but still I have the above problem. While I can load the method that I want to run, but in this case "Class.forName" can not find the class ("temp.testMulDiv") and I get "ClassNotFoundException" error.
URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/D:/workspace/tests.jar")});
//the name my test case class that I want to load
Class cls = clsLoader.loadClass("temp.testMulDiv");
Method[] methods = cls.getDeclaredMethods();
// I want to run only the first method
Request request = Request.method(Class.forName(cls.getName()), methods[0].getName());
Result result = new JUnitCore().run(request);
for (Failure failure : result.getFailures())
System.out.println(failure.toString());
}
Any suggestion, how can I run test cases on an input directory (and not a source folder)? How programmatically can I do that?
A good practice is to place test classes in the same package as the source Java classes so that they have visibility of each another.
For example, in Maven, you would have src/main/java, and src/test/java to contain the source Java classes and test classes respectively. These are standard Maven layouts.
Say i have a source Java class MyService.java in net/java/service:
src/main/java/net/java/service/MyService.java
Then i should place the corresponding test class MyServiceTest.java in net/java/service too:
src/test/java/net/java/service/MyService.java
Assume you have the following public method in MyService.java:
public void multiDiv(){
...
}
Then you should have the following test method in MyServiceTest.java:
#Test
public void testMultiDiv(){
MyService service = new MyService();
service.multiDiv();
// assert ....
}
Related
I want to use Soot library to build an SSA from *.java file. But all the examples I found use Soot as standalone tool, not library. Can anyone give me example hot to do it in program?
For a start I am just trying to load my class from the source file and print it (TestClass.class is in the directory A/home/abdullin/workspace/test):
import soot.G
import soot.Scene
import soot.options.Options
fun main(args: Array<String>) {
G.reset();
Options.v().set_whole_program(true)
Scene.v().loadBasicClasses()
Scene.v().sootClassPath = "${Scene.v().defaultClassPath()}:/home/abdullin/workspace/test"
val sc = Scene.v().getSootClass("TestClass")
Scene.v().loadNecessaryClasses()
sc.setApplicationClass()
println(sc.name)
sc.methods.forEach {
println(it)
}
}
But when I run this, I get runtime exception Aborting: can't find classfile TestClass. If I change Scene.v().getSootClass("TestClass") to Scene.v().loadClassAndSupport("TestClass") as they do in some of their tutorials, soot finds my class, but it is not complete. It prints me signatures of class methods, but can't find their bodies, activeBody field is null.
TestClass
<TestClass: void <init>()>
<TestClass: void main(java.lang.String[])>
<TestClass: void f1()>
First, make sure that the Soot jar is in the classpath.
Then, set up Soot using the classes soot.G and soot.options.Options (G.reset() and Options.v().parse() are methods of interest, also see command line options).
Using soot.Scene.v().setSootClassPath() and similar you can tell Soot where to find the class files of the code you want to analyze.
You can then use Scene.v().getSootClass() to obtain SootClass objects. Make sure that Soot loads all classes after setting the class you want to analyze as main class:
mySootClass.setApplicationClass();
Scene.v().loadNecessaryClasses();
After this, you can use Soot to obtain various types of graphs and run you analyses, as described in the Survivor's guide
You can read this post (https://o2lab.github.io/710/p/a1.html). But if you try to analyze a jar file, you should unzip it and get a set of class files. Then you should add your classes directory into the soot_class_path.
Demo:
public static void main(String[] args) {
//spotbugs -- testing
String classesDir = "D:\\wkspace\\seed8\\dir\\spotbugs";
String mainClass = "edu.umd.cs.findbugs.LaunchAppropriateUI";
//set classpath
String jreDir = System.getProperty("java.home") + "\\lib\\jce.jar";
String jceDir = System.getProperty("java.home") + "\\lib\\rt.jar";
String path = jreDir + File.pathSeparator + jceDir + File.pathSeparator + classesDir;
Scene.v().setSootClassPath(path);
//add an intra-procedural analysis phase to Soot
TestCallGraphSootJar_3 analysis = new TestCallGraphSootJar_3();
PackManager.v().getPack("wjtp").add(new Transform("wjtp.TestSootCallGraph", analysis));
excludeJDKLibrary();
Options.v().set_process_dir(Arrays.asList(classesDir));
Options.v().set_whole_program(true);
//Options.v().set_app(true);
SootClass appClass = Scene.v().loadClassAndSupport(mainClass);
Scene.v().setMainClass(appClass);
Scene.v().loadNecessaryClasses();
//enableCHACallGraph();
enableSparkCallGraph();
PackManager.v().runPacks();
}
If you replace
SootClass appclass = Scene.v().loadClassAndSupport(mainclass);
Scene.v().setMainClass(appclass);
Scene.v().loadNecessaryClasses();
by
Scene.v().loadNecessaryClasses();
SootClass appclass = Scene.v().getSootClass(mainclass);
Scene.v().setMainClass(appclass);
, the program also works.
I have the class name stored in a property file. I know that the classes store will implement IDynamicLoad. How do I instantiate the class dynamically?
Right now I have
Properties foo = new Properties();
foo.load(new FileInputStream(new File("ClassName.properties")));
String class_name = foo.getProperty("class","DefaultClass");
//IDynamicLoad newClass = Class.forName(class_name).newInstance();
Does the newInstance only load compiled .class files? How do I load a Java Class that is not compiled?
How do I load a Java Class that is not compiled?
You need to compile it first. This can be done programmatically with the javax.tools API. This only requires the JDK being installed at the local machine on top of JRE.
Here's a basic kickoff example (leaving obvious exception handling aside):
// Prepare source somehow.
String source = "package test; public class Test { static { System.out.println(\"hello\"); } public Test() { System.out.println(\"world\"); } }";
// Save source in .java file.
File root = new File("/java"); // On Windows running on C:\, this is C:\java.
File sourceFile = new File(root, "test/Test.java");
sourceFile.getParentFile().mkdirs();
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());
// Load and instantiate compiled class.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
Class<?> cls = Class.forName("test.Test", true, classLoader); // Should print "hello".
Object instance = cls.newInstance(); // Should print "world".
System.out.println(instance); // Should print "test.Test#hashcode".
Which yields like
hello
world
test.Test#ab853b
Further use would be more easy if those classes implements a certain interface which is already in the classpath.
SomeInterface instance = (SomeInterface) cls.newInstance();
Otherwise you need to involve the Reflection API to access and invoke the (unknown) methods/fields.
That said and unrelated to the actual problem:
properties.load(new FileInputStream(new File("ClassName.properties")));
Letting java.io.File rely on current working directory is recipe for portability trouble. Don't do that. Put that file in classpath and use ClassLoader#getResourceAsStream() with a classpath-relative path.
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties"));
In the same vein as BalusC's answer, but a bit more automatic wrapper is here in this piece of code from my kilim distribution.
https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java
It takes a list of strings containing Java source, extracts the package and public class/interface names and creates the corresponding directory/file hierarchy in a tmp directory. It then runs the java compiler on it, and returns a list of name,classfile pairs (the ClassInfo structure).
Help yourself to the code. It is MIT licensed.
Your commented code is correct if you know that the class has a public no-arg constructor. You just have to cast the result, as the compiler can't know that the class will in fact implement IDynamicLoad. So:
IDynamicLoad newClass = (IDynamicLoad) Class.forName(class_name).newInstance();
Of course the class has to be compiled and on the classpath for that to work.
If you are looking to dynamically compile a class from source code, that is a whole other kettle of fish.
Background:
I am currently working on a project in eclipse that programatically executes JUnit tests that are pushed to a server.
So far everything works but I would like to know the results of the tests (specifically any failures) so I can push them out to an email. Right now the tests just output to the console but that doesn't seem to give me much output to actually use.
Right now I use the Runtime class to call the tests but that doesn't seem to have the functionality I need for getting results.
I have looked into the JUnitCore class but can't call any tests outside of the current java project.
So my main question would be how can I use JUnitCore to run junit tests in a specific JAR file? Or is there an easier way to approach this problem using a different class?
This is the only thing I've been able to get to work:
RunTests()
{
junitCore = new JUnitCore();
junitCore.run(AllTests.class);
}
But I would like to do something along the lines of this:
RunTests()
{
junitCore = new JUnitCore();
junitCore.run("C:\\$batch\\test\\hil research\\201507071307\\CommsTestRunner\\plugins\\TestSuite\\US35644.class");
}
I would appreciate any suggestions to this problem I am having. I'm an EE and was just introduced to java last month so this has been quite the challenge for me.
JUnitCore expects to read loaded classes, not class files in a JAR. Your real question is likely how to load the JAR (or directory of .class files) so it can be run.
Poke around with URLClassLoader; once you've amended the classpath appropriately, you can get a Class out of findClass and pass it into the JUnitCore methods you've found.
Since the tests might have classes that are also used by your server (but not necessarily at the same version) I would suggest not having your server directly run the tests. Instead, you can have your server start a new JVM that runs the tests. This is how IDEs like Eclipse run tests. You simply need to write a main class that has JUnit run the tests, and serializes the results on disk.
Your main class would look something like this:
public class MyRunner {
public static void main(String... args) throws IOException {
String path = System.getProperty("resultPath");
if (path == null) {
throw new NullPointerException("must specify resultPath property");
}
// Possibly install a security manager to prevent calls to System.exit()
Result result = new JUnitCore().runMain(new RealSystem(), args);
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(path)) {
out.writeObject(result);
}
System.exit(result.wasSuccessful() ? 0 : 1);
}
}
Then your server simply needs to construct a java command line with the jars that include the tests, the JUnit jar file, and a jar that contains MyRunner.
I am trying to build a Dynamic web project where user can practice Java code.
I got success on writing the code written by user in a .java file, compile the code & get error messages using Java Compile API.
Now, I need to run JUnit 1.4 Compatible test on that code.
I researched for it, and found something like parameterized junit testing. But my view on how should it be done isn't still clear.
UPDATE
This (http://codingbat.com/prob/p171896) is the exact thing what I'm trying to implement.
I solved this problem with the following steps:
Compile the JUnit TestClass and the ClassToTest
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int compilerResult = compiler.run(null, null, null,"/path/to/TestClass.java", "/path/to/ClassToTest.java");
System.out.println("Compiler result code: " + compilerResult);
Load the compiled classes into the classloader
File dir = new File("/path/to/class/files/");
URL url = dir.toURI().toURL();
URL[] urls = {url};
ClassLoader classLoader = new URLClassLoader(urls);
Run the tests (make sure that you add junit as a dependency of your project)
Class<?> junitTest = Class.forName("TestClass", true, classLoader);
Result result = junit.run(junitTest);
Edit: If you're not needing the JUnit tests to be dynamic you can skip the compilation of those tests and add them here: junit.run(JunitTest.class)
I am starting to switch from a well-known Java build system to Gradle to build all my projects, and after barely two hours into it I have already been able to publish a new version of one of my projects without a problem -- a breeze.
But now I encounter a difficulty. In short, I need to replicate the functionality of this Maven plugin which generates the necessary files for a ServiceLoader-enabled service.
In short: given a base class foo.bar.MyClass, it generates a file named META-INF/services/foo.bar.MyClass whose content is a set of classes in the current project which implement that interface/extend that base class. Such a file would look like:
com.mycompany.MyClassImpl
org.othercompany.MyClassImpl
In order to do this, it uses I don't know what as a classloader, loads the Class objects for com.myCompany.MyClassImpl or whatever and checks whether this class implements the wanted interface.
I am trying to do the same in Gradle. Hours of googling led me to this plugin, but after discussing with its author a little, it appears this plugin is able to merge such files, not create them. So, I have to do that myself...
And I am a real beginner both with Gradle and Groovy, which does not help! Here is my current code, link to the full build.gradle here; output (which I managed to get somehow; doesn't work from a clean dir) shown below (and please bear with me... I do Java, and I am final happy; Groovy is totally new to me):
/*
* TEST CODE
*/
final int CLASS_SUFFIX = ".class".length();
final URLClassLoader classLoader = this.class.classLoader;
// Where the classes are: OK
final File classesDir = sourceSets.main.output.classesDir;
final String basePath = classesDir.getCanonicalPath();
// Add them to the classloader: OK
classLoader.addURL(classesDir.toURI().toURL())
// Recurse over each file
classesDir.eachFileRecurse {
// You "return" from a closure, you do not "continue"...
if (!isPotentialClass(it))
return;
// Transform into a class name
final String path = it.getAbsolutePath();
final String name = path.substring(basePath.length() + 1);
final String className = name.substring(0, name.length() - CLASS_SUFFIX)
.replace('/', '.');
// Try and load it
try {
classLoader.loadClass(className);
println(className);
} catch (NoClassDefFoundError ignored) {
println("failed to load " + className + ": " + ignored);
}
}
boolean isPotentialClass(final File file)
{
return file.isFile() && file.name.endsWith(".class")
}
The output:
com.github.fge.msgsimple.InternalBundle
failed to load com.github.fge.msgsimple.bundle.MessageBundle: java.lang.NoClassDefFoundError: com/github/fge/Frozen
failed to load com.github.fge.msgsimple.bundle.MessageBundleBuilder: java.lang.NoClassDefFoundError: com/github/fge/Thawed
com.github.fge.msgsimple.bundle.PropertiesBundle$1
com.github.fge.msgsimple.bundle.PropertiesBundle
com.github.fge.msgsimple.provider.MessageSourceProvider
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$1
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$2
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$3
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider
com.github.fge.msgsimple.provider.MessageSourceLoader
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$1
com.github.fge.msgsimple.provider.StaticMessageSourceProvider
com.github.fge.msgsimple.source.MessageSource
com.github.fge.msgsimple.source.MapMessageSource$Builder
com.github.fge.msgsimple.source.MapMessageSource$1
com.github.fge.msgsimple.source.MapMessageSource
com.github.fge.msgsimple.source.PropertiesMessageSource
com.github.fge.msgsimple.locale.LocaleUtils
com.github.fge.msgsimple.serviceloader.MessageBundleFactory
com.github.fge.msgsimple.serviceloader.MessageBundleProvider
:compileJava UP-TO-DATE
The problem is in the two first lines: Frozen and Thawed are in a different project, which is in the compile classpath but not in the classpath I managed to grab so far... As such, these classes cannot even load.
How do I modify that code so as to have the full compile classpath availabe? Is my first question. Second question: how do I plug that code, when it works, into the build process?
Here are some hints:
Create a new URLClassLoader, rather than reusing an existing one.
Initialize the class loader with sourceSets.main.compileClasspath (which is an Iterable<File>) rather than classesDir.
Turn the code into a Gradle task class. For more information, see "Writing a simple task class" in the Gradle User Guide.
Ideally, you'd use a library like ASM to analyze the code, rather than using a class loader. To avoid the case where you cannot load a class because it internally references a class that's not on the compile class path, you may want to initialize the class loader with sourceSets.main.runtimeClasspath instead.