From ec4696a6084369a10db650f9df17c074bd12e669 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 18:59:36 -0700 Subject: [PATCH 01/10] Make SplitterFactoryTest's main run its tests; fix test drivers' main methods and usage messages --- java/daikon/test/SampleTester.java | 31 ++++++++++-- .../split/SplitterFactoryTestUpdater.java | 47 ++++++++++++++----- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/java/daikon/test/SampleTester.java b/java/daikon/test/SampleTester.java index 40919edb6b..aa0888a8a5 100644 --- a/java/daikon/test/SampleTester.java +++ b/java/daikon/test/SampleTester.java @@ -67,7 +67,7 @@ public class SampleTester { /** The usage message for this program. */ private static String usage = StringsPlume.joinLines( - "Usage: java daikon.PrintInvariants [OPTION]... FILE", + "Usage: java daikon.test.SampleTester [OPTION]...", " -h, --" + Daikon.help_SWITCH, " Display this usage message", " --" + Daikon.config_option_SWITCH, @@ -78,8 +78,29 @@ public class SampleTester { " Specify a class, varinfos, and ppt to debug track.", " Format is class@ppt"); - @SuppressWarnings("JUnitMethodInvoked") // main() runs the tests without JUnit + /** + * Runs the tests without JUnit. + * + * @param args command-line arguments, like those of {@link #mainHelper} + * @throws IOException if there is a problem reading a test file + */ public static void main(String[] args) throws IOException { + try { + mainHelper(args); + } catch (Daikon.DaikonTerminationException e) { + Daikon.handleDaikonTerminationException(e); + } + } + + /** + * This does the work of {@link #main(String[])}, but it never calls System.exit, so it is + * appropriate to be called programmatically. + * + * @param args command-line arguments, like those of {@link #main} + * @throws IOException if there is a problem reading a test file + */ + @SuppressWarnings("JUnitMethodInvoked") // mainHelper() runs the tests without JUnit + public static void mainHelper(String[] args) throws IOException { LongOpt[] longopts = new LongOpt[] { @@ -89,7 +110,7 @@ public static void main(String[] args) throws IOException { new LongOpt(Daikon.track_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), }; - Getopt g = new Getopt("daikon.test.SampleTester", args, "h:", longopts); + Getopt g = new Getopt("daikon.test.SampleTester", args, "h", longopts); int c; while ((c = g.getopt()) != -1) { switch (c) { @@ -135,6 +156,10 @@ public static void main(String[] args) throws IOException { break; } } + if (g.getOptind() < args.length) { + throw new Daikon.UserError( + "Unexpected argument " + args[g.getOptind()] + "; run with -h for usage"); + } daikon.LogHelper.setupLogs(Global.debugAll ? FINE : INFO); diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index 0c806f6071..bf348fdf42 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -195,7 +195,11 @@ private static String getTestClassText() { ps.println("import java.io.*;"); ps.println("import java.util.*;"); ps.println("import junit.framework.*;"); + ps.println("import org.junit.BeforeClass;"); ps.println("import org.junit.Test;"); + ps.println("import org.junit.runner.JUnitCore;"); + ps.println("import org.junit.runner.Result;"); + ps.println("import org.junit.runner.notification.Failure;"); ps.println("import org.plumelib.util.FilesPlume;"); ps.println("import org.plumelib.util.StringsPlume;"); ps.println("import org.plumelib.util.UtilPlume;"); @@ -229,11 +233,6 @@ private static String getTestClassText() { ps.println(" // java files it produces, changing the order that the setUpTests"); ps.println(" // commands are run will cause the tests to fail."); ps.println(); - ps.println(" /** Do not instantiate. */"); - ps.println(" private SplitterFactoryTest() {"); - ps.println(" throw new Error(\"Do not instantiate\");"); - ps.println(" }"); - ps.println(); ps.println(" private static String targetDir = \"" + targetDir + "\";"); ps.println(); ps.println(" private static @Nullable String tempDir = null;"); @@ -242,12 +241,21 @@ private static String getTestClassText() { ps.println(); ps.println(" private static String usage ="); ps.println(" StringsPlume.joinLines("); - ps.println(" \"Usage: java daikon.tools.CreateSpinfo FILE.java ...\","); + ps.println(" \"Usage: java daikon.test.split.SplitterFactoryTest [OPTION]...\","); ps.println( - " \" -s Save (do not delete) the splitter java files in the temp directory\","); - ps.println(" \" -h Display this usage message\");"); + " \" -s Save (do not delete) the splitter java files in the temp" + + " directory\","); + ps.println(" \" -h Display this usage message\");"); ps.println(); ps.println(" public static void main(String[] args) {"); + ps.println(" try {"); + ps.println(" mainHelper(args);"); + ps.println(" } catch (Daikon.DaikonTerminationException e) {"); + ps.println(" Daikon.handleDaikonTerminationException(e);"); + ps.println(" }"); + ps.println(" }"); + ps.println(); + ps.println(" public static void mainHelper(String[] args) {"); ps.println( " Getopt g = new Getopt(\"daikon.test.split.SplitterFactoryTest\", args, \"hs\");"); ps.println(" int c;"); @@ -258,8 +266,7 @@ private static String getTestClassText() { ps.println(" break;"); ps.println(" case 'h':"); ps.println(" System.out.println(usage);"); - ps.println(" System.exit(1);"); - ps.println(" break;"); + ps.println(" throw new Daikon.NormalTermination();"); ps.println(" case '?':"); ps.println(" break;"); ps.println(" default:"); @@ -267,6 +274,21 @@ private static String getTestClassText() { ps.println(" break;"); ps.println(" }"); ps.println(" }"); + ps.println(" if (g.getOptind() < args.length) {"); + ps.println(" throw new Daikon.UserError("); + ps.println( + " \"Unexpected argument \" + args[g.getOptind()] + \"; run with -h for" + + " usage\");"); + ps.println(" }"); + ps.println(" Result result = JUnitCore.runClasses(SplitterFactoryTest.class);"); + ps.println(" for (Failure failure : result.getFailures()) {"); + ps.println(" System.err.println(failure.getTrace());"); + ps.println(" }"); + ps.println(" if (!result.wasSuccessful()) {"); + ps.println( + " throw new Daikon.UserError(StringsPlume.nPlural(result.getFailureCount(), \"test\")" + + " + \" failed\");"); + ps.println(" }"); ps.println(" }"); ps.println(); @@ -320,7 +342,8 @@ private static String getTestClassText() { * SplitterFactoryTest to set up the needed files to run the tests on. */ public static void appendSetUpTest(PrintStream ps) { - ps.println(" private static void setUpTests() {"); + ps.println(" @BeforeClass"); + ps.println(" public static void setUpTests() {"); ps.println(" List spinfoFiles;"); ps.println(" List declsFiles;"); for (int i = 0; i < spinfoFileLists.size(); i++) { @@ -350,7 +373,7 @@ private static void appendTests(PrintStream ps) { ps.println(); for (String className : classNames) { ps.println(" @Test"); - ps.println(" public static void test" + className + "() {"); + ps.println(" public void test" + className + "() {"); ps.println(" assertEqualFiles(\"" + className + ".java\");"); ps.println(" }"); ps.println(); From 8aaa95b2d66a4b2738585d9f80764a64472f0998 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 19:09:16 -0700 Subject: [PATCH 02/10] Accept --help in SampleTester; name failing tests in SplitterFactoryTest; remove unused locals --- java/daikon/test/SampleTester.java | 1 + java/daikon/test/split/SplitterFactoryTestUpdater.java | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/daikon/test/SampleTester.java b/java/daikon/test/SampleTester.java index aa0888a8a5..e2c2adbaba 100644 --- a/java/daikon/test/SampleTester.java +++ b/java/daikon/test/SampleTester.java @@ -104,6 +104,7 @@ public static void mainHelper(String[] args) throws IOException { LongOpt[] longopts = new LongOpt[] { + new LongOpt(Daikon.help_SWITCH, LongOpt.NO_ARGUMENT, null, 0), new LongOpt(Daikon.config_option_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), new LongOpt(Daikon.debugAll_SWITCH, LongOpt.NO_ARGUMENT, null, 0), new LongOpt(Daikon.debug_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index bf348fdf42..df255d9b95 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -282,12 +282,13 @@ private static String getTestClassText() { ps.println(" }"); ps.println(" Result result = JUnitCore.runClasses(SplitterFactoryTest.class);"); ps.println(" for (Failure failure : result.getFailures()) {"); + ps.println(" System.err.println(\"Failure in \" + failure.getTestHeader() + \":\");"); ps.println(" System.err.println(failure.getTrace());"); ps.println(" }"); ps.println(" if (!result.wasSuccessful()) {"); ps.println( - " throw new Daikon.UserError(StringsPlume.nPlural(result.getFailureCount(), \"test\")" - + " + \" failed\");"); + " throw new Daikon.UserError(StringsPlume.nPlural(result.getFailureCount()," + + " \"failure\"));"); ps.println(" }"); ps.println(" }"); ps.println(); @@ -344,8 +345,6 @@ private static String getTestClassText() { public static void appendSetUpTest(PrintStream ps) { ps.println(" @BeforeClass"); ps.println(" public static void setUpTests() {"); - ps.println(" List spinfoFiles;"); - ps.println(" List declsFiles;"); for (int i = 0; i < spinfoFileLists.size(); i++) { ps.println(" createSplitterFiles("); ps.println( From c52ece8ec12679d86fc037c83bb829614eed9c57 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 19:11:58 -0700 Subject: [PATCH 03/10] Run SplitterFactoryTest from the junit target --- java/Makefile | 13 ++++--------- java/daikon/test/AllTestsSuite.java | 7 +++---- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/java/Makefile b/java/Makefile index ce436e5d69..a886ae1bc7 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1219,7 +1219,7 @@ check-serialize: .PHONY: junit -junit: daikon/Daikon.class +junit: daikon/Daikon.class daikon/test/split/SplitterFactoryTest.class @echo "Running unit (not system) tests..." LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite ifeq (${JAVA24}, 1) @@ -1227,14 +1227,9 @@ ifeq (${JAVA24}, 1) @# under versions of Java that do not have java.lang.classfile. LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite24 endif - -## TODO: This target is never used!!! -junit-all: junit - @# Alternately, MasterUnitTester could run SplitterFactoryTest, - @# but I'm having trouble with that, apparently due to hard-coded - @# directory paths. And these tests are slow, so we want them to - @# be separate anyway. - ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTest + @# SplitterFactoryTest is not in AllTestsSuite, because SplitterFactoryTest.java + @# is generated after everything else is compiled. + LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTest ### ### Clover (code coverage) diff --git a/java/daikon/test/AllTestsSuite.java b/java/daikon/test/AllTestsSuite.java index 7d74e7e7ac..4f7cf959b2 100644 --- a/java/daikon/test/AllTestsSuite.java +++ b/java/daikon/test/AllTestsSuite.java @@ -34,9 +34,8 @@ daikon.test.TestQuant.class, daikon.test.TestAnnotate.class, daikon.test.DtraceDiffTester.class, - // , - // // I'm having trouble with this; need to fix, reinstate, and not call - // // specially from Makefile. -MDE 7/8/2005 - // daikon.test.split.SplitterFactoryTest.class + // daikon.test.split.SplitterFactoryTest is run separately by the Makefile's + // "junit" target, because SplitterFactoryTest.java is generated after + // everything else is compiled. }) public final class AllTestsSuite {} From 4255747e7e0c8391bf44ae30c12a7740a9ea1910 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 21:14:11 -0700 Subject: [PATCH 04/10] Do not overwrite goal files when generating SplitterFactoryTest; fix junit target dependences SplitterFactoryTestUpdater no longer overwrites existing goal files, so SplitterFactoryTest does not compare SplitterFactory's output against itself. The junit target builds SplitterFactoryTest.class only after Daikon.class, and SplitterFactoryTest.class is rebuilt when Daikon changes. Co-Authored-By: Claude Opus 5.5 --- java/Makefile | 11 +++++++---- java/daikon/test/AllTestsSuite.java | 5 ++++- .../test/split/SplitterFactoryTestUpdater.java | 16 ++++++++++------ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/java/Makefile b/java/Makefile index a886ae1bc7..b4c18dd81d 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1219,7 +1219,10 @@ check-serialize: .PHONY: junit -junit: daikon/Daikon.class daikon/test/split/SplitterFactoryTest.class +junit: daikon/Daikon.class + @# SplitterFactoryTest.class is not a prerequisite, so that under "make -j" it is not + @# built concurrently with daikon/Daikon.class. + ${MAKE} ${AUTO_GENERATED_CLASSES_2} @echo "Running unit (not system) tests..." LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite ifeq (${JAVA24}, 1) @@ -1227,8 +1230,7 @@ ifeq (${JAVA24}, 1) @# under versions of Java that do not have java.lang.classfile. LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite24 endif - @# SplitterFactoryTest is not in AllTestsSuite, because SplitterFactoryTest.java - @# is generated after everything else is compiled. + @# SplitterFactoryTest is not in AllTestsSuite; see the comment there. LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTest ### @@ -1470,7 +1472,8 @@ daikon/test/split/SplitterFactoryTest.java: daikon/test/split/SplitterFactoryTes (${JAVA_COMMAND} daikon.test.split.SplitterFactoryTestUpdater > /dev/null 2>&1) || ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTestUpdater chmod a-w $@ -${AUTO_GENERATED_CLASSES_2}: ${AUTO_GENERATED_FILES_2} +# SplitterFactoryTest calls Daikon's methods, so recompile it when Daikon changes. +${AUTO_GENERATED_CLASSES_2}: ${AUTO_GENERATED_FILES_2} daikon/Daikon.class ${JAVAC_COMMAND} ${AUTO_GENERATED_FILES_2} # Create the auto-generated files with jpp diff --git a/java/daikon/test/AllTestsSuite.java b/java/daikon/test/AllTestsSuite.java index 4f7cf959b2..0b1051f3b1 100644 --- a/java/daikon/test/AllTestsSuite.java +++ b/java/daikon/test/AllTestsSuite.java @@ -36,6 +36,9 @@ daikon.test.DtraceDiffTester.class, // daikon.test.split.SplitterFactoryTest is run separately by the Makefile's // "junit" target, because SplitterFactoryTest.java is generated after - // everything else is compiled. + // everything else is compiled, and because SplitterFactoryTest must run in + // its own JVM: SplitterFactory numbers the splitter files it creates using a + // JVM-wide counter, so other tests that create splitters would change the + // file names that SplitterFactoryTest expects. }) public final class AllTestsSuite {} diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index df255d9b95..b0f6ecac3e 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -25,7 +25,8 @@ *
    \rm -f targets/*.java.goal SplitterFactoryTest.java
* * Then simply run the main method without any arguments in the daikon/java directory, and then - * re-compile the SplitterFactoryTest. + * re-compile the SplitterFactoryTest. This program does not overwrite existing goal files, so + * goal files that are not removed are not updated. * *

To add additional tests to this test program, place the .spinfo and decls files into the * "targets" directory then add a call to generateSplitters with the new files. generateSplitters is @@ -43,10 +44,11 @@ public class SplitterFactoryTestUpdater { private SplitterFactoryTestUpdater() {} // blocks public constructor /** - * If one has changed the test cases used below, for best results run {@code rm *.java.goal} while - * in the targets directory before running this method. Creates new splitter java files, moves the - * new files into target directory, rewrites the code of SplitterFactoryTest to use the new files. - * One should recompile SplitterFactoryTest after running this method. + * If one has changed the test cases used below, run {@code rm *.java.goal} while in the targets + * directory before running this method. Creates new splitter java files, moves the new files into + * target directory (except where a goal file already exists), and rewrites the code of + * SplitterFactoryTest to use the new files. One should recompile SplitterFactoryTest after + * running this method. * * @param args are ignored */ @@ -154,7 +156,9 @@ private static void moveFile(String fromName, String toName) { // + " when copying from " + fromName); // } if (to.exists()) { - to.delete(); + // Do not overwrite an existing goal file. Otherwise, a run of this program would make + // SplitterFactoryTest compare SplitterFactory's output against itself. + return; } // file.renameTo(to) fails if the two files are on different file systems // (e.g., /tmp and /scratch may be different). From edc8871b5f64fc8ecc178537fad768ced1fde022 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 21:15:23 -0700 Subject: [PATCH 05/10] Reject invalid options in SampleTester and SplitterFactoryTest Co-Authored-By: Claude Opus 5.5 --- java/daikon/test/SampleTester.java | 3 ++- java/daikon/test/split/SplitterFactoryTestUpdater.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/java/daikon/test/SampleTester.java b/java/daikon/test/SampleTester.java index e2c2adbaba..67a911c9fc 100644 --- a/java/daikon/test/SampleTester.java +++ b/java/daikon/test/SampleTester.java @@ -150,7 +150,8 @@ public static void mainHelper(String[] args) throws IOException { throw new Daikon.NormalTermination(); case '?': - break; // getopt() already printed an error + // getopt() already printed an error message that names the option. + throw new Daikon.UserError("Invalid option; run with -h for usage"); default: System.out.println("getopt() returned " + c); diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index b0f6ecac3e..1f27881dd0 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -272,7 +272,8 @@ private static String getTestClassText() { ps.println(" System.out.println(usage);"); ps.println(" throw new Daikon.NormalTermination();"); ps.println(" case '?':"); - ps.println(" break;"); + ps.println(" // getopt() already printed an error message that names the option."); + ps.println(" throw new Daikon.UserError(\"Invalid option; run with -h for usage\");"); ps.println(" default:"); ps.println(" System.out.println(\"getopt() returned \" + c);"); ps.println(" break;"); From 9d284dd30533dc661f5848115108644ab051d62e Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 11:59:05 -0700 Subject: [PATCH 06/10] Reject unknown options in test drivers; report SplitterFactoryTest failures by exit status; run SplitterFactoryTest from make junit Also make the generated mainHelper private, fix Javadoc, and document --debugAll in SampleTester's usage message. --- java/Makefile | 11 +++----- java/daikon/test/AllTestsSuite.java | 7 +++-- java/daikon/test/SampleTester.java | 9 ++++--- .../split/SplitterFactoryTestUpdater.java | 26 ++++++++++++++----- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/java/Makefile b/java/Makefile index ce436e5d69..69bc75de71 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1227,14 +1227,9 @@ ifeq (${JAVA24}, 1) @# under versions of Java that do not have java.lang.classfile. LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite24 endif - -## TODO: This target is never used!!! -junit-all: junit - @# Alternately, MasterUnitTester could run SplitterFactoryTest, - @# but I'm having trouble with that, apparently due to hard-coded - @# directory paths. And these tests are slow, so we want them to - @# be separate anyway. - ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTest + @# SplitterFactoryTest is not in AllTestsSuite, because it must run in its + @# own JVM: it depends on the sequential numbering of splitter files. + LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTest ### ### Clover (code coverage) diff --git a/java/daikon/test/AllTestsSuite.java b/java/daikon/test/AllTestsSuite.java index 7d74e7e7ac..2bbf9d93ea 100644 --- a/java/daikon/test/AllTestsSuite.java +++ b/java/daikon/test/AllTestsSuite.java @@ -34,9 +34,8 @@ daikon.test.TestQuant.class, daikon.test.TestAnnotate.class, daikon.test.DtraceDiffTester.class, - // , - // // I'm having trouble with this; need to fix, reinstate, and not call - // // specially from Makefile. -MDE 7/8/2005 - // daikon.test.split.SplitterFactoryTest.class + // daikon.test.split.SplitterFactoryTest is not in this suite, because it must run in its own + // JVM: it depends on the sequential numbering of splitter files. The Makefile's "junit" + // target runs it separately. }) public final class AllTestsSuite {} diff --git a/java/daikon/test/SampleTester.java b/java/daikon/test/SampleTester.java index e2c2adbaba..fec8775b72 100644 --- a/java/daikon/test/SampleTester.java +++ b/java/daikon/test/SampleTester.java @@ -72,6 +72,8 @@ public class SampleTester { " Display this usage message", " --" + Daikon.config_option_SWITCH, " Specify a configuration option ", + " --" + Daikon.debugAll_SWITCH, + " Turn on all debug flags (voluminous output)", " --" + Daikon.debug_SWITCH, " Specify a logger to enable", " --" + Daikon.track_SWITCH, @@ -81,7 +83,7 @@ public class SampleTester { /** * Runs the tests without JUnit. * - * @param args command-line arguments, like those of {@link #mainHelper} + * @param args command-line arguments; see {@link #usage} * @throws IOException if there is a problem reading a test file */ public static void main(String[] args) throws IOException { @@ -96,7 +98,7 @@ public static void main(String[] args) throws IOException { * This does the work of {@link #main(String[])}, but it never calls System.exit, so it is * appropriate to be called programmatically. * - * @param args command-line arguments, like those of {@link #main} + * @param args command-line arguments; see {@link #usage} * @throws IOException if there is a problem reading a test file */ @SuppressWarnings("JUnitMethodInvoked") // mainHelper() runs the tests without JUnit @@ -150,7 +152,8 @@ public static void mainHelper(String[] args) throws IOException { throw new Daikon.NormalTermination(); case '?': - break; // getopt() already printed an error + // getopt() already printed an error + throw new Daikon.UserError("Run with -h for usage"); default: System.out.println("getopt() returned " + c); diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index df255d9b95..e44087c468 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -249,13 +249,23 @@ private static String getTestClassText() { ps.println(); ps.println(" public static void main(String[] args) {"); ps.println(" try {"); - ps.println(" mainHelper(args);"); + ps.println(" if (!mainHelper(args)) {"); + ps.println(" System.exit(1);"); + ps.println(" }"); ps.println(" } catch (Daikon.DaikonTerminationException e) {"); ps.println(" Daikon.handleDaikonTerminationException(e);"); ps.println(" }"); ps.println(" }"); ps.println(); - ps.println(" public static void mainHelper(String[] args) {"); + ps.println(" /**"); + ps.println(" * This does the work of {@link #main(String[])}, but it never calls System.exit."); + ps.println(" * It may be called only once per JVM, because SplitterFactory numbers the files it"); + ps.println(" * produces sequentially and setUpTests sets static state."); + ps.println(" *"); + ps.println(" * @param args the command-line arguments; see {@link #usage}"); + ps.println(" * @return true if all the tests passed"); + ps.println(" */"); + ps.println(" private static boolean mainHelper(String[] args) {"); ps.println( " Getopt g = new Getopt(\"daikon.test.split.SplitterFactoryTest\", args, \"hs\");"); ps.println(" int c;"); @@ -268,7 +278,8 @@ private static String getTestClassText() { ps.println(" System.out.println(usage);"); ps.println(" throw new Daikon.NormalTermination();"); ps.println(" case '?':"); - ps.println(" break;"); + ps.println(" // getopt() already printed an error"); + ps.println(" throw new Daikon.UserError(\"Run with -h for usage\");"); ps.println(" default:"); ps.println(" System.out.println(\"getopt() returned \" + c);"); ps.println(" break;"); @@ -287,9 +298,10 @@ private static String getTestClassText() { ps.println(" }"); ps.println(" if (!result.wasSuccessful()) {"); ps.println( - " throw new Daikon.UserError(StringsPlume.nPlural(result.getFailureCount()," - + " \"failure\"));"); + " System.err.println(\"SplitterFactoryTest: \" +" + + " StringsPlume.nPlural(result.getFailureCount(), \"failure\"));"); ps.println(" }"); + ps.println(" return result.wasSuccessful();"); ps.println(" }"); ps.println(); @@ -339,8 +351,8 @@ private static String getTestClassText() { } /** - * Appends the code to write the static block of code to code. This code is used by the - * SplitterFactoryTest to set up the needed files to run the tests on. + * Writes the {@code @BeforeClass} method setUpTests to {@code ps}. SplitterFactoryTest uses + * setUpTests to generate the splitter files that its tests check. */ public static void appendSetUpTest(PrintStream ps) { ps.println(" @BeforeClass"); From 0d4569fc047aea4044f6b0e679207913cf747ec6 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 12:13:27 -0700 Subject: [PATCH 07/10] Reset splitter numbering in SplitterFactoryTest; run it via JUnitCore; rebuild it when its inputs change; tidy usage messages --- java/Makefile | 11 ++++---- java/daikon/split/SplitterFactory.java | 9 +++++++ java/daikon/test/AllTestsSuite.java | 6 ++--- java/daikon/test/SampleTester.java | 13 +++++----- .../split/SplitterFactoryTestUpdater.java | 26 ++++++------------- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/java/Makefile b/java/Makefile index 69bc75de71..878ffe8e3a 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1219,7 +1219,7 @@ check-serialize: .PHONY: junit -junit: daikon/Daikon.class +junit: daikon/Daikon.class ${AUTO_GENERATED_CLASSES_2} @echo "Running unit (not system) tests..." LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite ifeq (${JAVA24}, 1) @@ -1227,9 +1227,9 @@ ifeq (${JAVA24}, 1) @# under versions of Java that do not have java.lang.classfile. LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite24 endif - @# SplitterFactoryTest is not in AllTestsSuite, because it must run in its - @# own JVM: it depends on the sequential numbering of splitter files. - LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTest + @# SplitterFactoryTest is not in AllTestsSuite, because SplitterFactoryTest + @# is generated after the rest of Daikon (including AllTestsSuite) is compiled. + LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.split.SplitterFactoryTest ### ### Clover (code coverage) @@ -1463,7 +1463,8 @@ daikon/test/split/SplitterFactoryTestUpdater.class: daikon/test/split/SplitterFa ${JAVAC_COMMAND} $< ## First, a file not generated via java-cpp. -daikon/test/split/SplitterFactoryTest.java: daikon/test/split/SplitterFactoryTestUpdater.class +daikon/test/split/SplitterFactoryTest.java: daikon/test/split/SplitterFactoryTestUpdater.class \ + $(wildcard daikon/test/split/targets/*.spinfo daikon/test/split/targets/*.decls) rm -f $@ # Has expected errors while compiling splitter files. # Suppress output unless whole command fails. diff --git a/java/daikon/split/SplitterFactory.java b/java/daikon/split/SplitterFactory.java index f7c590b287..af9d52a1d3 100644 --- a/java/daikon/split/SplitterFactory.java +++ b/java/daikon/split/SplitterFactory.java @@ -144,6 +144,15 @@ public static void load_splitters(PptTopLevel ppt, List spfiles) { Global.debugSplit.fine("<> load_splitters"); } + /** + * Resets the counter that numbers splitter files, so that the names of subsequently-written + * splitter files do not depend on what splitters were created earlier in this JVM. Accessible for + * the purpose of testing. + */ + public static void resetGuid() { + guid = 0; + } + // Accessible for the purpose of testing. public static String getTempDir() { if (tempdir == null) { diff --git a/java/daikon/test/AllTestsSuite.java b/java/daikon/test/AllTestsSuite.java index 2bbf9d93ea..f3801a1607 100644 --- a/java/daikon/test/AllTestsSuite.java +++ b/java/daikon/test/AllTestsSuite.java @@ -34,8 +34,8 @@ daikon.test.TestQuant.class, daikon.test.TestAnnotate.class, daikon.test.DtraceDiffTester.class, - // daikon.test.split.SplitterFactoryTest is not in this suite, because it must run in its own - // JVM: it depends on the sequential numbering of splitter files. The Makefile's "junit" - // target runs it separately. + // daikon.test.split.SplitterFactoryTest is not in this suite, because it is generated after the + // rest of Daikon (including this class) is compiled. The Makefile's "junit" target runs it + // separately. }) public final class AllTestsSuite {} diff --git a/java/daikon/test/SampleTester.java b/java/daikon/test/SampleTester.java index fec8775b72..516e37d0c5 100644 --- a/java/daikon/test/SampleTester.java +++ b/java/daikon/test/SampleTester.java @@ -70,15 +70,14 @@ public class SampleTester { "Usage: java daikon.test.SampleTester [OPTION]...", " -h, --" + Daikon.help_SWITCH, " Display this usage message", - " --" + Daikon.config_option_SWITCH, - " Specify a configuration option ", + " --" + Daikon.config_option_SWITCH + " config_var=val", + " Set the specified configuration variable", " --" + Daikon.debugAll_SWITCH, " Turn on all debug flags (voluminous output)", - " --" + Daikon.debug_SWITCH, - " Specify a logger to enable", - " --" + Daikon.track_SWITCH, - " Specify a class, varinfos, and ppt to debug track.", - " Format is class@ppt"); + " --" + Daikon.debug_SWITCH + " logger", + " Turn on the specified debug logger", + " --" + Daikon.track_SWITCH + " class@ppt", + " Print debug info on the specified invariant class, vars, and ppt"); /** * Runs the tests without JUnit. diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index e44087c468..112058501d 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -194,12 +194,10 @@ private static String getTestClassText() { ps.println("import gnu.getopt.*;"); ps.println("import java.io.*;"); ps.println("import java.util.*;"); - ps.println("import junit.framework.*;"); ps.println("import org.junit.BeforeClass;"); ps.println("import org.junit.Test;"); + ps.println("import org.junit.internal.TextListener;"); ps.println("import org.junit.runner.JUnitCore;"); - ps.println("import org.junit.runner.Result;"); - ps.println("import org.junit.runner.notification.Failure;"); ps.println("import org.plumelib.util.FilesPlume;"); ps.println("import org.plumelib.util.StringsPlume;"); ps.println("import org.plumelib.util.UtilPlume;"); @@ -258,14 +256,13 @@ private static String getTestClassText() { ps.println(" }"); ps.println(); ps.println(" /**"); - ps.println(" * This does the work of {@link #main(String[])}, but it never calls System.exit."); - ps.println(" * It may be called only once per JVM, because SplitterFactory numbers the files it"); - ps.println(" * produces sequentially and setUpTests sets static state."); + ps.println(" * This does the work of {@link #main(String[])}, but it never calls System.exit, so"); + ps.println(" * it is appropriate to be called programmatically."); ps.println(" *"); ps.println(" * @param args the command-line arguments; see {@link #usage}"); ps.println(" * @return true if all the tests passed"); ps.println(" */"); - ps.println(" private static boolean mainHelper(String[] args) {"); + ps.println(" public static boolean mainHelper(String[] args) {"); ps.println( " Getopt g = new Getopt(\"daikon.test.split.SplitterFactoryTest\", args, \"hs\");"); ps.println(" int c;"); @@ -291,17 +288,9 @@ private static String getTestClassText() { " \"Unexpected argument \" + args[g.getOptind()] + \"; run with -h for" + " usage\");"); ps.println(" }"); - ps.println(" Result result = JUnitCore.runClasses(SplitterFactoryTest.class);"); - ps.println(" for (Failure failure : result.getFailures()) {"); - ps.println(" System.err.println(\"Failure in \" + failure.getTestHeader() + \":\");"); - ps.println(" System.err.println(failure.getTrace());"); - ps.println(" }"); - ps.println(" if (!result.wasSuccessful()) {"); - ps.println( - " System.err.println(\"SplitterFactoryTest: \" +" - + " StringsPlume.nPlural(result.getFailureCount(), \"failure\"));"); - ps.println(" }"); - ps.println(" return result.wasSuccessful();"); + ps.println(" JUnitCore junit = new JUnitCore();"); + ps.println(" junit.addListener(new TextListener(System.out));"); + ps.println(" return junit.run(SplitterFactoryTest.class).wasSuccessful();"); ps.println(" }"); ps.println(); @@ -357,6 +346,7 @@ private static String getTestClassText() { public static void appendSetUpTest(PrintStream ps) { ps.println(" @BeforeClass"); ps.println(" public static void setUpTests() {"); + ps.println(" SplitterFactory.resetGuid();"); for (int i = 0; i < spinfoFileLists.size(); i++) { ps.println(" createSplitterFiles("); ps.println( From 8df52e116981458bfcc64090745b7964d3aef695 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 12:28:29 -0700 Subject: [PATCH 08/10] Write SplitterFactoryTest goal files only via make update-splitter-goals; avoid make -j race in junit; fix line length and Javadoc --- java/Makefile | 15 +++- .../split/SplitterFactoryTestUpdater.java | 79 ++++++++++++------- 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/java/Makefile b/java/Makefile index 878ffe8e3a..9779897f00 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1219,7 +1219,10 @@ check-serialize: .PHONY: junit -junit: daikon/Daikon.class ${AUTO_GENERATED_CLASSES_2} +junit: daikon/Daikon.class + @# This is not a prerequisite, so that "make -j" does not run it concurrently + @# with compiling Daikon, whose classes the SplitterFactoryTest rules use. + ${MAKE} ${AUTO_GENERATED_CLASSES_2} @echo "Running unit (not system) tests..." LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite ifeq (${JAVA24}, 1) @@ -1474,6 +1477,16 @@ daikon/test/split/SplitterFactoryTest.java: daikon/test/split/SplitterFactoryTes ${AUTO_GENERATED_CLASSES_2}: ${AUTO_GENERATED_FILES_2} ${JAVAC_COMMAND} ${AUTO_GENERATED_FILES_2} +# Overwrites SplitterFactoryTest's goal files with the current SplitterFactory output. +# Examine the differences (e.g., with "git diff") before committing them. +.PHONY: update-splitter-goals +update-splitter-goals: daikon/Daikon.class + rm -f daikon/test/split/SplitterFactoryTest.java + (${JAVA_COMMAND} daikon.test.split.SplitterFactoryTestUpdater --update-goals > /dev/null 2>&1) \ + || ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTestUpdater --update-goals + chmod a-w daikon/test/split/SplitterFactoryTest.java + ${MAKE} ${AUTO_GENERATED_CLASSES_2} + # Create the auto-generated files with jpp .PHONY: jpp jpp: ${PLUME_SCRIPTS} ${AUTO_GENERATED_FILES} diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index 112058501d..0a9d11ba6d 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -17,15 +17,13 @@ import org.plumelib.util.FilesPlume; /** - * This class's main method can be used to update both the target files of SplitterFactoryTest and - * the code of the SplitterFactoryTest itself. + * This class's main method writes the code of SplitterFactoryTest. When passed {@code + * --update-goals}, it also overwrites the goal files of SplitterFactoryTest with the splitter files + * that the current SplitterFactory produces. * - *

To use this program to update SplitterFactoryTest and the target files, run - * - *

    \rm -f targets/*.java.goal SplitterFactoryTest.java
- * - * Then simply run the main method without any arguments in the daikon/java directory, and then - * re-compile the SplitterFactoryTest. + *

To update the goal files, run {@code make update-splitter-goals} in the daikon/java directory, + * then examine the differences in the goal files (for example, with {@code git diff}) before + * committing them. * *

To add additional tests to this test program, place the .spinfo and decls files into the * "targets" directory then add a call to generateSplitters with the new files. generateSplitters is @@ -43,14 +41,29 @@ public class SplitterFactoryTestUpdater { private SplitterFactoryTestUpdater() {} // blocks public constructor /** - * If one has changed the test cases used below, for best results run {@code rm *.java.goal} while - * in the targets directory before running this method. Creates new splitter java files, moves the - * new files into target directory, rewrites the code of SplitterFactoryTest to use the new files. - * One should recompile SplitterFactoryTest after running this method. + * Creates new splitter java files and writes the code of SplitterFactoryTest, which compares + * splitter files against the goal files. One should recompile SplitterFactoryTest after running + * this method. + * + *

If one has changed the test cases used below, for best results run {@code rm *.java.goal} + * while in the targets directory before running this method with {@code --update-goals}. * - * @param args are ignored + * @param args either empty, or {@code --update-goals} to also move the new splitter java files + * into the target directory as goal files */ public static void main(String[] args) { + boolean updateGoals; + if (args.length == 0) { + updateGoals = false; + } else if (args.length == 1 && args[0].equals("--update-goals")) { + updateGoals = true; + } else { + System.err.println( + "Usage: java daikon.test.split.SplitterFactoryTestUpdater [--update-goals]"); + System.exit(1); + return; + } + // For debugging // SplitterFactory.dkconfig_delete_splitters_on_exit = false; @@ -58,7 +71,7 @@ public static void main(String[] args) { generateSplitters("Fib.spinfo", "Fib.decls"); generateSplitters("QueueAr.spinfo", "QueueAr.decls"); generateSplitters("BigFloat.spinfo", "BigFloat.decls"); - moveFiles(); + processFiles(updateGoals); writeTestClass(); } @@ -111,8 +124,13 @@ private static void generateSplitters(List spinfos, List decls) } } - /** Moves the generated splitter files from the tempDir to the target Dir. */ - private static void moveFiles() { + /** + * Records the class names of the generated splitter files, which are in the tempDir. + * + * @param updateGoals if true, also move the generated splitter files to the target directory as + * goal files + */ + private static void processFiles(boolean updateGoals) { File tempDir = new File(SplitterFactory.getTempDir()); String[] fileNames = tempDir.list(); if (fileNames == null) { @@ -121,16 +139,18 @@ private static void moveFiles() { for (int i = 0; i < fileNames.length; i++) { if (fileNames[i].endsWith(".java")) { String fileName = fileNames[i]; - String fromName = tempDir.getPath() + File.separator + fileName; - String toName = targetDir + fileName + ".goal"; - try { - moveFile(fromName, toName); - } catch (Error e) { - System.out.printf("Failed to move %s to %s%n", fromName, toName); - throw e; - } catch (RuntimeException e) { - System.out.printf("Failed to move %s to %s%n", fromName, toName); - throw e; + if (updateGoals) { + String fromName = tempDir.getPath() + File.separator + fileName; + String toName = targetDir + fileName + ".goal"; + try { + moveFile(fromName, toName); + } catch (Error e) { + System.out.printf("Failed to move %s to %s%n", fromName, toName); + throw e; + } catch (RuntimeException e) { + System.out.printf("Failed to move %s to %s%n", fromName, toName); + throw e; + } } String javaFileName = new File(fileName).getName(); String className = javaFileName.substring(0, javaFileName.length() - ".java".length()); @@ -256,7 +276,9 @@ private static String getTestClassText() { ps.println(" }"); ps.println(); ps.println(" /**"); - ps.println(" * This does the work of {@link #main(String[])}, but it never calls System.exit, so"); + ps.println( + " * This does the work of {@link #main(String[])}, but it never calls System.exit," + + " so"); ps.println(" * it is appropriate to be called programmatically."); ps.println(" *"); ps.println(" * @param args the command-line arguments; see {@link #usage}"); @@ -361,7 +383,8 @@ public static void appendSetUpTest(PrintStream ps) { /** Appends the code that executes the test in SplitterFactoryTest to code. */ private static void appendTests(PrintStream ps) { - ps.println(" /** Returns true iff files are the same (ignoring extra white space). */"); + ps.println( + " /** Fails the test unless the files are the same (ignoring extra white space). */"); ps.println(" public static void assertEqualFiles(String f1, String f2) {"); ps.println(" if (!FilesPlume.equalFiles(f1, f2)) {"); ps.println(" fail(\"Files \" + f1 + \" and \" + f2 + \" differ.\");"); From 965a5f63d2e8eae7150498ad0d9b2e8309b7a3db Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 16:32:53 -0700 Subject: [PATCH 09/10] update-splitter-goals removes stale goal files and rebuilds the updater; clear failure for a missing goal file --- java/Makefile | 2 +- .../split/SplitterFactoryTestUpdater.java | 31 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/java/Makefile b/java/Makefile index 9779897f00..3a4f2fed50 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1480,7 +1480,7 @@ ${AUTO_GENERATED_CLASSES_2}: ${AUTO_GENERATED_FILES_2} # Overwrites SplitterFactoryTest's goal files with the current SplitterFactory output. # Examine the differences (e.g., with "git diff") before committing them. .PHONY: update-splitter-goals -update-splitter-goals: daikon/Daikon.class +update-splitter-goals: daikon/Daikon.class daikon/test/split/SplitterFactoryTestUpdater.class rm -f daikon/test/split/SplitterFactoryTest.java (${JAVA_COMMAND} daikon.test.split.SplitterFactoryTestUpdater --update-goals > /dev/null 2>&1) \ || ${JAVA_COMMAND} daikon.test.split.SplitterFactoryTestUpdater --update-goals diff --git a/java/daikon/test/split/SplitterFactoryTestUpdater.java b/java/daikon/test/split/SplitterFactoryTestUpdater.java index 0a9d11ba6d..e3758e9f5d 100644 --- a/java/daikon/test/split/SplitterFactoryTestUpdater.java +++ b/java/daikon/test/split/SplitterFactoryTestUpdater.java @@ -45,11 +45,8 @@ private SplitterFactoryTestUpdater() {} // blocks public constructor * splitter files against the goal files. One should recompile SplitterFactoryTest after running * this method. * - *

If one has changed the test cases used below, for best results run {@code rm *.java.goal} - * while in the targets directory before running this method with {@code --update-goals}. - * - * @param args either empty, or {@code --update-goals} to also move the new splitter java files - * into the target directory as goal files + * @param args either empty, or {@code --update-goals} to also replace the goal files in the + * target directory by the new splitter java files */ public static void main(String[] args) { boolean updateGoals; @@ -127,8 +124,8 @@ private static void generateSplitters(List spinfos, List decls) /** * Records the class names of the generated splitter files, which are in the tempDir. * - * @param updateGoals if true, also move the generated splitter files to the target directory as - * goal files + * @param updateGoals if true, also replace the goal files in the target directory by the + * generated splitter files */ private static void processFiles(boolean updateGoals) { File tempDir = new File(SplitterFactory.getTempDir()); @@ -136,6 +133,18 @@ private static void processFiles(boolean updateGoals) { if (fileNames == null) { throw new Error("tmpdir = " + tempDir + " which is not a directory"); } + if (updateGoals) { + // Remove goal files for splitters that SplitterFactory no longer produces. + File[] oldGoals = new File(targetDir).listFiles((dir, name) -> name.endsWith(".java.goal")); + if (oldGoals == null) { + throw new Error("targetDir = " + targetDir + " which is not a directory"); + } + for (File oldGoal : oldGoals) { + if (!oldGoal.delete()) { + throw new Error("Failed to delete " + oldGoal); + } + } + } for (int i = 0; i < fileNames.length; i++) { if (fileNames[i].endsWith(".java")) { String fileName = fileNames[i]; @@ -392,7 +401,13 @@ private static void appendTests(PrintStream ps) { ps.println(" }"); ps.println(); ps.println(" public static void assertEqualFiles(String f1) {"); - ps.println(" assertEqualFiles(tempDir + f1, targetDir + f1 + \".goal\");"); + ps.println(" String goalFile = targetDir + f1 + \".goal\";"); + ps.println(" if (!new File(goalFile).exists()) {"); + ps.println( + " fail(\"Goal file \" + goalFile" + + " + \" does not exist; run: make update-splitter-goals\");"); + ps.println(" }"); + ps.println(" assertEqualFiles(tempDir + f1, goalFile);"); ps.println(" }"); ps.println(); for (String className : classNames) { From b2c34330a6ba1d2d21ae8d7180cc4ba9d6e53349 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 11 Oct 2026 19:42:51 -0700 Subject: [PATCH 10/10] Order the SplitterFactoryTest rules after compiling Daikon; correct Makefile comments --- java/Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/java/Makefile b/java/Makefile index c572c4bbf9..1e6ac0fb50 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1220,8 +1220,8 @@ check-serialize: .PHONY: junit junit: daikon/Daikon.class - @# This is not a prerequisite, so that "make -j" does not run it concurrently - @# with compiling Daikon, whose classes the SplitterFactoryTest rules use. + @# This is not a prerequisite: when the daikon/Daikon.class recipe runs, its + @# sub-make builds SplitterFactoryTest, and this make would redo that work. ${MAKE} ${AUTO_GENERATED_CLASSES_2} @echo "Running unit (not system) tests..." LC_ALL=en_US.UTF-8 ${JAVA_COMMAND} org.junit.runner.JUnitCore daikon.test.AllTestsSuite @@ -1461,8 +1461,9 @@ list_java_files: showvar_JAVA_FILES ### Automatically generated files ### -# This must NOT depend on "daikon/Daikon.class", as that would lead to infinite regress. -daikon/test/split/SplitterFactoryTestUpdater.class: daikon/test/split/SplitterFactoryTestUpdater.java +# The updater uses Daikon's classes, so it must be compiled after Daikon; this order-only +# prerequisite also orders the SplitterFactoryTest rules after Daikon, even under "make -j". +daikon/test/split/SplitterFactoryTestUpdater.class: daikon/test/split/SplitterFactoryTestUpdater.java | daikon/Daikon.class ${JAVAC_COMMAND} $< ## First, a file not generated via java-cpp.