From ebf0133c6b0c067638f3dbbc27fb3973ac4d3e3b Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Mon, 23 Mar 2026 13:24:26 +0000 Subject: [PATCH 1/5] Align runtime compiler tests with Jupiter --- pom.xml | 6 ++++++ src/test/java/mytest/RuntimeCompileTest.java | 6 ++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 39608fd..1779978 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,12 @@ test + + org.junit.jupiter + junit-jupiter + test + + org.slf4j slf4j-simple diff --git a/src/test/java/mytest/RuntimeCompileTest.java b/src/test/java/mytest/RuntimeCompileTest.java index 22b9f0f..affa2f7 100644 --- a/src/test/java/mytest/RuntimeCompileTest.java +++ b/src/test/java/mytest/RuntimeCompileTest.java @@ -5,7 +5,7 @@ import net.openhft.compiler.CachedCompiler; import net.openhft.compiler.CompilerUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.URL; import java.net.URLClassLoader; @@ -18,8 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.IntSupplier; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.*; public class RuntimeCompileTest { private static String code = "package mytest;\n" + @@ -44,7 +43,6 @@ public void outOfBounds() throws Exception { } } - //@Ignore("see https://teamcity.chronicle.software/viewLog.html?buildId=639347&tab=buildResultsDiv&buildTypeId=OpenHFT_BuildAll_BuildJava11compileJava11") @Test public void testMultiThread() throws Exception { StringBuilder largeClass = new StringBuilder("package mytest;\n" + From 2f1f48d2371609dc160a5ccfc83d0f0e1501ab03 Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Mon, 23 Mar 2026 13:40:17 +0000 Subject: [PATCH 2/5] Update runtime compiler main sources --- src/main/java/net/openhft/compiler/CachedCompiler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/openhft/compiler/CachedCompiler.java b/src/main/java/net/openhft/compiler/CachedCompiler.java index 4f1c989..7e2cb98 100644 --- a/src/main/java/net/openhft/compiler/CachedCompiler.java +++ b/src/main/java/net/openhft/compiler/CachedCompiler.java @@ -210,7 +210,6 @@ public void report(Diagnostic diagnostic) { } } - /** * Compile and load using a specific class loader and writer. The * compilation result is cached against the loader for future calls. From 9e43d07bddeabd5d554ec51ece9967aa01ff569f Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Mon, 23 Mar 2026 13:40:17 +0000 Subject: [PATCH 3/5] Migrate runtime compiler tests to Jupiter --- .../compiler/AiRuntimeGuardrailsTest.java | 18 +++++------ .../CachedCompilerAdditionalTest.java | 28 ++++++++--------- .../net/openhft/compiler/CompilerTest.java | 17 +++++++--- .../openhft/compiler/CompilerUtilsIoTest.java | 20 ++++++------ .../compiler/MyJavaFileManagerTest.java | 31 +++++++++---------- 5 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java b/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java index 6606fa5..e224f52 100644 --- a/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java +++ b/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java @@ -3,13 +3,13 @@ */ package net.openhft.compiler; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class AiRuntimeGuardrailsTest { @@ -51,12 +51,12 @@ public void validatorStopsCompilationAndRecordsFailure() { fail("Unexpected checked exception: " + unexpected.getMessage()); } - assertEquals("Compilation must not run after validation rejection", 0, compileInvocations.get()); + assertEquals(0, compileInvocations.get(), "Compilation must not run after validation rejection"); assertEquals(1, telemetry.compileAttempts("agent-A")); assertEquals(1, telemetry.validationFailures("agent-A")); assertEquals(0, telemetry.successes("agent-A")); assertEquals(0, telemetry.compileFailures("agent-A")); - assertFalse("Latency should not be recorded for rejected source", telemetry.hasLatency("agent-A")); + assertFalse(telemetry.hasLatency("agent-A"), "Latency should not be recorded for rejected source"); } @Test @@ -75,11 +75,11 @@ public void successfulCompilationRecordsMetrics() throws Exception { Class clazz = pipeline.compile("agent-B", "OkClass", "public class OkClass { public int add(int a, int b) { return a + b; } }"); - assertEquals("agent-B should see exactly one attempt", 1, telemetry.compileAttempts("agent-B")); + assertEquals(1, telemetry.compileAttempts("agent-B"), "agent-B should see exactly one attempt"); assertEquals(0, telemetry.validationFailures("agent-B")); assertEquals(1, telemetry.successes("agent-B")); assertEquals(0, telemetry.compileFailures("agent-B")); - assertTrue("Latency must be captured for successful compilation", telemetry.hasLatency("agent-B")); + assertTrue(telemetry.hasLatency("agent-B"), "Latency must be captured for successful compilation"); Object instance = clazz.getDeclaredConstructor().newInstance(); int sum = (int) clazz.getMethod("add", int.class, int.class).invoke(instance, 2, 3); @@ -103,12 +103,12 @@ public void cacheHitDoesNotRecompileButRecordsMetric() throws Exception { Class first = pipeline.compile("agent-C", "CacheCandidate", source); Class second = pipeline.compile("agent-C", "CacheCandidate", source); - assertEquals("Underlying compiler should only run once thanks to caching", 1, rawCompileCount.get()); + assertEquals(1, rawCompileCount.get(), "Underlying compiler should only run once thanks to caching"); assertEquals(2, telemetry.compileAttempts("agent-C")); assertEquals(0, telemetry.validationFailures("agent-C")); assertEquals(1, telemetry.successes("agent-C")); assertEquals(0, telemetry.compileFailures("agent-C")); - assertEquals("Cache hit count should be tracked", 1, telemetry.cacheHits("agent-C")); + assertEquals(1, telemetry.cacheHits("agent-C"), "Cache hit count should be tracked"); assertTrue(first == second); } @@ -140,7 +140,7 @@ public void compilerFailureRecordedSeparately() { assertEquals(0, telemetry.validationFailures("agent-D")); assertEquals(0, telemetry.successes("agent-D")); assertEquals(1, telemetry.compileFailures("agent-D")); - assertFalse("Failure should not record cache hits", telemetry.hasCacheHits("agent-D")); + assertFalse(telemetry.hasCacheHits("agent-D"), "Failure should not record cache hits"); } private static final class GuardrailedCompilerPipeline { diff --git a/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java b/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java index 535bc8d..8b9e54d 100644 --- a/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java +++ b/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java @@ -3,7 +3,7 @@ */ package net.openhft.compiler; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.tools.JavaCompiler; import javax.tools.StandardJavaFileManager; @@ -20,14 +20,14 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CachedCompilerAdditionalTest { @Test public void compileFromJavaReturnsBytecode() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager standardManager = compiler.getStandardFileManager(null, null, null)) { CachedCompiler cachedCompiler = new CachedCompiler(null, null); @@ -45,7 +45,7 @@ public void compileFromJavaReturnsBytecode() throws Exception { @Test public void compileFromJavaReturnsEmptyMapOnFailure() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager standardManager = compiler.getStandardFileManager(null, null, null)) { CachedCompiler cachedCompiler = new CachedCompiler(null, null); MyJavaFileManager fileManager = new MyJavaFileManager(standardManager); @@ -53,7 +53,7 @@ public void compileFromJavaReturnsEmptyMapOnFailure() throws Exception { "coverage.Broken", "package coverage; public class Broken { this does not compile }", fileManager); - assertTrue("Broken source should not produce classes", classes.isEmpty()); + assertTrue(classes.isEmpty(), "Broken source should not produce classes"); } } @@ -66,7 +66,7 @@ public void updateFileManagerForClassLoaderInvokesConsumer() throws Exception { AtomicBoolean invoked = new AtomicBoolean(false); compiler.updateFileManagerForClassLoader(loader, fm -> invoked.set(true)); - assertTrue("Consumer should be invoked when manager exists", invoked.get()); + assertTrue(invoked.get(), "Consumer should be invoked when manager exists"); } @Test @@ -75,13 +75,13 @@ public void updateFileManagerNoOpWhenClassLoaderUnknown() { AtomicBoolean invoked = new AtomicBoolean(false); compiler.updateFileManagerForClassLoader(new ClassLoader() { }, fm -> invoked.set(true)); - assertTrue("Consumer should not be invoked when manager missing", !invoked.get()); + assertTrue(!invoked.get(), "Consumer should not be invoked when manager missing"); } @Test public void closeClosesAllManagedFileManagers() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); CachedCompiler cachedCompiler = new CachedCompiler(null, null); AtomicBoolean closed = new AtomicBoolean(false); cachedCompiler.setFileManagerOverride(standard -> new TrackingFileManager(standard, closed)); @@ -90,7 +90,7 @@ public void closeClosesAllManagedFileManagers() throws Exception { }; cachedCompiler.loadFromJava(loader, "coverage.CloseTarget", "package coverage; public class CloseTarget {}"); cachedCompiler.close(); - assertTrue("Close should propagate to file managers", closed.get()); + assertTrue(closed.get(), "Close should propagate to file managers"); } @Test @@ -144,7 +144,7 @@ public void safeResolvePreventsPathTraversal() throws Exception { @Test public void writesSourceAndClassFilesWhenDirectoriesProvided() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); Path sourceDir = Files.createTempDirectory("cached-compiler-src"); Path classDir = Files.createTempDirectory("cached-compiler-classes"); @@ -160,8 +160,8 @@ public void writesSourceAndClassFilesWhenDirectoriesProvided() throws Exception Path sourceFile = sourceDir.resolve("coverage/FileOutput.java"); Path classFile = classDir.resolve("coverage/FileOutput.class"); - assertTrue("Source file should be emitted", Files.exists(sourceFile)); - assertTrue("Class file should be emitted", Files.exists(classFile)); + assertTrue(Files.exists(sourceFile), "Source file should be emitted"); + assertTrue(Files.exists(classFile), "Class file should be emitted"); byte[] firstBytes = Files.readAllBytes(classFile); CachedCompiler secondPass = new CachedCompiler(sourceDir.toFile(), classDir.toFile()); @@ -172,10 +172,10 @@ public void writesSourceAndClassFilesWhenDirectoriesProvided() throws Exception secondPass.close(); byte[] updatedBytes = Files.readAllBytes(classFile); - assertTrue("Updating the source should change emitted bytecode", !Arrays.equals(firstBytes, updatedBytes)); + assertTrue(!Arrays.equals(firstBytes, updatedBytes), "Updating the source should change emitted bytecode"); Path backupFile = classDir.resolve("coverage/FileOutput.class.bak"); - assertTrue("Backup should be cleaned up", !Files.exists(backupFile)); + assertTrue(!Files.exists(backupFile), "Backup should be cleaned up"); } finally { deleteRecursively(classDir); deleteRecursively(sourceDir); diff --git a/src/test/java/net/openhft/compiler/CompilerTest.java b/src/test/java/net/openhft/compiler/CompilerTest.java index 011434e..890da01 100644 --- a/src/test/java/net/openhft/compiler/CompilerTest.java +++ b/src/test/java/net/openhft/compiler/CompilerTest.java @@ -5,8 +5,9 @@ import eg.FooBarTee; import eg.components.Foo; -import junit.framework.TestCase; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; import java.io.*; import java.lang.reflect.Constructor; @@ -17,7 +18,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; -public class CompilerTest extends TestCase { +public class CompilerTest { private static final File parent; private static final String EG_FOO_BAR_TEE = "eg.FooBarTee"; private static final int RUNS = 1000 * 1000; @@ -36,6 +37,7 @@ public static void main(String[] args) throws Throwable { new CompilerTest().test_compiler(); } + @Test public void test_compiler() throws Throwable { // CompilerUtils.setDebug(true); // added so the test passes in Maven. @@ -94,6 +96,7 @@ public void test_compiler() throws Throwable { } } + @Test public void test_fromFile() throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { @@ -111,7 +114,7 @@ public void write(int b) throws IOException { for (int i = -RUNS / 10; i < RUNS; i++) { if (i == 0) start = System.nanoTime(); - Object fooBarTee2 = stringConstructor.newInstance(getName()); + Object fooBarTee2 = stringConstructor.newInstance("test_fromFile"); Foo foo = (Foo) clazz.getDeclaredField("foo").get(fooBarTee2); assertNotNull(foo); assertEquals("load java class from file.", foo.s); @@ -123,6 +126,7 @@ public void write(int b) throws IOException { } } + @Test public void test_settingPrintStreamWithCompilerErrors() throws Exception { final AtomicBoolean usedSysOut = new AtomicBoolean(false); final AtomicBoolean usedSysErr = new AtomicBoolean(false); @@ -164,10 +168,11 @@ public void write(int b) throws IOException { for (String expectedError : expectedInErrorFromCompiler) { String errorMessage = String.format("Does not contain expected '%s' in:\n%s", expectedError, writer.toString()); - assertTrue(errorMessage, writer.toString().contains(expectedError)); + assertTrue(writer.toString().contains(expectedError), errorMessage); } } + @Test public void test_settingPrintStreamWithNoErrors() throws Exception { final AtomicBoolean usedSysOut = new AtomicBoolean(false); final AtomicBoolean usedSysErr = new AtomicBoolean(false); @@ -203,6 +208,7 @@ public void write(int b) throws IOException { assertEquals("", writer.toString()); } + @Test public void test_settingPrintStreamWithWarnings() throws Exception { final AtomicBoolean usedSysOut = new AtomicBoolean(false); final AtomicBoolean usedSysErr = new AtomicBoolean(false); @@ -240,6 +246,7 @@ public void write(int b) throws IOException { assertEquals("", writer.toString()); } + @Test public void test_compilerErrorsDoNotBreakNextCompilations() throws Exception { // quieten the compiler output PrintWriter quietWriter = new PrintWriter(new StringWriter()); diff --git a/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java b/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java index dde885f..6cf86ad 100644 --- a/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java +++ b/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java @@ -3,7 +3,7 @@ */ package net.openhft.compiler; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.tools.JavaCompiler; import javax.tools.StandardJavaFileManager; @@ -17,7 +17,7 @@ import java.nio.file.Paths; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CompilerUtilsIoTest { @@ -28,13 +28,13 @@ public void writeTextDetectsNoChangeAndReadBytesMatches() throws Exception { File file = filePath.toFile(); boolean written = CompilerUtils.writeText(file, "hello"); - assertTrue("First write should report changes", written); + assertTrue(written, "First write should report changes"); boolean unchanged = CompilerUtils.writeText(file, "hello"); - assertTrue("Repeat write with identical content should be treated as unchanged", !unchanged); + assertTrue(!unchanged, "Repeat write with identical content should be treated as unchanged"); boolean changed = CompilerUtils.writeText(file, "different"); - assertTrue("Modified content should trigger a rewrite", changed); + assertTrue(changed, "Modified content should trigger a rewrite"); Method readBytes = CompilerUtils.class.getDeclaredMethod("readBytes", File.class); readBytes.setAccessible(true); @@ -73,7 +73,7 @@ public void encodeDecodeUtf8Matches() throws Exception { @Test public void defineClassLoadsCompiledBytes() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("JDK compiler required for tests", compiler); + assertNotNull(compiler, "JDK compiler required for tests"); try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) { CachedCompiler cachedCompiler = new CachedCompiler(null, null); MyJavaFileManager myJavaFileManager = new MyJavaFileManager(fileManager); @@ -109,7 +109,7 @@ public void defineClassLoadsCompiledBytes() throws Exception { public void addClassPathHandlesMissingDirectory() { Path nonExisting = Paths.get("not-existing-" + System.nanoTime()); boolean result = CompilerUtils.addClassPath(nonExisting.toString()); - assertTrue("Missing directories should return false", !result); + assertTrue(!result, "Missing directories should return false"); } @Test @@ -118,9 +118,9 @@ public void addClassPathAddsExistingDirectory() throws Exception { String originalClasspath = System.getProperty("java.class.path"); try { boolean added = CompilerUtils.addClassPath(tempDir.toAbsolutePath().toString()); - assertTrue("Existing directory should be added", added); + assertTrue(added, "Existing directory should be added"); boolean second = CompilerUtils.addClassPath(tempDir.toAbsolutePath().toString()); - assertTrue("Re-adding the same directory should report true because reset always occurs", second); + assertTrue(second, "Re-adding the same directory should report true because reset always occurs"); } finally { System.setProperty("java.class.path", originalClasspath); } @@ -218,7 +218,7 @@ public void writeBytesCreatesMissingParentDirectories() throws Exception { Path tempDir = Files.createTempDirectory("compiler-utils-parent"); Path nested = tempDir.resolve("nested").resolve("file.bin"); boolean changed = CompilerUtils.writeBytes(nested.toFile(), new byte[]{10, 20, 30}); - assertTrue("Path with missing parents should be created", changed); + assertTrue(changed, "Path with missing parents should be created"); assertTrue(Files.exists(nested)); } diff --git a/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java b/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java index 693519f..32f7dba 100644 --- a/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java +++ b/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java @@ -3,10 +3,9 @@ */ package net.openhft.compiler; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.tools.FileObject; -import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; @@ -27,14 +26,14 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class MyJavaFileManagerTest { @Test public void bufferedClassReturnedFromInput() throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { MyJavaFileManager manager = new MyJavaFileManager(delegate); @@ -53,7 +52,7 @@ public void bufferedClassReturnedFromInput() throws IOException { } manager.clearBuffers(); - assertTrue("Buffers should be cleared", manager.getAllBuffers().isEmpty()); + assertTrue(manager.getAllBuffers().isEmpty(), "Buffers should be cleared"); // Delegate path for non CLASS_OUTPUT locations manager.getJavaFileForInput(StandardLocation.CLASS_PATH, @@ -64,7 +63,7 @@ public void bufferedClassReturnedFromInput() throws IOException { @Test public void getJavaFileForInputDelegatesWhenBufferMissing() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager base = compiler.getStandardFileManager(null, null, null)) { AtomicBoolean delegated = new AtomicBoolean(false); JavaFileObject expected = new SimpleJavaFileObject(URI.create("string:///expected"), JavaFileObject.Kind.CLASS) { @@ -97,15 +96,15 @@ public InputStream openInputStream() { JavaFileObject result = manager.getJavaFileForInput(StandardLocation.CLASS_OUTPUT, "example.KindMismatch", JavaFileObject.Kind.SOURCE); - assertTrue("Delegate should be consulted when buffer missing", delegated.get()); - assertTrue("Result should match delegate outcome", result == expected); + assertTrue(delegated.get(), "Delegate should be consulted when buffer missing"); + assertTrue(result == expected, "Result should match delegate outcome"); } } @Test public void delegatingMethodsPassThroughToUnderlyingManager() throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager base = compiler.getStandardFileManager(null, null, null)) { MyJavaFileManager manager = new MyJavaFileManager(base); @@ -131,7 +130,7 @@ public void delegatingMethodsPassThroughToUnderlyingManager() throws IOException @Test public void listLocationsForModulesAndInferModuleNameDeferToDelegate() throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { MyJavaFileManager manager = new MyJavaFileManager(delegate); javax.tools.JavaFileManager.Location modulesLocation = resolveSystemModules(); @@ -157,7 +156,7 @@ public void listLocationsForModulesAndInferModuleNameDeferToDelegate() throws IO @Test public void invokeNamedMethodHandlesMissingMethods() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { MyJavaFileManager manager = new MyJavaFileManager(delegate); java.lang.reflect.Method method = MyJavaFileManager.class.getDeclaredMethod( @@ -175,7 +174,7 @@ public void invokeNamedMethodHandlesMissingMethods() throws Exception { @Test public void invokeNamedMethodWrapsInvocationFailures() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager base = compiler.getStandardFileManager(null, null, null)) { StandardJavaFileManager proxy = (StandardJavaFileManager) Proxy.newProxyInstance( StandardJavaFileManager.class.getClassLoader(), @@ -202,8 +201,8 @@ public void invokeNamedMethodWrapsInvocationFailures() throws Exception { if (cause instanceof InvocationTargetException) { cause = ((InvocationTargetException) cause).getCause(); } - assertTrue("Unexpected cause: " + cause, - cause instanceof UnsupportedOperationException || cause instanceof IOException); + assertTrue(cause instanceof UnsupportedOperationException || cause instanceof IOException, + "Unexpected cause: " + cause); } } } @@ -212,7 +211,7 @@ public void invokeNamedMethodWrapsInvocationFailures() throws Exception { @SuppressWarnings("unchecked") public void getAllBuffersSkipsEntriesWhenFutureFails() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - assertNotNull("System compiler required", compiler); + assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { MyJavaFileManager manager = new MyJavaFileManager(delegate); Field buffersField = MyJavaFileManager.class.getDeclaredField("buffers"); @@ -224,7 +223,7 @@ public void getAllBuffersSkipsEntriesWhenFutureFails() throws Exception { buffers.put("coverage.Faulty", faulty); } Map collected = manager.getAllBuffers(); - assertTrue("Faulty entries should be skipped when the close future fails", collected.isEmpty()); + assertTrue(collected.isEmpty(), "Faulty entries should be skipped when the close future fails"); } } From f21704c3fe477a7173928b9c2ba9d2e89112b108 Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Thu, 26 Mar 2026 09:50:48 +0000 Subject: [PATCH 4/5] Migrate tests to JUnit 5 - root cause: test suites still depended on JUnit 4 conventions and runners. - fix: migrate tests and supporting module code to JUnit 5. - impact: the module now follows the JUnit 5 branch baseline. --- src/test/java/mytest/RuntimeCompileTest.java | 12 +++---- .../compiler/AiRuntimeGuardrailsTest.java | 10 +++--- .../CachedCompilerAdditionalTest.java | 20 ++++++------ .../net/openhft/compiler/CompilerTest.java | 16 +++++----- .../openhft/compiler/CompilerUtilsIoTest.java | 32 +++++++++---------- .../compiler/MyJavaFileManagerTest.java | 16 +++++----- 6 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/test/java/mytest/RuntimeCompileTest.java b/src/test/java/mytest/RuntimeCompileTest.java index affa2f7..6f2abd9 100644 --- a/src/test/java/mytest/RuntimeCompileTest.java +++ b/src/test/java/mytest/RuntimeCompileTest.java @@ -20,7 +20,7 @@ import static org.junit.jupiter.api.Assertions.*; -public class RuntimeCompileTest { +class RuntimeCompileTest { private static String code = "package mytest;\n" + "public class Test implements IntConsumer {\n" + " public void accept(int num) {\n" + @@ -30,7 +30,7 @@ public class RuntimeCompileTest { "}\n"; @Test - public void outOfBounds() throws Exception { + void outOfBounds() throws Exception { ClassLoader cl = new URLClassLoader(new URL[0]); Class aClass = CompilerUtils.CACHED_COMPILER. loadFromJava(cl, "mytest.Test", code); @@ -44,7 +44,7 @@ public void outOfBounds() throws Exception { } @Test - public void testMultiThread() throws Exception { + void testMultiThread() throws Exception { StringBuilder largeClass = new StringBuilder("package mytest;\n" + "public class Test2 implements IntConsumer, java.util.function.IntSupplier {\n" + " static final java.util.concurrent.atomic.AtomicInteger called = new java.util.concurrent.atomic.AtomicInteger(0);\n" + @@ -52,8 +52,8 @@ public void testMultiThread() throws Exception { " public void accept(int num) {\n" + " called.incrementAndGet();\n" + " }\n"); - for (int j=0; j<1_000; j++) { - largeClass.append(" public void accept"+j+"(int num) {\n" + + for (int j = 0; j < 1_000; j++) { + largeClass.append(" public void accept" + j + "(int num) {\n" + " if ((byte) num != num)\n" + " throw new IllegalArgumentException();\n" + " }\n"); @@ -68,7 +68,7 @@ public void testMultiThread() throws Exception { final AtomicInteger started = new AtomicInteger(0); final ExecutorService executor = Executors.newFixedThreadPool(nThreads); final List> futures = new ArrayList<>(); - for (int i=0; i { started.incrementAndGet(); diff --git a/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java b/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java index e224f52..d2bbe97 100644 --- a/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java +++ b/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java @@ -11,10 +11,10 @@ import static org.junit.jupiter.api.Assertions.*; -public class AiRuntimeGuardrailsTest { +class AiRuntimeGuardrailsTest { @Test - public void validatorStopsCompilationAndRecordsFailure() { + void validatorStopsCompilationAndRecordsFailure() { AtomicInteger compileInvocations = new AtomicInteger(); TelemetryProbe telemetry = new TelemetryProbe(); GuardrailedCompilerPipeline pipeline = new GuardrailedCompilerPipeline( @@ -60,7 +60,7 @@ public void validatorStopsCompilationAndRecordsFailure() { } @Test - public void successfulCompilationRecordsMetrics() throws Exception { + void successfulCompilationRecordsMetrics() throws Exception { TelemetryProbe telemetry = new TelemetryProbe(); GuardrailedCompilerPipeline pipeline = new GuardrailedCompilerPipeline( Collections.singletonList(source -> { @@ -87,7 +87,7 @@ public void successfulCompilationRecordsMetrics() throws Exception { } @Test - public void cacheHitDoesNotRecompileButRecordsMetric() throws Exception { + void cacheHitDoesNotRecompileButRecordsMetric() throws Exception { AtomicInteger rawCompileCount = new AtomicInteger(); TelemetryProbe telemetry = new TelemetryProbe(); GuardrailedCompilerPipeline pipeline = new GuardrailedCompilerPipeline( @@ -113,7 +113,7 @@ public void cacheHitDoesNotRecompileButRecordsMetric() throws Exception { } @Test - public void compilerFailureRecordedSeparately() { + void compilerFailureRecordedSeparately() { TelemetryProbe telemetry = new TelemetryProbe(); GuardrailedCompilerPipeline pipeline = new GuardrailedCompilerPipeline( Collections.singletonList(source -> { diff --git a/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java b/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java index 8b9e54d..804a4b1 100644 --- a/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java +++ b/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java @@ -22,10 +22,10 @@ import static org.junit.jupiter.api.Assertions.*; -public class CachedCompilerAdditionalTest { +class CachedCompilerAdditionalTest { @Test - public void compileFromJavaReturnsBytecode() throws Exception { + void compileFromJavaReturnsBytecode() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); @@ -43,7 +43,7 @@ public void compileFromJavaReturnsBytecode() throws Exception { } @Test - public void compileFromJavaReturnsEmptyMapOnFailure() throws Exception { + void compileFromJavaReturnsEmptyMapOnFailure() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager standardManager = compiler.getStandardFileManager(null, null, null)) { @@ -58,7 +58,7 @@ public void compileFromJavaReturnsEmptyMapOnFailure() throws Exception { } @Test - public void updateFileManagerForClassLoaderInvokesConsumer() throws Exception { + void updateFileManagerForClassLoaderInvokesConsumer() throws Exception { CachedCompiler compiler = new CachedCompiler(null, null); ClassLoader loader = new ClassLoader() { }; @@ -70,7 +70,7 @@ public void updateFileManagerForClassLoaderInvokesConsumer() throws Exception { } @Test - public void updateFileManagerNoOpWhenClassLoaderUnknown() { + void updateFileManagerNoOpWhenClassLoaderUnknown() { CachedCompiler compiler = new CachedCompiler(null, null); AtomicBoolean invoked = new AtomicBoolean(false); compiler.updateFileManagerForClassLoader(new ClassLoader() { @@ -79,7 +79,7 @@ public void updateFileManagerNoOpWhenClassLoaderUnknown() { } @Test - public void closeClosesAllManagedFileManagers() throws Exception { + void closeClosesAllManagedFileManagers() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); CachedCompiler cachedCompiler = new CachedCompiler(null, null); @@ -94,7 +94,7 @@ public void closeClosesAllManagedFileManagers() throws Exception { } @Test - public void createDefaultWriterFlushesOnClose() throws Exception { + void createDefaultWriterFlushesOnClose() throws Exception { Method factory = CachedCompiler.class.getDeclaredMethod("createDefaultWriter"); factory.setAccessible(true); PrintWriter writer = (PrintWriter) factory.invoke(null); @@ -103,7 +103,7 @@ public void createDefaultWriterFlushesOnClose() throws Exception { } @Test - public void validateClassNameAllowsDescriptorForms() throws Exception { + void validateClassNameAllowsDescriptorForms() throws Exception { Method validate = CachedCompiler.class.getDeclaredMethod("validateClassName", String.class); validate.setAccessible(true); @@ -125,7 +125,7 @@ public void validateClassNameAllowsDescriptorForms() throws Exception { } @Test - public void safeResolvePreventsPathTraversal() throws Exception { + void safeResolvePreventsPathTraversal() throws Exception { Method method = CachedCompiler.class.getDeclaredMethod("safeResolve", File.class, String.class); method.setAccessible(true); Path root = Files.createTempDirectory("cached-compiler-safe"); @@ -142,7 +142,7 @@ public void safeResolvePreventsPathTraversal() throws Exception { } @Test - public void writesSourceAndClassFilesWhenDirectoriesProvided() throws Exception { + void writesSourceAndClassFilesWhenDirectoriesProvided() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); diff --git a/src/test/java/net/openhft/compiler/CompilerTest.java b/src/test/java/net/openhft/compiler/CompilerTest.java index 890da01..dc9472f 100644 --- a/src/test/java/net/openhft/compiler/CompilerTest.java +++ b/src/test/java/net/openhft/compiler/CompilerTest.java @@ -18,7 +18,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; -public class CompilerTest { +class CompilerTest { private static final File parent; private static final String EG_FOO_BAR_TEE = "eg.FooBarTee"; private static final int RUNS = 1000 * 1000; @@ -38,7 +38,7 @@ public static void main(String[] args) throws Throwable { } @Test - public void test_compiler() throws Throwable { + void test_compiler() throws Throwable { // CompilerUtils.setDebug(true); // added so the test passes in Maven. CompilerUtils.addClassPath("target/test-classes"); @@ -97,7 +97,7 @@ public void test_compiler() throws Throwable { } @Test - public void test_fromFile() + void test_fromFile() throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class clazz = CompilerUtils.loadFromResource("eg.FooBarTee2", "eg/FooBarTee2.jcf"); @@ -127,7 +127,7 @@ public void write(int b) throws IOException { } @Test - public void test_settingPrintStreamWithCompilerErrors() throws Exception { + void test_settingPrintStreamWithCompilerErrors() throws Exception { final AtomicBoolean usedSysOut = new AtomicBoolean(false); final AtomicBoolean usedSysErr = new AtomicBoolean(false); @@ -173,7 +173,7 @@ public void write(int b) throws IOException { } @Test - public void test_settingPrintStreamWithNoErrors() throws Exception { + void test_settingPrintStreamWithNoErrors() throws Exception { final AtomicBoolean usedSysOut = new AtomicBoolean(false); final AtomicBoolean usedSysErr = new AtomicBoolean(false); @@ -209,7 +209,7 @@ public void write(int b) throws IOException { } @Test - public void test_settingPrintStreamWithWarnings() throws Exception { + void test_settingPrintStreamWithWarnings() throws Exception { final AtomicBoolean usedSysOut = new AtomicBoolean(false); final AtomicBoolean usedSysErr = new AtomicBoolean(false); @@ -247,7 +247,7 @@ public void write(int b) throws IOException { } @Test - public void test_compilerErrorsDoNotBreakNextCompilations() throws Exception { + void test_compilerErrorsDoNotBreakNextCompilations() throws Exception { // quieten the compiler output PrintWriter quietWriter = new PrintWriter(new StringWriter()); @@ -279,7 +279,7 @@ public void test_compilerErrorsDoNotBreakNextCompilations() throws Exception { } @Test - public void testNewCompiler() throws Exception { + void testNewCompiler() throws Exception { for (int i = 1; i <= 3; i++) { ClassLoader classLoader = new ClassLoader() { }; diff --git a/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java b/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java index 6cf86ad..09d69a1 100644 --- a/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java +++ b/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java @@ -19,10 +19,10 @@ import static org.junit.jupiter.api.Assertions.*; -public class CompilerUtilsIoTest { +class CompilerUtilsIoTest { @Test - public void writeTextDetectsNoChangeAndReadBytesMatches() throws Exception { + void writeTextDetectsNoChangeAndReadBytesMatches() throws Exception { Path tempDir = Files.createTempDirectory("compiler-utils-io"); Path filePath = tempDir.resolve("sample.txt"); File file = filePath.toFile(); @@ -47,7 +47,7 @@ public void writeTextDetectsNoChangeAndReadBytesMatches() throws Exception { } @Test - public void writeBytesFailsWhenParentIsNotDirectory() throws Exception { + void writeBytesFailsWhenParentIsNotDirectory() throws Exception { Path tempDir = Files.createTempDirectory("compiler-utils-io-error"); Path parentFile = tempDir.resolve("not-a-directory"); Files.createFile(parentFile); @@ -59,7 +59,7 @@ public void writeBytesFailsWhenParentIsNotDirectory() throws Exception { } @Test - public void encodeDecodeUtf8Matches() throws Exception { + void encodeDecodeUtf8Matches() throws Exception { Method encode = CompilerUtils.class.getDeclaredMethod("encodeUTF8", String.class); Method decode = CompilerUtils.class.getDeclaredMethod("decodeUTF8", byte[].class); encode.setAccessible(true); @@ -71,7 +71,7 @@ public void encodeDecodeUtf8Matches() throws Exception { } @Test - public void defineClassLoadsCompiledBytes() throws Exception { + void defineClassLoadsCompiledBytes() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "JDK compiler required for tests"); try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) { @@ -106,14 +106,14 @@ public void defineClassLoadsCompiledBytes() throws Exception { } @Test - public void addClassPathHandlesMissingDirectory() { + void addClassPathHandlesMissingDirectory() { Path nonExisting = Paths.get("not-existing-" + System.nanoTime()); boolean result = CompilerUtils.addClassPath(nonExisting.toString()); assertTrue(!result, "Missing directories should return false"); } @Test - public void addClassPathAddsExistingDirectory() throws Exception { + void addClassPathAddsExistingDirectory() throws Exception { Path tempDir = Files.createTempDirectory("compiler-utils-classpath"); String originalClasspath = System.getProperty("java.class.path"); try { @@ -127,7 +127,7 @@ public void addClassPathAddsExistingDirectory() throws Exception { } @Test - public void readTextInlineShortcutAndReadBytesMissing() throws Exception { + void readTextInlineShortcutAndReadBytesMissing() throws Exception { Method readText = CompilerUtils.class.getDeclaredMethod("readText", String.class); readText.setAccessible(true); String inline = (String) readText.invoke(null, "=inline"); @@ -145,7 +145,7 @@ public void readTextInlineShortcutAndReadBytesMissing() throws Exception { } @Test - public void closeSwallowsExceptions() throws Exception { + void closeSwallowsExceptions() throws Exception { Method closeMethod = CompilerUtils.class.getDeclaredMethod("close", Closeable.class); closeMethod.setAccessible(true); closeMethod.invoke(null, (Closeable) () -> { @@ -154,14 +154,14 @@ public void closeSwallowsExceptions() throws Exception { } @Test - public void closeIgnoresNullReference() throws Exception { + void closeIgnoresNullReference() throws Exception { Method closeMethod = CompilerUtils.class.getDeclaredMethod("close", Closeable.class); closeMethod.setAccessible(true); closeMethod.invoke(null, new Object[]{null}); } @Test - public void getInputStreamSupportsInlineContent() throws Exception { + void getInputStreamSupportsInlineContent() throws Exception { Method method = CompilerUtils.class.getDeclaredMethod("getInputStream", String.class); method.setAccessible(true); try (InputStream is = (InputStream) method.invoke(null, "=inline-data")) { @@ -177,7 +177,7 @@ public void getInputStreamSupportsInlineContent() throws Exception { } @Test - public void getInputStreamRejectsEmptyFilename() throws Exception { + void getInputStreamRejectsEmptyFilename() throws Exception { Method method = CompilerUtils.class.getDeclaredMethod("getInputStream", String.class); method.setAccessible(true); InvocationTargetException ex = assertThrows(InvocationTargetException.class, @@ -186,7 +186,7 @@ public void getInputStreamRejectsEmptyFilename() throws Exception { } @Test - public void getInputStreamUsesSlashFallback() throws Exception { + void getInputStreamUsesSlashFallback() throws Exception { Method method = CompilerUtils.class.getDeclaredMethod("getInputStream", String.class); method.setAccessible(true); ClassLoader original = Thread.currentThread().getContextClassLoader(); @@ -208,13 +208,13 @@ public InputStream getResourceAsStream(String name) { } @Test - public void sanitizePathPreventsTraversal() { + void sanitizePathPreventsTraversal() { assertThrows(IllegalArgumentException.class, () -> CompilerUtils.sanitizePath(Paths.get("..", "escape"))); } @Test - public void writeBytesCreatesMissingParentDirectories() throws Exception { + void writeBytesCreatesMissingParentDirectories() throws Exception { Path tempDir = Files.createTempDirectory("compiler-utils-parent"); Path nested = tempDir.resolve("nested").resolve("file.bin"); boolean changed = CompilerUtils.writeBytes(nested.toFile(), new byte[]{10, 20, 30}); @@ -223,7 +223,7 @@ public void writeBytesCreatesMissingParentDirectories() throws Exception { } @Test - public void readBytesRejectsDirectories() throws Exception { + void readBytesRejectsDirectories() throws Exception { Method readBytes = CompilerUtils.class.getDeclaredMethod("readBytes", File.class); readBytes.setAccessible(true); Path tempDir = Files.createTempDirectory("compiler-utils-dir"); diff --git a/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java b/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java index 32f7dba..e91cd87 100644 --- a/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java +++ b/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java @@ -28,10 +28,10 @@ import static org.junit.jupiter.api.Assertions.*; -public class MyJavaFileManagerTest { +class MyJavaFileManagerTest { @Test - public void bufferedClassReturnedFromInput() throws IOException { + void bufferedClassReturnedFromInput() throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { @@ -61,7 +61,7 @@ public void bufferedClassReturnedFromInput() throws IOException { } @Test - public void getJavaFileForInputDelegatesWhenBufferMissing() throws Exception { + void getJavaFileForInputDelegatesWhenBufferMissing() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager base = compiler.getStandardFileManager(null, null, null)) { @@ -102,7 +102,7 @@ public InputStream openInputStream() { } @Test - public void delegatingMethodsPassThroughToUnderlyingManager() throws IOException { + void delegatingMethodsPassThroughToUnderlyingManager() throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager base = compiler.getStandardFileManager(null, null, null)) { @@ -128,7 +128,7 @@ public void delegatingMethodsPassThroughToUnderlyingManager() throws IOException } @Test - public void listLocationsForModulesAndInferModuleNameDeferToDelegate() throws IOException { + void listLocationsForModulesAndInferModuleNameDeferToDelegate() throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { @@ -154,7 +154,7 @@ public void listLocationsForModulesAndInferModuleNameDeferToDelegate() throws IO } @Test - public void invokeNamedMethodHandlesMissingMethods() throws Exception { + void invokeNamedMethodHandlesMissingMethods() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { @@ -172,7 +172,7 @@ public void invokeNamedMethodHandlesMissingMethods() throws Exception { } @Test - public void invokeNamedMethodWrapsInvocationFailures() throws Exception { + void invokeNamedMethodWrapsInvocationFailures() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager base = compiler.getStandardFileManager(null, null, null)) { @@ -209,7 +209,7 @@ public void invokeNamedMethodWrapsInvocationFailures() throws Exception { @Test @SuppressWarnings("unchecked") - public void getAllBuffersSkipsEntriesWhenFutureFails() throws Exception { + void getAllBuffersSkipsEntriesWhenFutureFails() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler, "System compiler required"); try (StandardJavaFileManager delegate = compiler.getStandardFileManager(null, null, null)) { From 78ab0a7b609bf4f99e7e5f519cd50bd5cb408c9d Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Thu, 26 Mar 2026 17:56:48 +0000 Subject: [PATCH 5/5] Fix remaining JUnit 5 migration regressions --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 1779978..1e0a1f0 100644 --- a/pom.xml +++ b/pom.xml @@ -60,12 +60,6 @@ test - - junit - junit - test - - org.junit.jupiter junit-jupiter