Skip to content

Commit f352f99

Browse files
committed
feat: update comparison logic to throw exceptions for KryoPlaceholder instances
1 parent 3bc4941 commit f352f99

4 files changed

Lines changed: 38 additions & 26 deletions

File tree

codeflash-java-runtime/src/main/java/com/codeflash/Comparator.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,18 @@ private static boolean compareInternal(Object orig, Object newObj,
284284
return false;
285285
}
286286

287-
// KryoPlaceholder means the Serializer couldn't serialize this part.
288-
// Skip comparison for placeholder fields — we can't compare what we couldn't serialize.
289-
if (orig instanceof KryoPlaceholder || newObj instanceof KryoPlaceholder) {
290-
return true;
287+
// Detect and reject KryoPlaceholder
288+
if (orig instanceof KryoPlaceholder) {
289+
KryoPlaceholder p = (KryoPlaceholder) orig;
290+
throw new KryoPlaceholderAccessException(
291+
"Cannot compare: original contains placeholder for unserializable object",
292+
p.getObjType(), p.getPath());
293+
}
294+
if (newObj instanceof KryoPlaceholder) {
295+
KryoPlaceholder p = (KryoPlaceholder) newObj;
296+
throw new KryoPlaceholderAccessException(
297+
"Cannot compare: new object contains placeholder for unserializable object",
298+
p.getObjType(), p.getPath());
291299
}
292300

293301
// Handle exceptions specially

codeflash-java-runtime/src/test/java/com/codeflash/ComparatorCorrectnessTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ void testAllPlaceholderSkips() throws Exception {
6060
String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
6161
Map<String, Object> result = parseJson(json);
6262

63-
// Placeholders are skipped during comparison (treated as matching) — counts as actual comparison
64-
assertTrue((Boolean) result.get("equivalent"));
65-
assertEquals(1, ((Number) result.get("actualComparisons")).intValue());
63+
assertFalse((Boolean) result.get("equivalent"));
64+
assertEquals(0, ((Number) result.get("actualComparisons")).intValue());
65+
assertTrue(((Number) result.get("skippedPlaceholders")).intValue() > 0);
6666
}
6767

6868
@Test
@@ -108,8 +108,8 @@ void testMixedRealAndPlaceholder() throws Exception {
108108
Map<String, Object> result = parseJson(json);
109109

110110
assertTrue((Boolean) result.get("equivalent"));
111-
// All 3 compared: 2 real values + 1 placeholder (placeholder skipped during deep compare)
112-
assertEquals(3, ((Number) result.get("actualComparisons")).intValue());
111+
assertEquals(2, ((Number) result.get("actualComparisons")).intValue());
112+
assertEquals(1, ((Number) result.get("skippedPlaceholders")).intValue());
113113
}
114114

115115
@Test

codeflash-java-runtime/src/test/java/com/codeflash/ComparatorTest.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -311,47 +311,49 @@ void testBothNullMessages() {
311311
class PlaceholderTests {
312312

313313
@Test
314-
@DisplayName("original contains placeholder: skipped, treated as matching")
314+
@DisplayName("original contains placeholder: throws exception")
315315
void testOriginalPlaceholder() {
316316
KryoPlaceholder placeholder = new KryoPlaceholder(
317317
"java.net.Socket", "<socket>", "error", "path"
318318
);
319319

320-
// Placeholders are skipped — comparison returns true (not comparable, so skip)
321-
assertTrue(Comparator.compare(placeholder, "anything"));
320+
assertThrows(KryoPlaceholderAccessException.class, () -> {
321+
Comparator.compare(placeholder, "anything");
322+
});
322323
}
323324

324325
@Test
325-
@DisplayName("new contains placeholder: skipped, treated as matching")
326+
@DisplayName("new contains placeholder: throws exception")
326327
void testNewPlaceholder() {
327328
KryoPlaceholder placeholder = new KryoPlaceholder(
328329
"java.net.Socket", "<socket>", "error", "path"
329330
);
330331

331-
assertTrue(Comparator.compare("anything", placeholder));
332+
assertThrows(KryoPlaceholderAccessException.class, () -> {
333+
Comparator.compare("anything", placeholder);
334+
});
332335
}
333336

334337
@Test
335-
@DisplayName("placeholder in nested structure: skipped, rest compared normally")
338+
@DisplayName("placeholder in nested structure: throws exception")
336339
void testNestedPlaceholder() {
337340
KryoPlaceholder placeholder = new KryoPlaceholder(
338341
"java.net.Socket", "<socket>", "error", "data.socket"
339342
);
340343

341344
Map<String, Object> map1 = new HashMap<>();
342345
map1.put("socket", placeholder);
343-
map1.put("name", "same");
344346

345347
Map<String, Object> map2 = new HashMap<>();
346348
map2.put("socket", "different");
347-
map2.put("name", "same");
348349

349-
// Placeholder field skipped, "name" field compared normally → equivalent
350-
assertTrue(Comparator.compare(map1, map2));
350+
assertThrows(KryoPlaceholderAccessException.class, () -> {
351+
Comparator.compare(map1, map2);
352+
});
351353
}
352354

353355
@Test
354-
@DisplayName("compareWithDetails with placeholder returns equal (skipped)")
356+
@DisplayName("compareWithDetails captures error message")
355357
void testCompareWithDetails() {
356358
KryoPlaceholder placeholder = new KryoPlaceholder(
357359
"java.net.Socket", "<socket>", "error", "path"
@@ -360,8 +362,9 @@ void testCompareWithDetails() {
360362
Comparator.ComparisonResult result =
361363
Comparator.compareWithDetails(placeholder, "anything");
362364

363-
assertTrue(result.isEqual());
364-
assertFalse(result.hasError());
365+
assertFalse(result.isEqual());
366+
assertTrue(result.hasError());
367+
assertNotNull(result.getErrorMessage());
365368
}
366369
}
367370

codeflash-java-runtime/src/test/java/com/codeflash/SerializerTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,8 @@ void testClassWithUnserializableAttribute() throws Exception {
268268
class PlaceholderAccessTests {
269269

270270
@Test
271-
@DisplayName("comparing objects with placeholder skips the placeholder field")
272-
void testPlaceholderComparisonSkips() throws Exception {
271+
@DisplayName("comparing objects with placeholder throws KryoPlaceholderAccessException")
272+
void testPlaceholderComparisonThrowsException() throws Exception {
273273
try (Socket socket = new Socket()) {
274274
Map<String, Object> data = new LinkedHashMap<>();
275275
data.put("socket", socket);
@@ -279,8 +279,9 @@ void testPlaceholderComparisonSkips() throws Exception {
279279

280280
KryoPlaceholder placeholder = (KryoPlaceholder) reloaded.get("socket");
281281

282-
// Placeholders are skipped during comparison — treated as matching
283-
assertTrue(Comparator.compare(placeholder, "anything"));
282+
assertThrows(KryoPlaceholderAccessException.class, () -> {
283+
Comparator.compare(placeholder, "anything");
284+
});
284285
}
285286
}
286287
}

0 commit comments

Comments
 (0)