From 0c99d74c85b937e5d566a7f2899d75025d9c54ea Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Thu, 17 Sep 2026 14:36:22 -0700 Subject: Get makefiles and fuzzer testing uniform across chapters --- Makefile | 16 ++ README.md | 12 +- build-support/chapter-release.mk | 7 + build-support/early-chapter.mk | 45 +++++ chapter01/Makefile | 1 + chapter02/Makefile | 1 + chapter03/Makefile | 1 + chapter04/Makefile | 3 + chapter05/Makefile | 1 + chapter06/Makefile | 3 + chapter07/Makefile | 3 + chapter08/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter09/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter10/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter11/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter12/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter13/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter14/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter15/Makefile | 5 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 23 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter16/Makefile | 3 + .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter17/Makefile | 3 + .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter18/Makefile | 3 + .../java/com/seaofnodes/simple/FuzzerWrap.java | 29 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter19/Makefile | 3 + .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter21/Makefile | 2 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter22/Makefile | 6 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter23/Makefile | 6 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter24/Makefile | 6 +- chapter24/README.md | 18 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 27 ++- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 14 ++ chapter25/Makefile | 3 + chapter25/README.md | 210 ++++++++++++--------- docs/chapter-backports.md | 176 +++++++++++++++++ 65 files changed, 1042 insertions(+), 197 deletions(-) create mode 100644 Makefile create mode 100644 build-support/chapter-release.mk create mode 100644 build-support/early-chapter.mk create mode 100644 chapter01/Makefile create mode 100644 chapter02/Makefile create mode 100644 chapter03/Makefile create mode 100644 chapter05/Makefile create mode 100644 docs/chapter-backports.md diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..0947e7fa --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +# Run chapter targets from the repository root. Override CHAPTERS for a review batch: +# make tests CHAPTERS="chapter21 chapter22" +# make -k tests # continue through failures, but return a failing status +.DEFAULT_GOAL := tests +CHAPTERS ?= $(sort $(patsubst %/pom.xml,%,$(wildcard chapter[0-9][0-9]/pom.xml))) +ACTIONS := tests tags release lib +CHAPTER_TARGETS := $(foreach action,$(ACTIONS),$(addsuffix /$(action),$(CHAPTERS))) + +# Keep baseline/review runs sequential, including when invoked with -j. +.NOTPARALLEL: +.PHONY: $(ACTIONS) tag $(CHAPTER_TARGETS) +tag: tags +$(foreach action,$(ACTIONS),$(eval $(action): $(addsuffix /$(action),$(CHAPTERS)))) + +$(CHAPTER_TARGETS): + +$(MAKE) -C $(dir $@) $(notdir $@) diff --git a/README.md b/README.md index e77a6d7a..9698e35a 100644 --- a/README.md +++ b/README.md @@ -68,4 +68,14 @@ The Simple language is styled after a subset of C or Java. * [Chapter 22](chapter22/README.md): A Simple Hello, World! * [Chapter 23](chapter23/README.md): Methods and Types Revisited * [Chapter 24](chapter24/README.md): Chained conditionals and SCCP -* [Chapter 25](chapter25/README.md): Modules and Separate Compilation +* [Chapter 25](chapter25/README.md): Modules, Separate Compilation, and SSA Construction with Incomplete Types + +## Building across chapters + +The top-level Makefile runs each chapter's `tests`, `tags` (also `tag`), +`release`, or `lib` target. Start with `make lib` on a fresh checkout, then +`make tests`. + +The [chapter backport queue](docs/chapter-backports.md) records proposed small +corrections and the per-chapter test/review workflow. Larger architectural moves +remain deferred while that workflow is established. diff --git a/build-support/chapter-release.mk b/build-support/chapter-release.mk new file mode 100644 index 00000000..74e216f4 --- /dev/null +++ b/build-support/chapter-release.mk @@ -0,0 +1,7 @@ +# Jar packaging for chapters predating the existing release target. +.PHONY: release +release: build/release/simple.jar + +build/release/simple.jar: $(main_classes) $(test_classes) + @mkdir -p build/release + jar cf $@ -C $(CLZDIR)/main . -C $(CLZDIR)/test . diff --git a/build-support/early-chapter.mk b/build-support/early-chapter.mk new file mode 100644 index 00000000..939b7782 --- /dev/null +++ b/build-support/early-chapter.mk @@ -0,0 +1,45 @@ +# Chapters 1-3 and 5 previously only had Maven builds. +# Included with the chapter as the working directory. +SHELL := /bin/bash +.DEFAULT_GOAL := tests +.DELETE_ON_ERROR: +SIMPLE := com/seaofnodes/simple +SRC := src/main/java +TST := src/test/java +CLZDIR := build/classes +SEP := $(if $(filter Windows_NT,$(OS)),;,:) +CTAGS ?= ctags +JAVAC_ARGS ?= -g +main_javas := $(wildcard $(SRC)/$(SIMPLE)/*.java $(SRC)/$(SIMPLE)/*/*.java) +test_javas := $(wildcard $(TST)/$(SIMPLE)/*.java $(TST)/$(SIMPLE)/*/*.java) +test_cp := $(patsubst $(TST)/$(SIMPLE)/%.java,com.seaofnodes.simple.%,$(wildcard $(TST)/$(SIMPLE)/*Test.java)) +jars := lib/junit-4.12.jar$(SEP)lib/hamcrest-core-1.3.jar +main_classes := $(CLZDIR)/main/.mtag +test_classes := $(CLZDIR)/test/.ttag + +$(main_classes): $(main_javas) + @mkdir -p $(CLZDIR)/main + javac $(JAVAC_ARGS) -d $(CLZDIR)/main $(main_javas) + @touch $@ + +$(test_classes): $(test_javas) $(main_classes) lib/junit-4.12.jar lib/hamcrest-core-1.3.jar + @mkdir -p $(CLZDIR)/test + javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -d $(CLZDIR)/test $(test_javas) + @touch $@ + +.PHONY: tests tags lib +tests: $(test_classes) + java -ea -cp "$(CLZDIR)/main$(SEP)$(CLZDIR)/test$(SEP)$(jars)" org.junit.runner.JUnitCore $(test_cp) + +tags: + $(CTAGS) -e --recurse=yes --fields=+fksaiS $(SRC) $(TST) + +lib: lib/junit-4.12.jar lib/hamcrest-core-1.3.jar +lib/junit-4.12.jar: + @mkdir -p lib + wget -O $@ https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar +lib/hamcrest-core-1.3.jar: + @mkdir -p lib + wget -O $@ https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar + +include ../build-support/chapter-release.mk diff --git a/chapter01/Makefile b/chapter01/Makefile new file mode 100644 index 00000000..86e9ebba --- /dev/null +++ b/chapter01/Makefile @@ -0,0 +1 @@ +include ../build-support/early-chapter.mk diff --git a/chapter02/Makefile b/chapter02/Makefile new file mode 100644 index 00000000..86e9ebba --- /dev/null +++ b/chapter02/Makefile @@ -0,0 +1 @@ +include ../build-support/early-chapter.mk diff --git a/chapter03/Makefile b/chapter03/Makefile new file mode 100644 index 00000000..86e9ebba --- /dev/null +++ b/chapter03/Makefile @@ -0,0 +1 @@ +include ../build-support/early-chapter.mk diff --git a/chapter04/Makefile b/chapter04/Makefile index c7724bc7..7f55b343 100644 --- a/chapter04/Makefile +++ b/chapter04/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter05/Makefile b/chapter05/Makefile new file mode 100644 index 00000000..86e9ebba --- /dev/null +++ b/chapter05/Makefile @@ -0,0 +1 @@ +include ../build-support/early-chapter.mk diff --git a/chapter06/Makefile b/chapter06/Makefile index 56f69e12..cb362b42 100644 --- a/chapter06/Makefile +++ b/chapter06/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -90,3 +91,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter07/Makefile b/chapter07/Makefile index 56f69e12..cb362b42 100644 --- a/chapter07/Makefile +++ b/chapter07/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -90,3 +91,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter08/Makefile b/chapter08/Makefile index 3c271042..742a9aef 100644 --- a/chapter08/Makefile +++ b/chapter08/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -66,7 +67,7 @@ $(test_classes): $(CLZDIR)/test/%class: $(TST)/%java $(main_classes) JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) - $(JVM) org.junit.runner.JUnitCore $(test_cp) + $(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap .PHONY: clean clean: @@ -90,3 +91,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 82a6a472..33434b6c 100644 --- a/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.FuzzerWrap#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Test @Ignore - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<100000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 5fcd8764..861c8b3a 100644 --- a/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -125,4 +125,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, false).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter09/Makefile b/chapter09/Makefile index b5c21b38..f141d0f6 100644 --- a/chapter09/Makefile +++ b/chapter09/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) @echo "testing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap fuzzer: $(default_targets) @echo "testing " $(test_cp) @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index bb3bec1c..019140ad 100644 --- a/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Ignore; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.FuzzerWrap#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Test @Ignore - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 936fb41e..b816ed86 100644 --- a/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -167,4 +167,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, false).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter10/Makefile b/chapter10/Makefile index c7724bc7..ed0a03c9 100644 --- a/chapter10/Makefile +++ b/chapter10/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) @echo "testing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap fuzzer: $(default_targets) @echo "fuzzing " $(test_cp) @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index bb3bec1c..019140ad 100644 --- a/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Ignore; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.FuzzerWrap#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Test @Ignore - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter11/Makefile b/chapter11/Makefile index c7724bc7..ed0a03c9 100644 --- a/chapter11/Makefile +++ b/chapter11/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) @echo "testing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap fuzzer: $(default_targets) @echo "fuzzing " $(test_cp) @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 699711fc..52c2a014 100644 --- a/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Ignore; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter12/Makefile b/chapter12/Makefile index c7724bc7..ed0a03c9 100644 --- a/chapter12/Makefile +++ b/chapter12/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) @echo "testing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap fuzzer: $(default_targets) @echo "fuzzing " $(test_cp) @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 699711fc..52c2a014 100644 --- a/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Ignore; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter13/Makefile b/chapter13/Makefile index c7724bc7..ed0a03c9 100644 --- a/chapter13/Makefile +++ b/chapter13/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) @echo "testing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap fuzzer: $(default_targets) @echo "fuzzing " $(test_cp) @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 699711fc..52c2a014 100644 --- a/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Ignore; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter14/Makefile b/chapter14/Makefile index c7724bc7..ed0a03c9 100644 --- a/chapter14/Makefile +++ b/chapter14/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) @echo "testing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap fuzzer: $(default_targets) @echo "fuzzing " $(test_cp) @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 699711fc..52c2a014 100644 --- a/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Ignore; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter15/Makefile b/chapter15/Makefile index c7724bc7..ed0a03c9 100644 --- a/chapter15/Makefile +++ b/chapter15/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" tests: $(default_targets) @echo "testing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap fuzzer: $(default_targets) @echo "fuzzing " $(test_cp) @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 699711fc..52c2a014 100644 --- a/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -8,15 +8,30 @@ import org.junit.Ignore; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); diff --git a/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter16/Makefile b/chapter16/Makefile index c14ffa27..ed0a03c9 100644 --- a/chapter16/Makefile +++ b/chapter16/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index eaeed8e2..2c98f405 100644 --- a/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter17/Makefile b/chapter17/Makefile index c14ffa27..ed0a03c9 100644 --- a/chapter17/Makefile +++ b/chapter17/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: # for printing variable values @@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index eaeed8e2..2c98f405 100644 --- a/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 51e39cd0..a82a7517 100644 --- a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -182,4 +182,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter18/Makefile b/chapter18/Makefile index f42659de..c2e1b060 100644 --- a/chapter18/Makefile +++ b/chapter18/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: @@ -103,3 +104,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index d67b2065..1427c99d 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,32 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run only this chapter's fixed regression seeds. Open failures + * and exploratory fuzzing are opt-in: run their methods directly in the IDE, + * or temporarily remove the relevant Ignore annotation when investigating. + * Seeds belong to this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + 973358943756616234L, // Parser failure on nullable field access after dead code + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +40,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index f57a0031..9f2ffa4d 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -172,4 +172,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter19/Makefile b/chapter19/Makefile index 4c1e1f49..3a6faaff 100644 --- a/chapter19/Makefile +++ b/chapter19/Makefile @@ -1,4 +1,5 @@ SHELL := /bin/bash +# Shared packaging target; included after chapter source variables below. .DELETE_ON_ERROR: @@ -103,3 +104,5 @@ lib/hamcrest-core-1.3.jar: tags: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) + +include ../build-support/chapter-release.mk diff --git a/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index d67b2065..b022bec9 100644 --- a/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index f7e0c4ad..612dec65 100644 --- a/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -171,4 +171,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index d67b2065..b022bec9 100644 --- a/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 30a548ad..40d69a62 100644 --- a/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -171,4 +171,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter21/Makefile b/chapter21/Makefile index 0d187b7c..055c632a 100644 --- a/chapter21/Makefile +++ b/chapter21/Makefile @@ -111,4 +111,4 @@ lib/hamcrest-core-1.3.jar: # Build emacs tags (part of a tasty emacs ide experience) tags: $(main_javas) $(test_javas) @rm -f TAGS - @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST) + @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) diff --git a/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index d67b2065..b022bec9 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 30a548ad..40d69a62 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -171,4 +171,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter22/Makefile b/chapter22/Makefile index 6547a73a..deca5c76 100644 --- a/chapter22/Makefile +++ b/chapter22/Makefile @@ -89,7 +89,7 @@ $(CLZDIR)/main/.mtag: $(main_classes) $(main_javas) @# This crap is really just "javac", but with a beautiful error message. @# The very very long list of java files is suppressed and counted. @# The required output "Note: blah blah deprecated blah" is suppressed. - $(file > .argsM.txt , $(OODM)) + $(file >.argsM.txt,$(OODM)) @if [ ! -z "$(OODM)" ] ; then \ echo "compiling main because " $< " and " `wc -w < .argsM.txt` " more files" ; \ if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(OODM) >& .out.txt ; then \ @@ -111,7 +111,7 @@ $(main_classes): $(CLZDIR)/main/%class: $(SRC)/%java OODT := $(CLZDIR)/test/.ttag: $(test_classes) $(test_javas) $(CLZDIR)/main/.mtag @[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test - $(file > .argsT.txt $(OODT)) + $(file >.argsT.txt,$(OODT)) @if [ ! -z "$(OODT)" ] ; then \ echo "compiling test because " $< " and " `wc -w < .argsT.txt` " more files" ; \ if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(OODT) >& .out.txt ; then \ @@ -177,4 +177,4 @@ lib/hamcrest-core-1.3.jar: # Build emacs tags (part of a tasty emacs ide experience) tags: $(main_javas) $(test_javas) @rm -f TAGS - @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST) + @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) diff --git a/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index d67b2065..b022bec9 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 30a548ad..40d69a62 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -171,4 +171,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter23/Makefile b/chapter23/Makefile index 6547a73a..deca5c76 100644 --- a/chapter23/Makefile +++ b/chapter23/Makefile @@ -89,7 +89,7 @@ $(CLZDIR)/main/.mtag: $(main_classes) $(main_javas) @# This crap is really just "javac", but with a beautiful error message. @# The very very long list of java files is suppressed and counted. @# The required output "Note: blah blah deprecated blah" is suppressed. - $(file > .argsM.txt , $(OODM)) + $(file >.argsM.txt,$(OODM)) @if [ ! -z "$(OODM)" ] ; then \ echo "compiling main because " $< " and " `wc -w < .argsM.txt` " more files" ; \ if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(OODM) >& .out.txt ; then \ @@ -111,7 +111,7 @@ $(main_classes): $(CLZDIR)/main/%class: $(SRC)/%java OODT := $(CLZDIR)/test/.ttag: $(test_classes) $(test_javas) $(CLZDIR)/main/.mtag @[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test - $(file > .argsT.txt $(OODT)) + $(file >.argsT.txt,$(OODT)) @if [ ! -z "$(OODT)" ] ; then \ echo "compiling test because " $< " and " `wc -w < .argsT.txt` " more files" ; \ if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(OODT) >& .out.txt ; then \ @@ -177,4 +177,4 @@ lib/hamcrest-core-1.3.jar: # Build emacs tags (part of a tasty emacs ide experience) tags: $(main_javas) $(test_javas) @rm -f TAGS - @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST) + @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) diff --git a/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index d67b2065..b022bec9 100644 --- a/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 77e61b42..962202a6 100644 --- a/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -171,4 +171,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter24/Makefile b/chapter24/Makefile index 6547a73a..deca5c76 100644 --- a/chapter24/Makefile +++ b/chapter24/Makefile @@ -89,7 +89,7 @@ $(CLZDIR)/main/.mtag: $(main_classes) $(main_javas) @# This crap is really just "javac", but with a beautiful error message. @# The very very long list of java files is suppressed and counted. @# The required output "Note: blah blah deprecated blah" is suppressed. - $(file > .argsM.txt , $(OODM)) + $(file >.argsM.txt,$(OODM)) @if [ ! -z "$(OODM)" ] ; then \ echo "compiling main because " $< " and " `wc -w < .argsM.txt` " more files" ; \ if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(OODM) >& .out.txt ; then \ @@ -111,7 +111,7 @@ $(main_classes): $(CLZDIR)/main/%class: $(SRC)/%java OODT := $(CLZDIR)/test/.ttag: $(test_classes) $(test_javas) $(CLZDIR)/main/.mtag @[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test - $(file > .argsT.txt $(OODT)) + $(file >.argsT.txt,$(OODT)) @if [ ! -z "$(OODT)" ] ; then \ echo "compiling test because " $< " and " `wc -w < .argsT.txt` " more files" ; \ if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(OODT) >& .out.txt ; then \ @@ -177,4 +177,4 @@ lib/hamcrest-core-1.3.jar: # Build emacs tags (part of a tasty emacs ide experience) tags: $(main_javas) $(test_javas) @rm -f TAGS - @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST) + @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) diff --git a/chapter24/README.md b/chapter24/README.md index 22fda399..3774c80a 100644 --- a/chapter24/README.md +++ b/chapter24/README.md @@ -3,8 +3,12 @@ In this Chapter we allow *chaining* conditionals, e.g. `(60 <= score < 90)` where `score` in the middle does not have to be repeated. -We also present an Interprocedural Sparse Conditional Constant Propagation -algorithm. +The main compiler change is Interprocedural Sparse Conditional Constant +Propagation (SCCP): an optimistic type solve which also discovers call-graph +edges. This snapshot still uses the earlier parser/type and memory machinery; +Chapter 25 currently contains the larger incomplete-types SSA redesign and +separate compilation. Independent correctness fixes will move backward first; +see the [backport review queue](../docs/chapter-backports.md). You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter24) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter23...linear-chapter24) it to the previous chapter. @@ -37,7 +41,7 @@ if (60 <= score < 90) { This checks if score is between 60 (inclusive) and 90 (exclusive), without repeating `score`. Expressions that mix opposite directions of comparison, like using both -`<=` and `=>`, are not allowed, because they create ambiguous logic. +`<=` and `>=`, are not allowed by Simple's direction rule. ```java if (a <= b >= c) @@ -52,7 +56,8 @@ Stacked comparisons are *only* valid if all the comparison operators - `>` with `>` - `>` with `>=` -All operators above can be combined with `==` and `!=`. +Equality operators (`==` and `!=`) have their normal, lower precedence; they +are not links in a relational chain. It is not allowed to mix directions in a single chain: ```java @@ -87,7 +92,8 @@ a < b && b == c ``` Comparisons can be chained together in any length, and different comparison operators may be mixed freely as long as they point in the same direction. -Equality(`==`) and inequality(`!=`) operators may be combined with any other comparisons at any point in the chain. +Each intermediate operand is evaluated once. Later operands are evaluated only +if earlier comparisons succeed, so calls and updates in a chain short-circuit. E.g: ``` @@ -167,7 +173,7 @@ Without SCCP, this small program cannot remove the computation of `x` and hence the loop either. A glance at the program tells us `x` must always be a `1` but the bottom-up (pessimistic) approach decides that since x1 is BOT, x2 must be BOT so x1 must be BOT. We need the optimistic approach to break the -statemate, and just *assume* x1 is `1`, and then we can discover than x2 is +stalemate, and just *assume* x1 is `1`, and then we can discover that x2 is also a `1`. diff --git a/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index d67b2065..b022bec9 100644 --- a/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -7,15 +7,30 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; /** - * To use the fuzzer run the fuzzPeeps methods. - * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line - * via mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps + * Normal tests run chapter-local regression seeds. Open failures and exploratory + * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). + * Seeds depend on this chapter's generator; do not copy them between chapters. */ public class FuzzerWrap { + private static final long[] REGRESSION_SEEDS = { + }; + + private static final long[] OPEN_FAILING_SEEDS = { + }; + + @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } + @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } + + private static void fuzzPeepsSeeds(long... seeds) { + var fuzzer = new Fuzzer(); + for (long seed : seeds) + fuzzer.fuzzPeepsRegression(seed); + } + @Ignore @Test - public void fuzzPeeps() { + public void fuzzPeepsLarge() { var fuzzer = new Fuzzer(); for (int i=0; i<1000000; i++) fuzzer.fuzzPeeps(i); @@ -23,8 +38,8 @@ public class FuzzerWrap { } - @Test - public void fuzzPeepsSmall() { + @Test @Ignore + public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); var fuzzer = new Fuzzer(); for (int i=0; i<100; i++) diff --git a/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 77e61b42..962202a6 100644 --- a/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -171,4 +171,18 @@ public class Fuzzer { public boolean noExceptions() { return exceptions.isEmpty(); } + + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ + public void fuzzPeepsRegression(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + try { + runCheck(sb.toString(), valid); + } catch (Throwable e) { + AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); + ae.initCause(e); + throw ae; + } + } } diff --git a/chapter25/Makefile b/chapter25/Makefile index f07f5d4b..9cacde76 100644 --- a/chapter25/Makefile +++ b/chapter25/Makefile @@ -236,6 +236,9 @@ lib/hamcrest-core-1.3.jar: @(cd lib; wget https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar) # Build emacs tags (part of a tasty emacs ide experience) +.PHONY: tags +tags: TAGS + TAGS: $(main_javas) $(test_javas) @rm -f TAGS @$(CTAGS) -e --recurse=yes --fields=+fksaiS $(SRC) $(TST) diff --git a/chapter25/README.md b/chapter25/README.md index 10cfe7e1..971c4746 100644 --- a/chapter25/README.md +++ b/chapter25/README.md @@ -1,86 +1,124 @@ -# Chapter 25: Modules - - -You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter22...linear-chapter24) it to the previous chapter. - - -## Modules - -Goal: A Module plan (not yet a "system") -Goal: No syntax for the starter 1-file-1-line helloWorld.smp "module" -Goal: External symbols are in ".o" files, like Java ".class" - not an source file #include like C/C++ - -Needs a search strategy for ".o" files, so they mirror the source code directory structure. -Means there is a "build" root for .o files. -Means the CLI has a --flag for the build root, or some sensible default ($cwd/build) - -Projects with subdirectories pick up all symbols qualified names from the path-to-project-root - -Goal: under no circumstances can you load from an uninitialized variable (although you can see default inits for fields that have defaults). -Goal: nested tree-structured name spaces, to control complexity. -Goal: field decl order within a file is fairly flexible. - - -## more - -Has a defined project root (default: CWD). - -Has a path to root: %PATH, e.g. $ROOT/a/b/c/ - -Has a file name: $ROOT/ a/b/c/ foo.smp - -Everything in a dir is implicitly in name-space above it. - -Suppose `foo.smp` contains `PI=3.14;` - -Then the fully qualified name is: a.b.c.foo.PI - -Code is implicitly in the `struct a.b.c.foo {}` namespace. - -Final constant values can be accessed by direct names. - -Non-finals or non-constant need a constructed object. - - - -Top-level code in `foo.smp` is implicitly in the foo class init, -which runs the first time foo is touched, -which requires a "has been init" touch on every possible-first-access, -which is every non-local ("sideways") reference. - -Always the init for parents are done before children, -so `a` completes before `a.b`, and so on. - -Means `a` init can NOT reference e.g. `b,c,foo`, although -you can define top-level fcns that refer to `b`. - -Legal in `a.smp`: `val make_b = { -> new b{} };` // fcn is final constant - -Legal in `a.smp`: `val b = new b{} ;` // not constant, so requires instance & is done - -Means making an `a` instance in a's , will also run a's -which can refer to instance fcns, which can refer to not-init finals? -Limit: all fcns can only refer to instance vars *before* them. - - - -Would like to make golden instances, static singletons: -val special = new a{/*anything already in-scope and ed*/}; - - - -The default 1-file main then: -$ROOT==$CWD -Code is implicitly in the *empty* name space, is in the for the empty -name space and gets run on project start. - ------- - -Implications for e.g. sys: - - .../simple/sys.smp - top level, has sys.parseAryI64 - ../simple/sys/sys.io.smp - has `struct io { ... }` - - -For now just gather the exported TYPE symbols, and a count of them, and export/input ELF. -NO: for now split into `sys` class from instance, \ No newline at end of file +# Chapter 25: Modules, Separate Compilation, and SSA Construction + +This chapter compiles Simple source files into reusable object files, loads +their types and ideal IR into later compilations, and links native programs +against a separately built system library. It also contains a substantial +parser, type, and memory-SSA redesign to support incomplete information. + +These are the contents of the current snapshot. Independent correctness fixes +will move to earlier chapters first; moving the larger SSA redesign backward, +or splitting this chapter, is deferred. See the [backport queue](../docs/chapter-backports.md). + +You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25) +in the [linear history](https://github.com/SeaOfNodes/Simple/tree/linear) and +[compare it with Chapter 24](https://github.com/SeaOfNodes/Simple/compare/linear-chapter24...linear-chapter25). + +## Compilation units and names + +`CompUnit` represents a source or object file. `ParseAll` discovers dependencies, +parses needed sources, and loads previously compiled units. Source and build +trees use corresponding relative paths; class names use dotted paths. For +example, `a/b/foo.smp` supplies the namespace `a.b.foo`. + +Each unit has Start/Stop nodes. A function's ownership by a compilation unit is +distinct from an edge representing unknown external callers: retaining code +for emission must not imply that arbitrary callers can reach it. + +The driver accepts `--root` for the project root, `-o` for output, and `-L` for +external Simple object search paths, including an object file or a directory. +The compiler also resolves external C symbols for native linking. Names beginning +with `_` control privacy; public reachability depends on enclosing names too. + +## Object files contain ideal IR + +The ELF writer stores native code and a `.simple` section containing canonical +types, ideal graph nodes, and dependencies. The reader reconstructs that graph +so imported functions can participate in optimization, including inlining. +Calls that remain out of line are resolved by native linking. + +`GlobalBits` maps file identities and per-file indices to local dense indices +for aliases, function targets, and return points. Equal local numbers from +independent compilations must not be mistaken for equal identities. Types are +closed and upgraded after loading, including cross-unit cyclic references. + +Serialization preserves declaration types separately from sharpened flow types. +Every semantic node/type field needs matching read/write support. The source's +expensive serialization bijection check is currently disabled, and the larger +incremental-rebuild test `testModule0` is ignored. Neither is an active +validation guarantee. + +## Constructing SSA before types are complete + +Simple still parses directly to SSA without an AST. Forward references mean an +expression's eventual type may be unavailable while parsing it. Syntax and +resolved lexical binding therefore choose topology; types subsequently refine, +optimize, and validate the graph. + +- Arithmetic nodes defer integer versus floating-point mode selection, including + resolution through recursive graph components after SCCP. +- Calls preserve their syntactic receiver slot before the callee kind is settled. + Symbolic field information defers layout-dependent decisions. +- Loads and ordinary Phis no longer carry parser-supplied lower-bound types. +- `TypeScalar` represents integer, float, memory-pointer, and function-pointer + values separately from global control/memory bottom. +- `TypeStruct._open` describes an incomplete field set; `_fref` records the + absence of an authoritative definition. Discovering fields does not define a type. +- Guards carry branch-proven zero/nonzero facts before the input family is known. + `FunPtrNode` retains an explicit edge to its function's return graph. + +The driver runs parsing, pessimistic iteration, SCCP, and final type checking +before serialization and machine selection. Nontrivial inlining is coordinated +with iteration instead of always occurring as an immediate peephole. + +## Memory and construction + +The parser carries ordinary bulk memory. The optimizer recovers precise aliases +using `MemMerge`, `MemPhi`, and `BulkMemPhi`. A bulk Phi covers all aliases +except its exclusions, represented by parallel precise Phis. Every slice must +be covered exactly once. + +User constructors are declared and called as follows: + +```java +struct Box { + i64[] values; + new Box = { i64[] initial -> values = initial; }; +}; +return new Box(new i64[1]); +``` + +An allocation helper is generated at the declaration. It allocates, calls the +hidden `` and then the user constructor, and publishes initialized private +memory through `EscapeNode`s. Parser `Var` metadata detects early field reads +and missing initialization on constructor exits. Store widths come from target +declarations, not from the values being stored. + +File-level code is represented by a class initializer (``), distinct +from instance initialization. The older [module design notes](module.md) and +[roadmap](ROADMAP.md) contain proposals and alternatives, especially concerning +initialization order; they are not a specification of all implemented rules. + +## System library and examples + +The former single `sys.smp` library is organized into units for libc bindings, +I/O, characters, scanners, arrays, growable buffers, and bitsets. The native +driver supports real program arguments. Examples in `docs/examples` include +Bubble Sort, Capitalize, Dijkstra, and FileIO. + +`Chapter25Test` exercises library loading and native linking, including a Hello +World call deliberately kept out of line. That test checks the client's external +symbol before linking against `sys.o`. Other tests cover forward constructors, +field updates through calls, literal escapes, and reduced compiler failures. +Independent regressions are candidates for earlier chapters. + +## Building and checking + +From this directory, `make lib` supplies Java test dependencies and `make tests` +runs the raw chapter suites, standalone suites, system tests, and fuzzer +regressions. `make release` builds the compiler jar and native library artifacts; +`make tags` builds editor tags. Native tools and the selected CPU/ABI must match +the environment; the Makefile currently defaults to x86-64/win64. + +Compiler changes can invalidate both serialized IR and native code in `sys.o`. +Rebuild it before interpreting linked-program test results. A source-only subset +is not equivalent to this chapter's full `make tests`. diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md new file mode 100644 index 00000000..4e7e2a69 --- /dev/null +++ b/docs/chapter-backports.md @@ -0,0 +1,176 @@ +# Chapter backport review + +This is a work queue, not a record of completed compiler backports. Independent +corrections should start in the earliest affected chapter and propagate through +every later affected snapshot. A regression in Chapter 25's `Chapter21Test` +does not test the compiler in the `chapter21` directory. + +For now, keep building SSA with incomplete types in Chapter 25. Moving that +architecture earlier, or splitting Chapter 25, is deferred while small changes +establish the review workflow. No renumbering is committed. + +## First two review candidates + +| ID | Small change | Destination | Evidence and regression | +|---|---|---|---| +| B01 | Encode a 64-bit memory comparison with a 32-bit immediate | 21, then 22-24; 25 already fixed | `CmpMemX86.encoding`: `case 3: enc.add8(_imm)` becomes `add4`. Keep each chapter's type accessor. Check instruction length/bytes and a comparison followed by another instruction. | +| B02 | Correct immediate-multiply REX register order | 22, then 23-24; audit 21 separately | `MulIX86.encoding`: `rex(src,dst,0)` becomes `rex(dst,src,0)`. Exercise source and destination on opposite sides of register 8. Chapter 21 uses `ImmX86`, not this one-line implementation. | + +The initial proposal was B01 followed by B02. The Chapter 18 investigation below +now supplies another small first-review candidate, B11. Review one correction's +reduced failure, smallest patch, and test results before starting another. +No compiler fixes have been applied with this build and documentation change. + +## Subsequent small changes + +An **audit** item needs an old-chapter reproducer before it is scheduled. Copy +the correction into the chapter's representation, not the entire modern file. + +| ID | Change | Proposed destination | Scope and acceptance evidence | +|---|---|---|---| +| B03 | Correct x86 logical-not encoding | 21 through 24 | `NotX86`: preserve a shared source/destination until after testing; correct SETcc operand fields and zero-extend its byte result. Check zero/nonzero, overlapping registers, and byte registers requiring REX. Exclude Not/Phi optimizations. | +| B04 | Preserve comparison results while releasing parser temporaries | 24 | `parseComparison` lifetime fix in `414931c8`. Reduce seed `-8212834489697770130`; exclude unrelated `` cleanup in that commit. | +| B05 | Preserve RHS control, memory, and local updates in logical OR | 23 through 24; **audit** earliest implementation | Chapter 25 `parseIf` retains `_scope` for the false-side `||` expression. Test scalar and field updates and short-circuit behavior. Avoid importing GuardNode. | +| B06 | Decode string escapes; reject unknown/truncated escapes | 22 through 24 | Literal scanner only. Check newline, tab, carriage return, backslash, quote, NUL, and malformed input. | +| B07 | Decode character escapes; reject truncated literals | 22 through 24 | Separate patch sharing B06's helper. Check values and errors; `parseChar` first appears in 22. | +| B08 | Complete `&=` and `|=` syntax sugar | 17 onward; **audit** availability of bitwise operators | A small language addition, not a correctness-only patch. Move lexer and assignment expansion together. Check locals, fields, arrays, and exactly-once address evaluation. | +| B09 | Make diagnostic function lookup read-only | 18 onward; **audit** first mutating printer lookup | Separate `lookupFun` from cleanup-performing `link`. Verify printing preserves graph/linker state. Do not import FunPtrNode or compilation units for this fix. | +| B10 | Preserve widening state in integer nonzero refinement | **Audit** first chapter with both widening and `nonZero`; no later than 24 | Chapter 25 `TypeInteger.nonZero` preserves `_widen`. Check lattice laws and loop refinement. Exclude TypeScalar, storage-type, and serialization changes. | +| B11 | Diagnose return types using the optimized return expression | 18; 19 already has the correction | `ReturnNode.err()` uses `expr()._type` instead of the parse-time `mt` aggregate. Chapter 18 rejects `struct S { u8 x; }; return new S; return 0;` with a mixed integer/reference error; Chapter 19 accepts it. Also test genuinely reachable incompatible returns. This is a candidate, not yet an applied or isolated-patch-verified fix. | + +After the first two reviews, batch only corrections with established independence +and regressions. Keep one logical correction per commit across affected chapters. + +## Larger or entangled changes: defer + +| Group | Eventual home | Why not in the small queue yet | +|---|---|---| +| Dominator caches, scheduling, global constant cloning | 11 / 18 / 19 as applicable | Current changes mix inlining, scheduling, and compilation-unit ownership. Need a reduced old-IR failure. | +| Register allocation and spilling | 20 onward | Need constrained-register/spill regressions; changed golden spill counts are insufficient evidence. | +| Conditional Store and array Load control | Memory/arrays chapters | Reproduce under the earlier alias model before extracting fixes from the new memory implementation. | +| SCCP dependencies, function revival, reachability | 24; some foundations may fit 18 | Separate old-IR corrections from new Guard/Escape/BulkMemPhi and external-caller machinery. | +| Lattice fixes in `7f3b8856` | Owning type introduction | The commit mixes TypeFunPtr, TypeMem, and new XInt/escape-summary behavior. Not a generic earlier-lattice patch. | +| TypeScalar, numeric modes, guards, symbolic fields, open/forward types | Revisit earlier homes later | A connected incomplete-types architecture, including phase ordering and errors. | +| BulkMemPhi/MemPhi, private constructor memory, allocation helpers | Revisit memory / constructors / methods | Move invariants and regressions together. | +| Serialization, global identity remapping, module escape summaries | Separate compilation | Remain with modules. | + +## Review and test protocol + +1. Run the destination's unmodified `make tests`. Record baseline failures and + resolve them before accepting a compiler backport into that chapter. +2. Add a deterministic regression proving the old failure. Prefer tiny source + programs; use encoding checks when register placement is the property at issue. +3. Apply the smallest correction; run the focused regression and full chapter + `make tests`. Stop for Cliff's review before the next trial fix. +4. Propagate the accepted fix and regression through every affected later + snapshot. Run each snapshot's full `make tests`, not just 25's inherited tests. +5. Run TypeTest for lattice changes and rebuild system objects when compiler + changes invalidate their IR/native code. Force a full Java rebuild when shared + APIs or constants change: older Makefiles do not track all Java dependencies. + Do not weaken checks or drop tests. +6. Record chapters, commands, and results here. Keep unrelated dirty changes; + do not push without explicit approval. + +The top-level runner supports all 25 chapters: + +```sh +make lib # dependencies, before tests on a fresh checkout +make tests +make -k tests # visit all chapters; failures still fail the command +make tests CHAPTERS="chapter21 chapter22" +make tags release CHAPTERS="chapter01 chapter24 chapter25" +``` + +Runs are sequential, and child failures propagate. `make tag` aliases `tags`. +Java, GNU make, bash, ctags (for tags), and the existing native test tools must +be on PATH. Early release targets package classes into jars; they do not imply +standalone executable compiler drivers. Older Makefiles discover jars when make +starts, so run `make lib` separately before `make tests`. + +## Validation record + +All compiler backports B01-B11 are pending. + +On 2026-09-17, using Windows/Cygwin and OpenJDK 22.0.2: + +| Check | Result | +|---|---| +| All 25 chapter `tags` targets | Passed via `make -k tags`. | +| All 25 chapter `release` targets | Passed, in two batches (1-22 and 23-25). | +| `make -k tests`: Chapters 1-17, 19-21, 23-25 | Passed each complete chapter target. | +| Chapter 18 | 300 ordinary tests passed; `fuzzPeepsSmall` failed on seed `973358943756616234`. This test chooses seeds from wall-clock time. Preserve the failure rather than rerunning until green. | +| Chapter 22, initial run | `testLatticeTheory` failed with a TypeConAry/TypeRPC cast error. | +| `make -B tests CHAPTERS=chapter22` | Passed after full rebuild: 369 ordinary tests and the fuzzer test. This is consistent with stale classes; the original failure remains recorded. | + +That initial test baseline was **not green** because of Chapter 18. The follow-up +below records the explicitly requested change to default fuzzing coverage; the +underlying compiler failure remains open. + +Chapter 25 initially could not run its system suite because the Hello World +example was deleted in the working tree. Cliff restored it; the final full run +passed (362 raw0, 32 raw1, 7 standalone, 16 system, and 1 fuzzer test). + +Build-only fixes made during setup: supply missing early Makefiles and release +targets, alias Chapter 25's `TAGS` as `tags`, use the existing older-chapter ctags +option spelling in 21-24, and correct malformed `$(file ...)` argument-list +writes in 22-24. These changes do not move compiler behavior between chapters. + +Local logs (ignored build artifacts): `build/review/tests-verified.log`, +`build/review/chapter22-rebuilt-tests.log`, `build/review/tags-verified.log`, +`build/review/release-01-22.log`, and `build/review/release-23-25.log`. + +The Chapter 18 fuzzer printed this reduced input: + +```java +struct s0 { + u8 v1; +}; +s0? !UmPOLQK=null; +if(0) + while(0&UmPOLQK.v1) {} +if(UmPOLQK.v1) {} +return new s0; +while(0) {} +``` + +### Deterministic fuzzer follow-up + +Chapters 8-24 now follow Chapter 25's seed-list structure. Each wrapper has its +own `REGRESSION_SEEDS` and ignored `OPEN_FAILING_SEEDS`. Exploratory fuzzing is +opt-in, including the previously automatic random runs. Chapters 8-15 now invoke +their wrappers from `make tests` as well. Fixed-seed failures throw immediately +with the seed and original cause, without entering the exploratory reducer. +Each chapter retains its generator and checking strategy; seeds are not copied +between chapters. + +The regression lists start empty. Chapter 18's open list contains only +`973358943756616234`; explicitly invoking that list still reproduces the failure. +Passing default tests therefore does not mean that this compiler bug is fixed +or that new fuzz regression coverage has been established. After a correction, +move its seed to that chapter's regression list once the full seed passes. + +Validation: `make -k tests` passed across all 25 chapters after the harness +changes. After adding wrapper invocation to Chapters 8-15, their complete +`make tests` targets passed again. Logs: `build/review/seed-lists-all-tests.log`, +`build/review/seed-lists-08-15-tests.log`, and +`build/review/chapter18-open-seed.log` (intentional failure). + +The reduced Chapter 18 source exposes two separate concerns: + +- With peepholes disabled, nullable field lookup can fail during parsing; + with peepholes enabled, the reduced original reaches a mixed-return error. + Chapter 19 changes the fuzzer comparison to two optimizer worklist seeds, + rather than peepholes disabled/enabled. That harness change deserves separate + review; it is not proof that the compiler discrepancy was corrected. +- The mixed-return diagnostic reduces further to + `struct S { u8 x; }; return new S; return 0;`. Chapter 18 rejects it with + peepholes both disabled and enabled. Chapter 19 accepts it under both fuzzer + worklist orders. A plain `return new S;` is accepted in both chapters. The + corresponding Chapter 19 `ReturnNode.err()` change is B11 above. Dead + `if(0) return 0;` and a trailing `while(0) {}` also trigger the Chapter 18 + error. No compiler code has been changed yet. + +Additional source probes across Chapters 19-25 show differing treatment of +unused nullable loads. A live `return p.x;` with null `p` was rejected in every +tested snapshot. Keep the original seed open: B11 alone has not been shown to +resolve its separate parser/checking discrepancy. -- cgit v1.2.3