Skip to content

Commit 3e56aee

Browse files
committed
Add integration test project
1 parent 6bd9b7e commit 3e56aee

File tree

5 files changed

+282
-1
lines changed

5 files changed

+282
-1
lines changed

integration-tests/build.gradle.kts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
plugins {
2+
id("java.conventions")
3+
id("checkstyle.conventions")
4+
}
5+
6+
dependencies {
7+
testImplementation(platform("org.junit:junit-bom:5.13.0"))
8+
testImplementation("org.junit.jupiter:junit-jupiter")
9+
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
10+
}
11+
12+
tasks.test {
13+
useJUnitPlatform()
14+
// Build fat jar before running tests
15+
val fatJarTask = rootProject.tasks["fatJar"] as Jar
16+
dependsOn(fatJarTask)
17+
val fatJarFile = fatJarTask.archiveFile.get().asFile
18+
// Set system properties for test runtime
19+
systemProperty("tai-e.jar.path", fatJarFile.absolutePath)
20+
systemProperty("tai-e.project.path", rootProject.projectDir.absolutePath)
21+
systemProperty("java.executable.path", javaExecutablePath.asFile.absolutePath)
22+
}
23+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Tai-e: A Static Analysis Framework for Java
3+
*
4+
* Copyright (C) 2022 Tian Tan <[email protected]>
5+
* Copyright (C) 2022 Yue Li <[email protected]>
6+
*
7+
* This file is part of Tai-e.
8+
*
9+
* Tai-e is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License
11+
* as published by the Free Software Foundation, either version 3
12+
* of the License, or (at your option) any later version.
13+
*
14+
* Tai-e is distributed in the hope that it will be useful,but WITHOUT
15+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
17+
* Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
21+
*/
22+
23+
package pascal.taie.integration;
24+
25+
import java.io.File;
26+
import java.io.IOException;
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.ArrayList;
29+
import java.util.Arrays;
30+
import java.util.List;
31+
import java.util.concurrent.TimeUnit;
32+
33+
/**
34+
* Utility class for running the fat JAR in tests
35+
*/
36+
public class FatJarRunner {
37+
38+
private static final int DEFAULT_TIMEOUT_SECONDS = 30;
39+
40+
private final String jarPath;
41+
private final String javaCommand;
42+
private final File workingDir;
43+
44+
public FatJarRunner(String javaCommand, String jarPath, File workingDir) {
45+
this.jarPath = jarPath;
46+
this.javaCommand = System.getProperty("java.command", "java");
47+
this.workingDir = workingDir;
48+
// Verify JAR exists
49+
File jarFile = new File(jarPath);
50+
if (!jarFile.exists()) {
51+
throw new IllegalArgumentException("JAR file does not exist: " + jarPath);
52+
}
53+
}
54+
55+
/**
56+
* Run the JAR with given arguments
57+
*/
58+
public ProcessResult run(String... args) throws IOException, InterruptedException {
59+
return run(DEFAULT_TIMEOUT_SECONDS, args);
60+
}
61+
62+
/**
63+
* Run the JAR with custom timeout
64+
*/
65+
public ProcessResult run(int timeoutSeconds, String... args)
66+
throws IOException, InterruptedException {
67+
// Build command
68+
List<String> command = buildCommand(args);
69+
// Start process
70+
ProcessBuilder pb = new ProcessBuilder(command);
71+
pb.directory(workingDir);
72+
long startTime = System.currentTimeMillis();
73+
Process process = pb.start();
74+
75+
// Wait for the process to complete
76+
String stdout = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
77+
String stderr = new String(process.getErrorStream().readAllBytes(), StandardCharsets.UTF_8);
78+
boolean finished = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
79+
if (!finished) {
80+
process.destroyForcibly();
81+
throw new RuntimeException("Process timed out after " + timeoutSeconds + " seconds");
82+
}
83+
84+
long executionTime = System.currentTimeMillis() - startTime;
85+
return new ProcessResult(
86+
process.exitValue(),
87+
stdout,
88+
stderr,
89+
executionTime
90+
);
91+
}
92+
93+
/**
94+
* Build command list for process execution
95+
*/
96+
private List<String> buildCommand(String... args) {
97+
List<String> command = new ArrayList<>();
98+
command.add(javaCommand);
99+
command.add("-jar");
100+
command.add(jarPath);
101+
// add application arguments
102+
command.addAll(Arrays.asList(args));
103+
return command;
104+
}
105+
106+
public File getWorkingDir() {
107+
return workingDir;
108+
}
109+
110+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Tai-e: A Static Analysis Framework for Java
3+
*
4+
* Copyright (C) 2022 Tian Tan <[email protected]>
5+
* Copyright (C) 2022 Yue Li <[email protected]>
6+
*
7+
* This file is part of Tai-e.
8+
*
9+
* Tai-e is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License
11+
* as published by the Free Software Foundation, either version 3
12+
* of the License, or (at your option) any later version.
13+
*
14+
* Tai-e is distributed in the hope that it will be useful,but WITHOUT
15+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
17+
* Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
21+
*/
22+
23+
package pascal.taie.integration;
24+
25+
import org.junit.jupiter.api.BeforeAll;
26+
import org.junit.jupiter.api.DisplayName;
27+
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.api.io.TempDir;
29+
30+
import java.io.File;
31+
import java.nio.file.Files;
32+
33+
import static org.junit.jupiter.api.Assertions.assertFalse;
34+
import static org.junit.jupiter.api.Assertions.assertNotNull;
35+
import static org.junit.jupiter.api.Assertions.assertTrue;
36+
37+
class FatJarTests {
38+
39+
@TempDir
40+
private static File tempDir;
41+
42+
private static FatJarRunner jarRunner;
43+
44+
private static File rootProjectDir;
45+
46+
@BeforeAll
47+
static void setupClass() {
48+
String jarPath = System.getProperty("tai-e.jar.path");
49+
assertNotNull(jarPath, "System property 'tai-e.jar.path'"
50+
+ " must be set to the path of the fat JAR");
51+
rootProjectDir = new File(System.getProperty("tai-e.project.path"));
52+
assertTrue(rootProjectDir.exists() && rootProjectDir.isDirectory(),
53+
"System property 'tai-e.project.path' must point to the root of the Tai-e project");
54+
String javaPath = System.getProperty("java.executable.path");
55+
assertNotNull(javaPath, "System property 'java.executable.path'"
56+
+ " must be set to the path of the Java executable");
57+
jarRunner = new FatJarRunner(javaPath, jarPath, tempDir);
58+
}
59+
60+
@Test
61+
@DisplayName("Should display usage with no arguments")
62+
void testNoArguments() throws Exception {
63+
ProcessResult result = jarRunner.run();
64+
assertTrue(result.isSuccess());
65+
assertTrue(result.stdout().contains("Usage: Options"));
66+
}
67+
68+
@Test
69+
@DisplayName("Should display usage with -h")
70+
void testHelp() throws Exception {
71+
ProcessResult result = jarRunner.run("-h");
72+
assertTrue(result.isSuccess());
73+
assertTrue(result.stdout().contains("Usage: Options"));
74+
}
75+
76+
@Test
77+
@DisplayName("Should handle invalid command")
78+
void testInvalidCommand() throws Exception {
79+
ProcessResult result = jarRunner.run("invalid");
80+
assertFalse(result.isSuccess());
81+
// TODO: should print usage message
82+
// assertTrue(result.stdout().contains("Usage: Options"));
83+
}
84+
85+
@Test
86+
@DisplayName("Should write tai-e.log and options.yml files")
87+
void testLogFile() throws Exception {
88+
ProcessResult result = jarRunner.run("-pp");
89+
// Check if the process ran successfully
90+
assertTrue(result.isSuccess());
91+
// Check if the log file is created
92+
File logFile = new File(tempDir, "output/tai-e.log");
93+
assertTrue(logFile.exists(), "Log file should be created");
94+
// Check if the log file is not empty
95+
String logContent = new String(Files.readAllBytes(logFile.toPath()));
96+
assertFalse(logContent.isEmpty(), "Log file should not be empty");
97+
// Check if the log file contains expected content
98+
assertTrue(logContent.contains("Writing log to"));
99+
assertTrue(result.stdout().contains("Writing log to"));
100+
// Check if the options.yml file is created
101+
File optionsFile = new File(tempDir, "output/options.yml");
102+
assertTrue(optionsFile.exists(), "Options file should be created");
103+
// Check if the options.yml file is not empty
104+
String optionsContent = new String(Files.readAllBytes(optionsFile.toPath()));
105+
assertFalse(optionsContent.isEmpty(), "Options file should not be empty");
106+
}
107+
108+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Tai-e: A Static Analysis Framework for Java
3+
*
4+
* Copyright (C) 2022 Tian Tan <[email protected]>
5+
* Copyright (C) 2022 Yue Li <[email protected]>
6+
*
7+
* This file is part of Tai-e.
8+
*
9+
* Tai-e is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License
11+
* as published by the Free Software Foundation, either version 3
12+
* of the License, or (at your option) any later version.
13+
*
14+
* Tai-e is distributed in the hope that it will be useful,but WITHOUT
15+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
17+
* Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
21+
*/
22+
23+
package pascal.taie.integration;
24+
25+
/**
26+
* Container for process execution results
27+
*/
28+
public record ProcessResult(
29+
int exitCode,
30+
String stdout,
31+
String stderr,
32+
long executionTimeMs
33+
) {
34+
35+
public boolean isSuccess() {
36+
return exitCode == 0;
37+
}
38+
39+
}

settings.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
include(
44
":", // root project
55
"docs",
6+
"integration-tests",
67
)
78

89
// Auto detect and provision JVM
910
plugins {
10-
id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0")
11+
id("org.gradle.toolchains.foojay-resolver-convention") version("1.0.0")
1112
}

0 commit comments

Comments
 (0)