Skip to content

Commit 84a77a9

Browse files
committed
feat: rust analysis support tests
1 parent b7b4d96 commit 84a77a9

2 files changed

Lines changed: 754 additions & 0 deletions

File tree

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
/*
2+
* Copyright 2023-2025 Trustify Dependency Analytics Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
*
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package io.github.guacsec.trustifyda.providers;
18+
19+
import static org.junit.jupiter.api.Assertions.*;
20+
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.io.TempDir;
27+
28+
public class RustProviderPerformanceTest {
29+
30+
@Test
31+
public void testLargeCargoTomlPerformanceBenchmark(@TempDir Path tempDir) throws Exception {
32+
System.out.println("=== RustProvider Performance Benchmark ===");
33+
34+
// Create multiple large Cargo.toml files to test performance
35+
List<Path> testFiles = createPerformanceTestFiles(tempDir);
36+
37+
for (int fileIndex = 0; fileIndex < testFiles.size(); fileIndex++) {
38+
Path cargoToml = testFiles.get(fileIndex);
39+
System.out.println(
40+
"\nTesting file " + (fileIndex + 1) + " with complexity level " + (fileIndex + 1));
41+
42+
// Warm up JVM for more accurate measurements
43+
for (int warmup = 0; warmup < 3; warmup++) {
44+
RustProvider provider = new RustProvider(cargoToml);
45+
provider.provideComponent();
46+
}
47+
48+
// Measure performance over multiple runs
49+
List<Long> durations = new ArrayList<>();
50+
for (int run = 0; run < 10; run++) {
51+
long startTime = System.nanoTime();
52+
53+
RustProvider provider = new RustProvider(cargoToml);
54+
var result = provider.provideComponent();
55+
56+
long endTime = System.nanoTime();
57+
long duration = (endTime - startTime) / 1_000_000; // Convert to milliseconds
58+
59+
durations.add(duration);
60+
assertNotNull(result);
61+
assertTrue(result.buffer.length > 0);
62+
}
63+
64+
// Calculate statistics
65+
long avgDuration = durations.stream().mapToLong(Long::longValue).sum() / durations.size();
66+
long minDuration = durations.stream().mapToLong(Long::longValue).min().orElse(0);
67+
long maxDuration = durations.stream().mapToLong(Long::longValue).max().orElse(0);
68+
69+
System.out.println("Performance Results:");
70+
System.out.println(" Average: " + avgDuration + "ms");
71+
System.out.println(" Min: " + minDuration + "ms");
72+
System.out.println(" Max: " + maxDuration + "ms");
73+
74+
// Performance assertions
75+
assertTrue(
76+
avgDuration < 200, "Average parsing should be under 200ms, got " + avgDuration + "ms");
77+
assertTrue(maxDuration < 500, "Max parsing should be under 500ms, got " + maxDuration + "ms");
78+
}
79+
80+
System.out.println("\n✓ Performance benchmark completed successfully!");
81+
}
82+
83+
@Test
84+
public void testCachePerformance(@TempDir Path tempDir) throws Exception {
85+
System.out.println("\n=== Cache Performance Test ===");
86+
87+
// Create a moderately complex Cargo.toml
88+
Path cargoToml = tempDir.resolve("Cargo.toml");
89+
String content =
90+
"""
91+
[package]
92+
name = "cache-test-project"
93+
version = "1.0.0"
94+
edition = "2021"
95+
96+
[dependencies]
97+
serde = "1.0"
98+
tokio = { version = "1.0", features = ["full"] }
99+
anyhow = "1.0"
100+
clap = "4.0"
101+
reqwest = { version = "0.11", features = ["json"] }
102+
""";
103+
Files.writeString(cargoToml, content);
104+
105+
// First call (cache miss)
106+
long startTime1 = System.nanoTime();
107+
RustProvider provider1 = new RustProvider(cargoToml);
108+
var result1 = provider1.provideComponent();
109+
long duration1 = (System.nanoTime() - startTime1) / 1_000_000;
110+
111+
// Second call (should hit cache if cargo tree was executed)
112+
long startTime2 = System.nanoTime();
113+
RustProvider provider2 = new RustProvider(cargoToml);
114+
var result2 = provider2.provideComponent();
115+
long duration2 = (System.nanoTime() - startTime2) / 1_000_000;
116+
117+
// Third call (should also hit cache)
118+
long startTime3 = System.nanoTime();
119+
RustProvider provider3 = new RustProvider(cargoToml);
120+
var result3 = provider3.provideComponent();
121+
long duration3 = (System.nanoTime() - startTime3) / 1_000_000;
122+
123+
System.out.println("Cache Performance Results:");
124+
System.out.println(" First call (cache miss): " + duration1 + "ms");
125+
System.out.println(" Second call (cache hit): " + duration2 + "ms");
126+
System.out.println(" Third call (cache hit): " + duration3 + "ms");
127+
128+
// Verify cache effectiveness (second and third calls should be faster or similar)
129+
// Note: If cargo is not available, all calls should be fast and similar
130+
assertTrue(duration2 <= duration1 + 10, "Second call should not be significantly slower");
131+
assertTrue(duration3 <= duration1 + 10, "Third call should not be significantly slower");
132+
133+
// Verify results are consistent
134+
assertNotNull(result1);
135+
assertNotNull(result2);
136+
assertNotNull(result3);
137+
assertEquals(result1.buffer.length, result2.buffer.length);
138+
assertEquals(result1.buffer.length, result3.buffer.length);
139+
140+
System.out.println("✓ Cache performance test passed!");
141+
}
142+
143+
@Test
144+
public void testMemoryUsage(@TempDir Path tempDir) throws Exception {
145+
System.out.println("\n=== Memory Usage Test ===");
146+
147+
// Create a large Cargo.toml file
148+
Path cargoToml = createLargeCargoToml(tempDir, 500); // 500 dependencies
149+
150+
// Measure memory usage
151+
Runtime runtime = Runtime.getRuntime();
152+
153+
// Force GC before test
154+
System.gc();
155+
Thread.sleep(100);
156+
long memoryBefore = runtime.totalMemory() - runtime.freeMemory();
157+
158+
// Process the large file
159+
RustProvider provider = new RustProvider(cargoToml);
160+
var result = provider.provideComponent();
161+
162+
long memoryAfter = runtime.totalMemory() - runtime.freeMemory();
163+
long memoryUsed = memoryAfter - memoryBefore;
164+
165+
System.out.println("Memory Usage Results:");
166+
System.out.println(" Memory before: " + (memoryBefore / 1024 / 1024) + " MB");
167+
System.out.println(" Memory after: " + (memoryAfter / 1024 / 1024) + " MB");
168+
System.out.println(" Memory used: " + (memoryUsed / 1024 / 1024) + " MB");
169+
170+
// Memory usage should be reasonable (less than 100MB for a large project)
171+
assertTrue(
172+
memoryUsed < 100 * 1024 * 1024,
173+
"Memory usage should be under 100MB, got " + (memoryUsed / 1024 / 1024) + " MB");
174+
175+
// Verify the result is valid
176+
assertNotNull(result);
177+
assertTrue(result.buffer.length > 0);
178+
179+
String sbomContent = new String(result.buffer);
180+
assertTrue(sbomContent.contains("large-memory-test"));
181+
182+
System.out.println("✓ Memory usage test passed!");
183+
}
184+
185+
private List<Path> createPerformanceTestFiles(Path tempDir) throws Exception {
186+
List<Path> files = new ArrayList<>();
187+
188+
// Test file 1: Small project
189+
files.add(createSmallCargoToml(tempDir));
190+
191+
// Test file 2: Medium project
192+
files.add(createMediumCargoToml(tempDir));
193+
194+
// Test file 3: Large project
195+
files.add(createLargeCargoToml(tempDir, 200));
196+
197+
return files;
198+
}
199+
200+
private Path createSmallCargoToml(Path tempDir) throws Exception {
201+
Path cargoToml = tempDir.resolve("small-Cargo.toml");
202+
String content =
203+
"""
204+
[package]
205+
name = "small-test-project"
206+
version = "1.0.0"
207+
edition = "2021"
208+
209+
[dependencies]
210+
serde = "1.0"
211+
anyhow = "1.0"
212+
""";
213+
Files.writeString(cargoToml, content);
214+
return cargoToml;
215+
}
216+
217+
private Path createMediumCargoToml(Path tempDir) throws Exception {
218+
Path cargoToml = tempDir.resolve("medium-Cargo.toml");
219+
StringBuilder contentBuilder = new StringBuilder();
220+
contentBuilder.append(
221+
"""
222+
[package]
223+
name = "medium-test-project"
224+
version = "2.0.0"
225+
edition = "2021"
226+
227+
[dependencies]
228+
""");
229+
230+
// Add 50 dependencies
231+
for (int i = 1; i <= 50; i++) {
232+
contentBuilder.append(String.format("dep%d = \"1.0\"%n", i));
233+
}
234+
235+
Files.writeString(cargoToml, contentBuilder.toString());
236+
return cargoToml;
237+
}
238+
239+
private Path createLargeCargoToml(Path tempDir, int numDeps) throws Exception {
240+
Path cargoToml = tempDir.resolve("large-Cargo.toml");
241+
StringBuilder contentBuilder = new StringBuilder();
242+
contentBuilder.append(
243+
"""
244+
[package]
245+
name = "large-memory-test"
246+
version = "3.0.0"
247+
edition = "2021"
248+
249+
[dependencies]
250+
""");
251+
252+
// Add specified number of dependencies
253+
for (int i = 1; i <= numDeps; i++) {
254+
if (i % 10 == 0) {
255+
// Add some with ignore patterns
256+
contentBuilder.append(String.format("large-dep%d = \"1.0\" # trustify-da-ignore%n", i));
257+
} else {
258+
contentBuilder.append(String.format("large-dep%d = \"1.0\"%n", i));
259+
}
260+
}
261+
262+
contentBuilder.append(
263+
"""
264+
265+
[build-dependencies]
266+
""");
267+
268+
// Add build dependencies
269+
for (int i = 1; i <= numDeps / 4; i++) {
270+
contentBuilder.append(String.format("build-dep%d = \"1.0\"%n", i));
271+
}
272+
273+
Files.writeString(cargoToml, contentBuilder.toString());
274+
return cargoToml;
275+
}
276+
}

0 commit comments

Comments
 (0)