Skip to content

Commit 4589557

Browse files
committed
Merge branch 'matthieu-vergne-feature/functional_testing'
2 parents 6b0dcf5 + 93c5f54 commit 4589557

File tree

5 files changed

+230
-0
lines changed

5 files changed

+230
-0
lines changed

pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,24 @@
2626
<version>3.12.1.GA</version>
2727
<scope>provided</scope>
2828
</dependency>
29+
<dependency>
30+
<groupId>io.cucumber</groupId>
31+
<artifactId>cucumber-java</artifactId>
32+
<version>2.3.1</version>
33+
<scope>test</scope>
34+
</dependency>
35+
<dependency>
36+
<groupId>io.cucumber</groupId>
37+
<artifactId>cucumber-junit</artifactId>
38+
<version>2.3.1</version>
39+
<scope>test</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>junit</groupId>
43+
<artifactId>junit</artifactId>
44+
<version>4.12</version>
45+
<scope>test</scope>
46+
</dependency>
2947
</dependencies>
3048
<build>
3149
<plugins>
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package gr.gousiosg.javacg;
2+
3+
import java.io.BufferedInputStream;
4+
import java.io.File;
5+
import java.io.FileInputStream;
6+
import java.io.FileNotFoundException;
7+
import java.io.FileOutputStream;
8+
import java.io.IOException;
9+
import java.io.StringWriter;
10+
import java.net.URI;
11+
import java.util.Arrays;
12+
import java.util.Collection;
13+
import java.util.LinkedList;
14+
import java.util.jar.Attributes;
15+
import java.util.jar.JarEntry;
16+
import java.util.jar.JarOutputStream;
17+
import java.util.jar.Manifest;
18+
19+
import javax.tools.Diagnostic;
20+
import javax.tools.DiagnosticCollector;
21+
import javax.tools.JavaCompiler;
22+
import javax.tools.JavaCompiler.CompilationTask;
23+
import javax.tools.JavaFileObject;
24+
import javax.tools.SimpleJavaFileObject;
25+
import javax.tools.StandardJavaFileManager;
26+
import javax.tools.StandardLocation;
27+
import javax.tools.ToolProvider;
28+
29+
public class JARBuilder {
30+
private static final String TEMP_DIR = System.getProperty("java.io.tmpdir");
31+
32+
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
33+
private final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
34+
private final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
35+
private final LinkedList<JavaFileObject> compilationUnits = new LinkedList<>();
36+
private final Collection<File> classFiles = new LinkedList<>();
37+
38+
public JARBuilder() throws IOException {
39+
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File(TEMP_DIR)));
40+
}
41+
42+
public void add(String className, String classCode) throws IOException {
43+
compilationUnits.add(createJavaFile(className, classCode));
44+
classFiles.add(new File(TEMP_DIR, className + ".class"));
45+
}
46+
47+
public File build() throws FileNotFoundException, IOException {
48+
CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
49+
boolean success = task.call();
50+
if (!success) {
51+
displayDiagnostic(diagnostics);
52+
throw new RuntimeException("Cannot compile classes for the JAR");
53+
}
54+
55+
File file = File.createTempFile("test", ".jar");
56+
JarOutputStream jar = new JarOutputStream(new FileOutputStream(file), createManifest());
57+
for (File classFile : classFiles) {
58+
add(classFile, jar);
59+
}
60+
jar.close();
61+
return file;
62+
}
63+
64+
private void displayDiagnostic(DiagnosticCollector<JavaFileObject> diagnostics) {
65+
for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
66+
JavaSourceFromString sourceClass = (JavaSourceFromString) diagnostic.getSource();
67+
System.err.println("-----");
68+
System.err.println("Source: " + sourceClass.getName());
69+
System.err.println("Message: " + diagnostic.getMessage(null));
70+
System.err.println("Position: " + diagnostic.getPosition());
71+
System.err.println(diagnostic.getKind() + " " + diagnostic.getCode());
72+
}
73+
}
74+
75+
private JavaFileObject createJavaFile(String className, String classCode) throws IOException {
76+
StringWriter writer = new StringWriter();
77+
writer.append(classCode);
78+
writer.close();
79+
JavaFileObject file = new JavaSourceFromString(className, writer.toString());
80+
return file;
81+
}
82+
83+
private Manifest createManifest() {
84+
Manifest manifest = new Manifest();
85+
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
86+
return manifest;
87+
}
88+
89+
private void add(File classFile, JarOutputStream jar) throws IOException {
90+
JarEntry entry = new JarEntry(classFile.getPath().replace("\\", "/"));
91+
jar.putNextEntry(entry);
92+
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(classFile))) {
93+
byte[] buffer = new byte[1024];
94+
while (true) {
95+
int count = in.read(buffer);
96+
if (count == -1)
97+
break;
98+
jar.write(buffer, 0, count);
99+
}
100+
jar.closeEntry();
101+
}
102+
}
103+
}
104+
105+
class JavaSourceFromString extends SimpleJavaFileObject {
106+
final String code;
107+
108+
JavaSourceFromString(String name, String code) throws IOException {
109+
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
110+
this.code = code;
111+
}
112+
113+
@Override
114+
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
115+
return code;
116+
}
117+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package gr.gousiosg.javacg;
2+
3+
import cucumber.api.CucumberOptions;
4+
import cucumber.api.junit.Cucumber;
5+
import org.junit.runner.RunWith;
6+
7+
@RunWith(Cucumber.class)
8+
@CucumberOptions(plugin = { "pretty" })
9+
public class RunCucumberTest {
10+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package gr.gousiosg.javacg;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.io.File;
5+
import java.io.IOException;
6+
import java.io.PrintStream;
7+
8+
import cucumber.api.java.en.Given;
9+
import cucumber.api.java.en.Then;
10+
import cucumber.api.java.en.When;
11+
import gr.gousiosg.javacg.stat.JCallGraph;
12+
13+
public class StepDefinitions {
14+
private final JARBuilder jarBuilder;
15+
private String result;
16+
17+
public StepDefinitions() throws IOException {
18+
jarBuilder = new JARBuilder();
19+
}
20+
21+
@Given("^I have the class \"([^\"]*)\" with code:$")
22+
public void i_have_the_class_with_code(String className, String classCode) throws Exception {
23+
jarBuilder.add(className, classCode);
24+
}
25+
26+
@When("^I run the analyze$")
27+
public void i_analyze_it() throws Exception {
28+
File jarFile = jarBuilder.build();
29+
30+
PrintStream oldOut = System.out;
31+
ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
32+
System.setOut(new PrintStream(resultBuffer));
33+
JCallGraph.main(new String[] { jarFile.getPath() });
34+
System.setOut(oldOut);
35+
36+
result = resultBuffer.toString();
37+
}
38+
39+
@Then("^the result should contain:$")
40+
public void the_result_should_contain(String line) throws Exception {
41+
if (result.contains(line)) {
42+
// OK
43+
} else {
44+
System.err.println(result);
45+
throw new RuntimeException("Cannot found: " + line);
46+
}
47+
}
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#Author: matthieu.vergne@gmail.com
2+
Feature: Lambda
3+
I want to identify all lambdas within the analyzed code.
4+
5+
Background:
6+
# Introduce the lambda we will use
7+
Given I have the class "Runner" with code:
8+
"""
9+
@FunctionalInterface
10+
public interface Runner {
11+
public void run();
12+
}
13+
"""
14+
15+
Scenario: Retrieve lambda in method
16+
Given I have the class "LambdaTest" with code:
17+
"""
18+
public class LambdaTest {
19+
public void methodA() {
20+
Runner r = () -> methodB();
21+
r.run();
22+
}
23+
24+
public void methodB() {}
25+
}
26+
"""
27+
When I run the analyze
28+
# Creation of r in methodA
29+
Then the result should contain:
30+
"""
31+
M:LambdaTest:methodA() (D)Runner:run(LambdaTest)
32+
"""
33+
# Call of methodB in r
34+
And the result should contain:
35+
"""
36+
M:LambdaTest:lambda$methodA$0() (M)LambdaTest:methodB()
37+
"""

0 commit comments

Comments
 (0)