Skip to content

Commit 2d30981

Browse files
abhisheksplunkAbhishek ChatterjeeCopilot
authored
Seed-Based Regression Testing Implementation in Java (#303)
* Implement seed storing for Java schema tests . Remember seeds of failing test cases and rerun * Implement seed storing for Java schema tests . Remember seeds of failing test cases and rerun * Resolving CoPilot comments * Update stefc/generator/testdata/seeds/java/README.md Co-authored-by: Copilot <[email protected]> * Regenerated the files after reverting the changes with protoc version v5.29.5 * Regenerated the files after reverting the changes with protoc version v5.29.5 * Regenerated the files after reverting the changes with protoc version v5.29.5 --------- Co-authored-by: Abhishek Chatterjee <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 6f2d3b8 commit 2d30981

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
This directory stores **random seeds that previously caused failures in Java-generated schema tests** (e.g. `*WriterTest`).
2+
3+
## Why this exists
4+
5+
- When a randomized Java writer/reader test fails, it prints the seed and **appends it** to the appropriate `*_seeds.txt` file in this directory.
6+
- On subsequent test runs, the seeds in these files are **replayed first** to ensure the bug does not regress.
7+
8+
## File naming convention
9+
Each file is named:
10+
`<javaPackage>_<RootStruct>_seeds.txt`
11+
Example:
12+
`com.example.gentest.json_like_Record_seeds.txt`
13+
14+
## Important
15+
- Only add seeds that were found by **Java** failures. Go/other-language seeds are not portable because RNGs differ.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
680413050164375
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1765233907441407000
2+
1765234703981295000

stefc/templates/java/writerTest.java.tmpl

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import static org.junit.jupiter.api.Assertions.*;
1111

1212
import java.io.ByteArrayInputStream;
1313
import java.io.EOFException;
14+
import java.nio.file.Files;
15+
import java.nio.file.Path;
16+
import java.nio.file.Paths;
17+
import java.nio.file.StandardOpenOption;
1418
import java.util.ArrayList;
1519
import java.util.Arrays;
1620
import java.util.List;
@@ -35,8 +39,9 @@ class {{.StructName}}WriterTest {
3539
return records;
3640
}
3741

38-
@Test
39-
void test{{.StructName}}WriteRead() throws Exception {
42+
boolean test{{.StructName}}WriteReadSeed(long seed) {
43+
boolean retVal = true;
44+
4045
List<WriterOptions.Builder> opts = Arrays.asList(
4146
WriterOptions.builder(),
4247
WriterOptions.builder().compression(Compression.Zstd),
@@ -61,12 +66,9 @@ class {{.StructName}}WriterTest {
6166
WriterOptions.builder().frameRestartFlags(FrameFlags.RestartCodecs).maxUncompressedFrameByteSize(500)
6267
);
6368

64-
// Choose a seed (non-pseudo) randomly. We will print the seed
65-
// on failure for easy reproduction.
66-
long seed1 = System.nanoTime();
67-
Random random = new Random(seed1);
69+
Random random = new Random(seed);
6870

69-
for (int optIdx=0; optIdx < opts.size(); optIdx++) {
71+
for (int optIdx = 0; optIdx < opts.size(); optIdx++) {
7072
WriterOptions.Builder opt = opts.get(optIdx);
7173
try {
7274
MemChunkWriter buf = new MemChunkWriter();
@@ -85,12 +87,44 @@ class {{.StructName}}WriterTest {
8587
{{.StructName}}Reader reader = new {{.StructName}}Reader(new ByteArrayInputStream(buf.getBytes()));
8688
for (int i = 0; i < records.size(); i++) {
8789
assertEquals(ReadResult.Success, reader.read(ReadOptions.none));
88-
assertTrue(reader.record.equals(records.get(i)), "record " + i + " seed " + seed1 + " optIdx " + optIdx);
90+
assertTrue(reader.record.equals(records.get(i)), "record " + i + " seed " + seed + " optIdx " + optIdx);
8991
}
9092
assertThrows(EOFException.class, () -> reader.read(ReadOptions.none));
9193
} catch (Exception e) {
92-
fail("seed " + seed1 + " optIdx " + optIdx + ": " + e.getMessage());
94+
System.out.printf("Test failed with seed %d optIdx %d: %s%n", seed, optIdx, e.getMessage());
95+
e.printStackTrace();
96+
retVal = false;
97+
}
98+
}
99+
100+
return retVal;
101+
}
102+
103+
@Test
104+
void test{{.StructName}}WriteRead() throws Exception {
105+
// Seed files are stored in-repo so previously-failing seeds can be replayed to prevent regressions.
106+
Path seedFilePath = Paths.get("../stefc/generator/testdata/seeds/java/{{ .PackageName }}_{{.StructName}}_seeds.txt");
107+
108+
if (Files.exists(seedFilePath)) {
109+
for (String line : Files.readAllLines(seedFilePath)) {
110+
String s = line.trim();
111+
if (s.isEmpty()) continue;
112+
long seed = Long.parseLong(s);
113+
System.out.printf("Testing with seed from file: %d%n", seed);
114+
boolean passed = test{{.StructName}}WriteReadSeed(seed);
115+
if (!passed) {
116+
fail("Previously-failing seed " + seed + " still fails");
117+
}
93118
}
94119
}
120+
121+
long seed = System.nanoTime();
122+
boolean succeeded = test{{.StructName}}WriteReadSeed(seed);
123+
if (!succeeded) {
124+
System.out.printf("Test failed with seed %d, adding to seed file%n", seed);
125+
Files.createDirectories(seedFilePath.getParent());
126+
Files.writeString(seedFilePath, seed + "\n", StandardOpenOption.CREATE, StandardOpenOption.APPEND);
127+
fail("Test failed with seed " + seed);
128+
}
95129
}
96-
}
130+
}

0 commit comments

Comments
 (0)