java-callgraph
Details
pom.xml 18(+18 -0)
diff --git a/pom.xml b/pom.xml
index 0992d00..3b51020 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,6 +26,24 @@
<version>3.12.1.GA</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>io.cucumber</groupId>
+ <artifactId>cucumber-java</artifactId>
+ <version>2.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.cucumber</groupId>
+ <artifactId>cucumber-junit</artifactId>
+ <version>2.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
src/test/java/gr/gousiosg/javacg/JARBuilder.java 117(+117 -0)
diff --git a/src/test/java/gr/gousiosg/javacg/JARBuilder.java b/src/test/java/gr/gousiosg/javacg/JARBuilder.java
new file mode 100644
index 0000000..327c55b
--- /dev/null
+++ b/src/test/java/gr/gousiosg/javacg/JARBuilder.java
@@ -0,0 +1,117 @@
+package gr.gousiosg.javacg;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaCompiler.CompilationTask;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+public class JARBuilder {
+ private static final String TEMP_DIR = System.getProperty("java.io.tmpdir");
+
+ private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ private final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
+ private final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
+ private final LinkedList<JavaFileObject> compilationUnits = new LinkedList<>();
+ private final Collection<File> classFiles = new LinkedList<>();
+
+ public JARBuilder() throws IOException {
+ fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File(TEMP_DIR)));
+ }
+
+ public void add(String className, String classCode) throws IOException {
+ compilationUnits.add(createJavaFile(className, classCode));
+ classFiles.add(new File(TEMP_DIR, className + ".class"));
+ }
+
+ public File build() throws FileNotFoundException, IOException {
+ CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
+ boolean success = task.call();
+ if (!success) {
+ displayDiagnostic(diagnostics);
+ throw new RuntimeException("Cannot compile classes for the JAR");
+ }
+
+ File file = File.createTempFile("test", ".jar");
+ JarOutputStream jar = new JarOutputStream(new FileOutputStream(file), createManifest());
+ for (File classFile : classFiles) {
+ add(classFile, jar);
+ }
+ jar.close();
+ return file;
+ }
+
+ private void displayDiagnostic(DiagnosticCollector<JavaFileObject> diagnostics) {
+ for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
+ JavaSourceFromString sourceClass = (JavaSourceFromString) diagnostic.getSource();
+ System.err.println("-----");
+ System.err.println("Source: " + sourceClass.getName());
+ System.err.println("Message: " + diagnostic.getMessage(null));
+ System.err.println("Position: " + diagnostic.getPosition());
+ System.err.println(diagnostic.getKind() + " " + diagnostic.getCode());
+ }
+ }
+
+ private JavaFileObject createJavaFile(String className, String classCode) throws IOException {
+ StringWriter writer = new StringWriter();
+ writer.append(classCode);
+ writer.close();
+ JavaFileObject file = new JavaSourceFromString(className, writer.toString());
+ return file;
+ }
+
+ private Manifest createManifest() {
+ Manifest manifest = new Manifest();
+ manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+ return manifest;
+ }
+
+ private void add(File classFile, JarOutputStream jar) throws IOException {
+ JarEntry entry = new JarEntry(classFile.getPath().replace("\\", "/"));
+ jar.putNextEntry(entry);
+ try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(classFile))) {
+ byte[] buffer = new byte[1024];
+ while (true) {
+ int count = in.read(buffer);
+ if (count == -1)
+ break;
+ jar.write(buffer, 0, count);
+ }
+ jar.closeEntry();
+ }
+ }
+}
+
+class JavaSourceFromString extends SimpleJavaFileObject {
+ final String code;
+
+ JavaSourceFromString(String name, String code) throws IOException {
+ super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
+ this.code = code;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return code;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/gr/gousiosg/javacg/RunCucumberTest.java b/src/test/java/gr/gousiosg/javacg/RunCucumberTest.java
new file mode 100644
index 0000000..e99e65a
--- /dev/null
+++ b/src/test/java/gr/gousiosg/javacg/RunCucumberTest.java
@@ -0,0 +1,10 @@
+package gr.gousiosg.javacg;
+
+import cucumber.api.CucumberOptions;
+import cucumber.api.junit.Cucumber;
+import org.junit.runner.RunWith;
+
+@RunWith(Cucumber.class)
+@CucumberOptions(plugin = { "pretty" })
+public class RunCucumberTest {
+}
\ No newline at end of file
diff --git a/src/test/java/gr/gousiosg/javacg/StepDefinitions.java b/src/test/java/gr/gousiosg/javacg/StepDefinitions.java
new file mode 100644
index 0000000..281c1c1
--- /dev/null
+++ b/src/test/java/gr/gousiosg/javacg/StepDefinitions.java
@@ -0,0 +1,48 @@
+package gr.gousiosg.javacg;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import cucumber.api.java.en.Given;
+import cucumber.api.java.en.Then;
+import cucumber.api.java.en.When;
+import gr.gousiosg.javacg.stat.JCallGraph;
+
+public class StepDefinitions {
+ private final JARBuilder jarBuilder;
+ private String result;
+
+ public StepDefinitions() throws IOException {
+ jarBuilder = new JARBuilder();
+ }
+
+ @Given("^I have the class \"([^\"]*)\" with code:$")
+ public void i_have_the_class_with_code(String className, String classCode) throws Exception {
+ jarBuilder.add(className, classCode);
+ }
+
+ @When("^I run the analyze$")
+ public void i_analyze_it() throws Exception {
+ File jarFile = jarBuilder.build();
+
+ PrintStream oldOut = System.out;
+ ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(resultBuffer));
+ JCallGraph.main(new String[] { jarFile.getPath() });
+ System.setOut(oldOut);
+
+ result = resultBuffer.toString();
+ }
+
+ @Then("^the result should contain:$")
+ public void the_result_should_contain(String line) throws Exception {
+ if (result.contains(line)) {
+ // OK
+ } else {
+ System.err.println(result);
+ throw new RuntimeException("Cannot found: " + line);
+ }
+ }
+}
diff --git a/src/test/resources/gr/gousiosg/javacg/lambda.feature b/src/test/resources/gr/gousiosg/javacg/lambda.feature
new file mode 100644
index 0000000..14d8f3f
--- /dev/null
+++ b/src/test/resources/gr/gousiosg/javacg/lambda.feature
@@ -0,0 +1,37 @@
+#Author: matthieu.vergne@gmail.com
+Feature: Lambda
+ I want to identify all lambdas within the analyzed code.
+
+ Background:
+ # Introduce the lambda we will use
+ Given I have the class "Runner" with code:
+ """
+ @FunctionalInterface
+ public interface Runner {
+ public void run();
+ }
+ """
+
+ Scenario: Retrieve lambda in method
+ Given I have the class "LambdaTest" with code:
+ """
+ public class LambdaTest {
+ public void methodA() {
+ Runner r = () -> methodB();
+ r.run();
+ }
+
+ public void methodB() {}
+ }
+ """
+ When I run the analyze
+ # Creation of r in methodA
+ Then the result should contain:
+ """
+ M:LambdaTest:methodA() (D)Runner:run(LambdaTest)
+ """
+ # Call of methodB in r
+ And the result should contain:
+ """
+ M:LambdaTest:lambda$methodA$0() (M)LambdaTest:methodB()
+ """