Skip to content

Commit 4bcd026

Browse files
committed
Match compiler fuzz budgets to the generators' distinct shapes and assert newline and recompile invariance instead of non-null.
1 parent 9feb558 commit 4bcd026

1 file changed

Lines changed: 65 additions & 8 deletions

File tree

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/CompilerFuzzTestsSC.java

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
import smallcheck.annotations.Property;
99
import smallcheck.generators.SeriesGen;
1010

11+
import java.io.File;
12+
import java.io.IOException;
13+
import java.nio.charset.StandardCharsets;
14+
import java.nio.file.Files;
1115
import java.util.ArrayList;
1216
import java.util.Arrays;
1317
import java.util.List;
@@ -17,15 +21,26 @@
1721
@RunWith(SmallCheckRunner.class)
1822
public class CompilerFuzzTestsSC extends WurstScriptTest {
1923

20-
@Property(maxInvocations = 320)
24+
/**
25+
* How many structurally distinct programs each generator can actually produce.
26+
*
27+
* <p>buildRandomSingleProgram varies its shape with six bits of the seed - indent style,
28+
* tuple, interface, module, loop, callback - so there are 2^6 shapes; the rest of the seed
29+
* only renames packages and changes integer literals, which reaches no new compiler path.
30+
* Budgets above these counts recompile the same shapes under different names.
31+
*/
32+
private static final int SINGLE_PROGRAM_SHAPES = 64;
33+
private static final int CROSS_PACKAGE_SHAPES = 24;
34+
35+
@Property(maxInvocations = SINGLE_PROGRAM_SHAPES)
2136
public void generatedProgramsAreCrashFree(@From(RandomProgram.class) Program program) {
2237
CompilationResult result = runProgram(program);
2338

2439
Assert.assertNotNull(result);
2540
Assert.assertNotNull(result.getGui());
2641
}
2742

28-
@Property(maxInvocations = 64)
43+
@Property(maxInvocations = SINGLE_PROGRAM_SHAPES)
2944
public void generatedProgramsCompileForBothBackends(@From(RandomProgram.class) Program program) {
3045
assertCompilesForBothBackends(program, "generatedProgramsCompileForBothBackends");
3146
}
@@ -56,16 +71,58 @@ private void assertCompilesForBothBackends(Program program, String testName) {
5671
+ "\nsource:\n" + String.join("\n---\n", program.sources));
5772
}
5873

59-
@Property(maxInvocations = 180)
60-
public void mixedNewlineStylesAreCrashFree(@From(RandomProgram.class) Program program) {
74+
/**
75+
* Line endings are a lexer detail: the same source written with LF and with CRLF has to emit
76+
* byte-identical Jass. This previously only asserted that compiling the CRLF variant returned
77+
* non-null, which no realistic bug would violate.
78+
*/
79+
@Property(maxInvocations = SINGLE_PROGRAM_SHAPES)
80+
public void newlineStyleDoesNotAffectEmittedCode(@From(RandomProgram.class) Program program) {
6181
String alternateNewline = "\n".equals(program.newline) ? "\r\n" : "\n";
62-
CompilationResult result = runProgram(program.withNewline(alternateNewline));
6382

64-
Assert.assertNotNull(result);
65-
Assert.assertNotNull(result.getGui());
83+
String fromOriginal = compileAndReadJass(program, "newlineOriginal");
84+
String fromAlternate = compileAndReadJass(program.withNewline(alternateNewline), "newlineAlternate");
85+
86+
Assert.assertEquals(fromAlternate, fromOriginal,
87+
"line ending style changed the emitted Jass\nsource:\n" + String.join("\n---\n", program.sources));
88+
}
89+
90+
/**
91+
* The same source compiled twice in one process has to emit the same script. DeterministicChecks
92+
* pins this for a few hand-written programs; this runs it across every generated shape.
93+
*/
94+
@Property(maxInvocations = SINGLE_PROGRAM_SHAPES)
95+
public void compilingTwiceEmitsIdenticalCode(@From(RandomProgram.class) Program program) {
96+
String first = compileAndReadJass(program, "determinismFirst");
97+
String second = compileAndReadJass(program, "determinismSecond");
98+
99+
Assert.assertEquals(second, first,
100+
"recompiling the same source emitted different Jass\nsource:\n" + String.join("\n---\n", program.sources));
101+
}
102+
103+
/**
104+
* Compiles {@code program} and returns the unoptimised Jass it emitted. The output name is
105+
* explicit so two compilations inside one property do not overwrite each other's file.
106+
*/
107+
private String compileAndReadJass(Program program, String outputName) {
108+
CompilationResult result = testNamed(outputName)
109+
.setStopOnFirstError(false)
110+
.executeProg(false)
111+
.compilationUnits(asCompilationUnits(program));
112+
113+
Assert.assertTrue(result.getGui().getErrorList().isEmpty(),
114+
"generated program produced compiler diagnostics: " + result.getGui().getErrorList()
115+
+ "\nsource:\n" + String.join("\n---\n", program.sources));
116+
117+
File emitted = new File(TEST_OUTPUT_PATH + getClass().getSimpleName() + "_" + outputName + "_no_opts.j");
118+
try {
119+
return Files.readString(emitted.toPath(), StandardCharsets.UTF_8);
120+
} catch (IOException e) {
121+
throw new AssertionError("could not read emitted script " + emitted, e);
122+
}
66123
}
67124

68-
@Property(maxInvocations = 120)
125+
@Property(maxInvocations = CROSS_PACKAGE_SHAPES)
69126
public void crossPackageProgramsAreCrashFree(@From(CrossPackageProgram.class) Program program) {
70127
CompilationResult result = runProgram(program);
71128

0 commit comments

Comments
 (0)