Skip to content

Fix Issue 113: call graph is missing edges to implementations of interfaces. Merge to v1.X.X. #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
2 changes: 1 addition & 1 deletion src/main/java/com/ibm/cldk/utils/AnalysisUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public static Iterable<Entrypoint> getEntryPoints(IClassHierarchy cha) {
System.exit(1);
return Stream.empty();
}
}).filter(method -> method.isPublic() || method.isPrivate() || method.isProtected() || method.isStatic()).map(method -> new DefaultEntrypoint(method, cha)).collect(Collectors.toList());
}).map(method -> new DefaultEntrypoint(method, cha)).collect(Collectors.toList());

Log.info("Registered " + entrypoints.size() + " entrypoints.");
return entrypoints;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -37,7 +38,7 @@ public static List<Path> jarFilesStream(String projectPath) throws IOException {
.collect(Collectors.toList());
}
}
return null;
return new ArrayList<>();
}

public static List<Path> sourceFilesStream(String projectPath) throws IOException {
Expand Down
89 changes: 66 additions & 23 deletions src/test/java/com/ibm/cldk/CodeAnalyzerIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package com.ibm.cldk;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.json.JSONArray;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.MountableFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.time.Duration;
import java.util.Properties;
import java.util.stream.StreamSupport;

import static org.junit.jupiter.api.Assertions.assertThrows;

@Testcontainers
@SuppressWarnings("resource")
Expand All @@ -28,7 +33,7 @@ public class CodeAnalyzerIntegrationTest {
*/
static String codeanalyzerVersion;
static final String javaVersion = "17";

static String javaHomePath;
static {
// Build project first
try {
Expand All @@ -44,15 +49,14 @@ public class CodeAnalyzerIntegrationTest {
}

@Container
static final GenericContainer<?> container = new GenericContainer<>("openjdk:17-jdk")
static final GenericContainer<?> container = new GenericContainer<>("ubuntu:latest")
.withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("sh"))
.withCommand("-c", "while true; do sleep 1; done")
.withFileSystemBind(
String.valueOf(Paths.get(System.getProperty("user.dir")).resolve("build/libs")),
"/opt/jars",
BindMode.READ_WRITE)
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("build/libs")), "/opt/jars")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("build/libs")), "/opt/jars")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-corrupt-test")), "/test-applications/mvnw-corrupt-test")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/plantsbywebsphere")), "/test-applications/plantsbywebsphere")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/call-graph-test")), "/test-applications/call-graph-test")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-working-test")), "/test-applications/mvnw-working-test");

@Container
Expand All @@ -61,11 +65,32 @@ public class CodeAnalyzerIntegrationTest {
.withCommand("-c", "while true; do sleep 1; done")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("build/libs")), "/opt/jars")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-corrupt-test")), "/test-applications/mvnw-corrupt-test")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-working-test")), "/test-applications/mvnw-working-test");
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-working-test")), "/test-applications/mvnw-working-test")
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/daytrader8")), "/test-applications/daytrader8");

public CodeAnalyzerIntegrationTest() throws IOException, InterruptedException {
}

@BeforeAll
static void setUp() {
// Install Java 17 in the base container
try {
container.execInContainer("apt-get", "update");
container.execInContainer("apt-get", "install", "-y", "openjdk-17-jdk");

// Get JAVA_HOME dynamically
var javaHomeResult = container.execInContainer("bash", "-c",
"dirname $(dirname $(readlink -f $(which java)))"
);
javaHomePath = javaHomeResult.getStdout().trim();
Assertions.assertFalse(javaHomePath.isEmpty(), "Failed to determine JAVA_HOME");

} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}


// Get the version of the codeanalyzer jar
Properties properties = new Properties();
try (FileInputStream fis = new FileInputStream(
Paths.get(System.getProperty("user.dir"), "gradle.properties").toFile())) {
Expand Down Expand Up @@ -94,18 +119,36 @@ void shouldHaveCodeAnalyzerJar() throws Exception {
@Test
void shouldBeAbleToRunCodeAnalyzer() throws Exception {
var runCodeAnalyzerJar = container.execInContainer(
"java",
"-jar",
String.format("/opt/jars/codeanalyzer-%s.jar", codeanalyzerVersion),
"--help"
);
"bash", "-c",
String.format("export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --help",
javaHomePath, codeanalyzerVersion
));

Assertions.assertEquals(0, runCodeAnalyzerJar.getExitCode(),
"Command should execute successfully");
Assertions.assertTrue(runCodeAnalyzerJar.getStdout().length() > 0,
"Should have some output");
}

@Test
void callGraphShouldHaveKnownEdges() throws Exception {
var whatIsInTheTestAppFolder = container.execInContainer("ls", "/test-applications/call-graph-test");
var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
"bash", "-c",
String.format(
"export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/call-graph-test --analysis-level=2",
javaHomePath, codeanalyzerVersion
)
);

// Read the output JSON
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
JsonArray systemDepGraph = jsonObject.getAsJsonArray("system_dependency_graph");
Assertions.assertEquals(4, StreamSupport.stream(systemDepGraph.spliterator(), false).count(),
"Expected exactly 4 entries in the system dependency graph");
}

@Test
void corruptMavenShouldNotBuildWithWrapper() throws IOException, InterruptedException {
// Make executable
Expand All @@ -129,19 +172,19 @@ void corruptMavenShouldProduceAnalysisArtifactsWhenMVNCommandIsInPath() throws I
Assertions.assertTrue(runCodeAnalyzer.getStdout().contains("[ERROR]\tCannot run program \"/test-applications/mvnw-corrupt-test/mvnw\"") && runCodeAnalyzer.getStdout().contains("/mvn."));
// We should correctly identify the build tool used in the mvn command from the system path.
Assertions.assertTrue(runCodeAnalyzer.getStdout().contains("[INFO]\tBuilding the project using /usr/bin/mvn."));
}
}

@Test
void corruptMavenShouldNotTerminateWithErrorWhenMavenIsNotPresentUnlessAnalysisLevel2() throws IOException, InterruptedException {
// When analysis level 2, we should get a Runtime Exception
var runCodeAnalyzer = container.execInContainer(
"java",
"-jar",
String.format("/opt/jars/codeanalyzer-%s.jar", codeanalyzerVersion),
"--input=/test-applications/mvnw-corrupt-test",
"--output=/tmp/",
"--analysis-level=2"
);
"bash", "-c",
String.format(
"export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/mvnw-corrupt-test --output=/tmp/ --analysis-level=2",
javaHomePath, codeanalyzerVersion
)
);

Assertions.assertEquals(1, runCodeAnalyzer.getExitCode());
Assertions.assertTrue(runCodeAnalyzer.getStderr().contains("java.lang.RuntimeException"));
}
Expand Down
2 changes: 2 additions & 0 deletions src/test/resources/test-applications/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ target/
*.iws
*.ipr

!gradle-wrapper.jar

# Ignore Eclipse files
.settings/
*.classpath
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf

# These are Windows script files and should use crlf
*.bat text eol=crlf

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build
30 changes: 30 additions & 0 deletions src/test/resources/test-applications/call-graph-test/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
plugins {
id 'application'
}

repositories {
mavenCentral()
}

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

if (project.hasProperty('mainClass')) {
mainClassName = project.getProperty('mainClass')
} else {
// use a default
mainClassName =("org.example.User")
}

sourceSets {
main {
java {
srcDirs = ["src/main/java"]
}
resources {
srcDirs = ["src/main/resources"]
}
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading