();
+ int seed = 127;
+ for (String fileName : exampleFiles) {
+ Randomizer.setSeed(seed);
+ seed += 10; // need more than one to prevent trouble with multiMCMC logs
+ System.out.println("Processing " + fileName);
+ XMLParser parser = new XMLParser();
+ try {
+ beast.base.inference.Runnable runable = parser.parseFile(new File(dir + "/" + fileName));
+ if (runable instanceof MCMC) {
+ MCMC mcmc = (MCMC) runable;
+ mcmc.setInputValue("preBurnin", 0);
+ mcmc.setInputValue("chainLength", 1000l);
+ mcmc.run();
+ }
+ } catch (Exception e) {
+ System.out.println("ExampleXmlParsing::Failed for " + fileName
+ + ": " + e.getMessage());
+ failedFiles.add(fileName);
+ }
+ System.out.println("Done " + fileName);
+ }
+ if (failedFiles.size() > 0) {
+ System.out.println("\ntest_ThatXmlExamplesRun::Failed for : " + failedFiles.toString());
+ } else {
+ System.out.println("SUCCESS!!!");
+ }
+ assertTrue(failedFiles.size() == 0, failedFiles.toString());
+ } catch (Exception e) {
+ System.out.println("exception thrown ");
+ System.out.println(e.getMessage());
+ ;
+ }
+ } // test_ThatXmlExamplesRun
+
+
+ protected static class ExitException extends SecurityException
+ {
+ public final int status;
+ public ExitException(int status)
+ {
+ super("There is no escape!");
+ this.status = status;
+ }
+ }
+
+ // Suppress warning for removal of SecurityManager
+ // there does not seem to be a viable alternative
+ // for blocking System.exit() calls yet
+ @SuppressWarnings({ "removal", "deprecation" })
+ @DisabledForJreRange(min = JRE.JAVA_18, disabledReason = "SecurityManager removed in Java 18+")
+ @Test
+ public void test_ThatParameterisedXmlExamplesRuns() throws IOException {
+ String dir = XMLPathUtil.resolveExamplesDir() + "/parameterised";
+ Logger.FILE_MODE = Logger.LogFileMode.overwrite;
+ System.out.println("Test that parameterised XML example runs in " + dir + "/RSV2.xml");
+ Randomizer.setSeed(127);
+
+ // prevent System.exit() having an effect
+ final SecurityManager securityManager = new SecurityManager() {
+ @Override
+ public void checkPermission( Permission permission ) {
+ if( "exitVM".equals( permission.getName() ) ) {
+ // throw new RuntimeException("Exit called") ;
+ System.err.println("Exit called");
+ }
+ }
+ @Override
+ public void checkExit(int status)
+ {
+ throw new ExitException(status);
+ }
+ };
+ SecurityManager sm = System.getSecurityManager();
+ System.setSecurityManager( securityManager ) ;
+
+ try {
+ BeastMain.main(new String[]{
+ "-D", "chainLength=1000",
+ "-DF", dir + "/RSV2.json",
+ "-DFout", "/tmp/RSV2.out.xml",
+ dir + "/RSV2.xml"});
+ } catch (ExitException e) {
+ if (e.status != 0) {
+ e.printStackTrace();
+ throw new RuntimeException("Exitted with status = " + e.status);
+ }
+ }
+
+ // reinstate System.exit() behaviour
+ System.setSecurityManager(sm) ;
+
+ if (!new File("/tmp/RSV2.out.xml").exists()) {
+ throw new RuntimeException("Could not find file /tmp/RSV2.out.xml");
+ }
+
+ } // test_ThatParameterisedXmlExamplesRuns
+
+
+
+
+ public static void main(String args[]) {
+ // see ExampleJSONParsingTest.main for comments
+ // org.junit.runner.JUnitCore.main("test.beast.integration.ExampleXmlParsingTest");
+ }
+
+
+} // ExampleXmlParsingTest
diff --git a/src/test/java/test/beast/integration/XMLPathUtil.java b/src/test/java/test/beast/integration/XMLPathUtil.java
new file mode 100644
index 0000000..9dcee37
--- /dev/null
+++ b/src/test/java/test/beast/integration/XMLPathUtil.java
@@ -0,0 +1,71 @@
+package test.beast.integration;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+/**
+ * Shared test infrastructure for beast-base integration tests.
+ *
+ * Two distinct concerns are kept as separate methods on purpose:
+ *
+ * - {@link #resolveExamplesDir()} — pure function; finds where BEAST reads
+ * XML/JSON input examples from the test classpath.
+ * - {@link #setUpOutputDir()} — side-effectful; creates the {@code ./test/} directory
+ * and sets {@code file.name.prefix} so BEAST writes log/tree output there.
+ * Call from {@code @BeforeEach}.
+ *
+ * Merging them would couple a pure query to a mutating side effect, forcing every
+ * caller of {@code resolveExamplesDir()} to trigger directory creation implicitly.
+ *
+ * Individual test classes are responsible for naming their own XML/JSON files.
+ */
+public class XMLPathUtil {
+
+ private static final String EXAMPLES_CLASSPATH = "nestedsampling/examples";
+
+ /**
+ * Returns the absolute path to this package's examples directory.
+ * Resolves via the test classpath (works on any machine or CI runner),
+ * falling back to {@code user.dir} if the resource is not found.
+ */
+ public static String resolveExamplesDir() {
+ URL url = XMLPathUtil.class.getClassLoader().getResource(EXAMPLES_CLASSPATH);
+ if (url != null) {
+ try {
+ return new File(url.toURI()).getAbsolutePath();
+ } catch (URISyntaxException e) {
+ // fall through to user.dir fallback
+ }
+ }
+ return System.getProperty("user.dir") + "/" + EXAMPLES_CLASSPATH;
+ }
+
+ /**
+ * Creates the {@code ./test/} output directory if absent and sets
+ * {@code file.name.prefix=test/} so BEAST logger output is written there.
+ * Call from {@code @BeforeEach} in each integration test class.
+ */
+ public static void setUpOutputDir() {
+ setUpOutputDir("");
+ }
+
+ /**
+ * Creates {@code ./test//} and sets {@code file.name.prefix} to that
+ * path, isolating log and tree files for one specific test from those of others.
+ * Use when multiple tests in the same class share log-file names (e.g. when
+ * XMLs all write to {@code test.$(seed).log}).
+ *
+ * Note: {@code file.name.prefix} is a JVM-wide system property. Callers that
+ * mutate it must declare {@code @ResourceLock("beast.logger.globals")} so JUnit 5's
+ * parallel scheduler serializes them; see {@code junit-platform.properties} for the
+ * full list of affected classes.
+ */
+ public static void setUpOutputDir(String subdir) {
+ String path = subdir == null || subdir.isEmpty() ? "test/" : "test/" + subdir + "/";
+ File dir = new File("./" + path);
+ if (!dir.exists())
+ dir.mkdirs();
+ System.setProperty("file.name.prefix", path);
+ }
+}
diff --git a/src/test/java/test/nestedsampling/evolution/speciation/YuleModelNormalisedTest.java b/src/test/java/test/nestedsampling/evolution/speciation/YuleModelNormalisedTest.java
new file mode 100644
index 0000000..3656965
--- /dev/null
+++ b/src/test/java/test/nestedsampling/evolution/speciation/YuleModelNormalisedTest.java
@@ -0,0 +1,32 @@
+package test.nestedsampling.evolution.speciation;
+
+import beast.base.evolution.alignment.Alignment;
+import beast.base.evolution.tree.Tree;
+import nestedsampling.evolution.speciation.YuleModelNormalised;
+import org.junit.jupiter.api.Test;
+import test.beast.BEASTTestCase;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The expected logP is computed from beast3 code on July 2026.
+ */
+public class YuleModelNormalisedTest {
+
+ @Test
+ public void testYuleModelNormalised() throws Exception {
+ Alignment data = BEASTTestCase.getAlignment();
+ Tree tree = BEASTTestCase.getTree(data);
+
+ YuleModelNormalised myd = new YuleModelNormalised();
+ myd.initByName("tree", tree,
+// "newick", "(human:0.024003,chimp:0.010772,bonobo:0.010772),gorilla:0.036038,orangutan:0.069125,siamang:0.099582;",
+ "birthDiffRate", "0.1",
+ "rho", "0.5");
+// logP = -10.018014963613476
+ double logP = myd.calculateLogP();
+ System.out.println("logP = " + logP);
+
+ assertEquals(-10.018015, logP, 1.0E6);
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/nestedsampling/examples/NS_4taxa_NormalBirthRate.xml b/src/test/resources/nestedsampling/examples/NS_4taxa_NormalBirthRate.xml
new file mode 100644
index 0000000..21bf304
--- /dev/null
+++ b/src/test/resources/nestedsampling/examples/NS_4taxa_NormalBirthRate.xml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.0
+ 2.0
+ 0.25
+
+
+
+
+
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.0
+ 1.0
+ 0.0
+
+
+
+
+
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/nestedsampling/examples/dna.xml b/src/test/resources/nestedsampling/examples/dna.xml
new file mode 100644
index 0000000..8357322
--- /dev/null
+++ b/src/test/resources/nestedsampling/examples/dna.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.0
+
+
+
+
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+ 1.0
+ 1.0
+ 0.0
+
+
+
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/NS_4taxa_NormalBirthRate.xml b/src/test/resources/nestedsampling/examples/legacy/NS_4taxa_NormalBirthRate.xml
similarity index 100%
rename from examples/NS_4taxa_NormalBirthRate.xml
rename to src/test/resources/nestedsampling/examples/legacy/NS_4taxa_NormalBirthRate.xml
diff --git a/examples/dna.xml b/src/test/resources/nestedsampling/examples/legacy/dna.xml
similarity index 100%
rename from examples/dna.xml
rename to src/test/resources/nestedsampling/examples/legacy/dna.xml
diff --git a/version.xml b/version.xml
index a013439..767274f 100644
--- a/version.xml
+++ b/version.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+