Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
117 changes: 117 additions & 0 deletions src/test/java/javacg/JARBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package 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;
}
}
10 changes: 10 additions & 0 deletions src/test/java/javacg/RunCucumberTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package javacg;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(plugin = { "pretty" })
public class RunCucumberTest {
}
48 changes: 48 additions & 0 deletions src/test/java/javacg/StepDefinitions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package 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);
}
}
}
37 changes: 37 additions & 0 deletions src/test/resources/javacg/lambda.feature
Original file line number Diff line number Diff line change
@@ -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()
"""