Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.

Commit 56095c6

Browse files
authored
issues/200: support for Class files w/o location information (#202)
1 parent 2a8bba5 commit 56095c6

File tree

4 files changed

+25
-55
lines changed

4 files changed

+25
-55
lines changed

skippy-core/src/main/java/io/skippy/core/Reason.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ public enum Category {
4545
*/
4646
NO_IMPACT_DATA_FOUND_FOR_TEST,
4747

48-
/**
49-
* Skippy was unable to obtain the location of the class file for the test.
50-
*/
51-
CLASS_FILE_LOCATION_UNAVAILABLE,
52-
5348
/**
5449
* Bytecode change in test detected.
5550
*/

skippy-core/src/main/java/io/skippy/core/SkippyTestApi.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,20 @@
2222
import java.nio.charset.StandardCharsets;
2323
import java.nio.file.Files;
2424
import java.nio.file.Path;
25-
import java.util.*;
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Stack;
2629
import java.util.concurrent.ConcurrentHashMap;
2730

31+
import static io.skippy.core.ClassUtil.getOutputFolder;
2832
import static io.skippy.core.JacocoUtil.mergeExecutionData;
2933
import static io.skippy.core.JacocoUtil.swallowJacocoExceptions;
3034
import static io.skippy.core.SkippyConstants.PREDICTIONS_LOG_FILE;
31-
import static io.skippy.core.ClassUtil.getOutputFolder;
3235
import static java.lang.System.lineSeparator;
33-
import static java.nio.file.StandardOpenOption.*;
36+
import static java.nio.file.StandardOpenOption.APPEND;
37+
import static java.nio.file.StandardOpenOption.CREATE;
3438
import static java.util.Arrays.asList;
35-
import static java.util.stream.Collectors.joining;
3639

3740
/**
3841
* API that is used by Skippy's JUnit libraries to query for skip-or-execute predictions and to trigger the generation of .exec files.
@@ -114,6 +117,9 @@ private static SkippyTestApi getInstance() {
114117
* @param tag the {@link TestTag}
115118
*/
116119
public void tagTest(Class<?> testClass, TestTag tag) {
120+
if (false == ClassUtil.locationAvailable(testClass)) {
121+
return;
122+
}
117123
skippyRepository.tagTest(testClass, tag);
118124
}
119125

@@ -125,6 +131,9 @@ public void tagTest(Class<?> testClass, TestTag tag) {
125131
*/
126132
public boolean testNeedsToBeExecuted(Class<?> test) {
127133
return Profiler.profile("SkippyTestApi#testNeedsToBeExecuted", () -> {
134+
if (false == ClassUtil.locationAvailable(test)) {
135+
return true;
136+
}
128137
try {
129138
// re-use prediction made for the first test method in a class for all subsequent test methods
130139
if (predictions.containsKey(test)) {
@@ -179,6 +188,9 @@ public boolean testNeedsToBeExecuted(Class<?> test) {
179188
*/
180189
public void before(Class<?> testClass, String testMethod) {
181190
Profiler.profile("SkippyTestApi#prepareExecFileGeneration", () -> {
191+
if (false == ClassUtil.locationAvailable(testClass)) {
192+
return;
193+
}
182194
swallowJacocoExceptions(() -> {
183195
IAgent agent = RT.getAgent();
184196
agent.reset();
@@ -193,6 +205,9 @@ public void before(Class<?> testClass, String testMethod) {
193205
*/
194206
public void beforeAll(Class<?> testClass) {
195207
Profiler.profile("SkippyTestApi#prepareExecFileGeneration", () -> {
208+
if (false == ClassUtil.locationAvailable(testClass)) {
209+
return;
210+
}
196211
swallowJacocoExceptions(() -> {
197212
IAgent agent = RT.getAgent();
198213
if (isNestedTest()) {
@@ -214,6 +229,9 @@ public void beforeAll(Class<?> testClass) {
214229
*/
215230
public void after(Class<?> testClass, String testMethod) {
216231
Profiler.profile("SkippyTestApi#after", () -> {
232+
if (false == ClassUtil.locationAvailable(testClass)) {
233+
return;
234+
}
217235
swallowJacocoExceptions(() -> {
218236
IAgent agent = RT.getAgent();
219237
skippyRepository.after(testClass, testMethod, agent.getExecutionData(true));
@@ -228,6 +246,9 @@ public void after(Class<?> testClass, String testMethod) {
228246
*/
229247
public void afterAll(Class<?> testClass) {
230248
Profiler.profile("SkippyTestApi#afterAll", () -> {
249+
if (false == ClassUtil.locationAvailable(testClass)) {
250+
return;
251+
}
231252
swallowJacocoExceptions(() -> {
232253
IAgent agent = RT.getAgent();
233254
var executionData = executionDataStack.lastElement();

skippy-core/src/main/java/io/skippy/core/TestImpactAnalysis.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,6 @@ PredictionWithReason predict(Class<?> testClazz, SkippyConfiguration configurati
139139
}
140140
var maybeAnalyzedTest = classFileContainer.getAnalyzedTestForTestClass(testClazz, analyzedTests);
141141

142-
if (false == ClassUtil.locationAvailable(testClazz)) {
143-
return PredictionWithReason.execute(new Reason(CLASS_FILE_LOCATION_UNAVAILABLE, Optional.empty()));
144-
}
145-
146142
if (maybeAnalyzedTest.isEmpty()) {
147143
return PredictionWithReason.execute(new Reason(NO_IMPACT_DATA_FOUND_FOR_TEST, Optional.empty()));
148144
}

skippy-core/src/test/java/io/skippy/core/TestImpactAnalysisPredictTest.java

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@
1919
import org.junit.jupiter.api.Nested;
2020
import org.junit.jupiter.api.Test;
2121

22-
23-
import java.io.IOException;
24-
import java.net.URISyntaxException;
25-
import java.nio.file.Files;
26-
import java.nio.file.Path;
2722
import java.util.Optional;
2823

2924
import static io.skippy.core.Prediction.EXECUTE;
@@ -46,43 +41,6 @@ void testNoTestImpactAnalysisFound() throws ClassNotFoundException {
4641
assertEquals(TEST_IMPACT_ANALYSIS_NOT_FOUND, predictionWithReason.reason().category());
4742
}
4843

49-
@Test
50-
void testTestClassWithoutLocationInformation() throws ClassNotFoundException, URISyntaxException, IOException {
51-
var testImpactAnalysis = TestImpactAnalysis.parse("""
52-
{
53-
"classes": {
54-
"0": {
55-
"name": "com.example.LeftPadderTest",
56-
"path": "com/example/LeftPadderTest.class",
57-
"outputFolder": "build/classes/java/test",
58-
"hash": "80E52EBA"
59-
}
60-
},
61-
"tests": [
62-
{
63-
"class": "0",
64-
"tags": ["PASSED"],
65-
"coveredClasses": ["0"]
66-
}
67-
]
68-
}
69-
""");
70-
var location = Class.forName("com.example.LeftPadderTest").getProtectionDomain().getCodeSource().getLocation();
71-
var pathToClassFile = Path.of(location.toURI()).resolve("com").resolve("example").resolve("LeftPadderTest.class");
72-
73-
class ByteArrayClassLoader extends ClassLoader {
74-
public Class<?> loadClassFromBytes(String className, byte[] classBytes) {
75-
return defineClass(className, classBytes, 0, classBytes.length);
76-
}
77-
}
78-
79-
var testClass = new ByteArrayClassLoader().loadClassFromBytes("com.example.LeftPadderTest", Files.readAllBytes(pathToClassFile));
80-
81-
var predictionWithReason = testImpactAnalysis.predict(testClass, SkippyConfiguration.DEFAULT, SkippyRepository.getInstance(SkippyConfiguration.DEFAULT));
82-
assertEquals(EXECUTE, predictionWithReason.prediction());
83-
assertEquals(CLASS_FILE_LOCATION_UNAVAILABLE, predictionWithReason.reason().category());
84-
}
85-
8644
@Test
8745
void testPredictNoChange() throws ClassNotFoundException {
8846
var testImpactAnalysis = TestImpactAnalysis.parse("""

0 commit comments

Comments
 (0)