diff --git a/jjava/pom.xml b/jjava/pom.xml index 76daa2d..a326bf6 100644 --- a/jjava/pom.xml +++ b/jjava/pom.xml @@ -31,6 +31,21 @@ org.apache.maven maven-model-builder + + org.junit.jupiter + junit-jupiter-api + test + + + org.testcontainers + testcontainers + test + + + org.slf4j + slf4j-simple + test + @@ -177,4 +192,4 @@ - \ No newline at end of file + diff --git a/jjava/src/main/java/org/dflib/jjava/Env.java b/jjava/src/main/java/org/dflib/jjava/Env.java index 795c30e..aa0764f 100644 --- a/jjava/src/main/java/org/dflib/jjava/Env.java +++ b/jjava/src/main/java/org/dflib/jjava/Env.java @@ -12,7 +12,6 @@ public final class Env { public static final String JJAVA_STARTUP_SCRIPT = "JJAVA_STARTUP_SCRIPT"; public static final String JJAVA_LOAD_EXTENSIONS = "JJAVA_LOAD_EXTENSIONS"; - // not used by Java, but rather by the Python kernel boot script + // not used by JJava, but rather by the kernel launcher script public static final String JJAVA_JVM_OPTS = "JJAVA_JVM_OPTS"; - } diff --git a/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/ContainerizedKernelCase.java b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/ContainerizedKernelCase.java new file mode 100644 index 0000000..1c6fb6a --- /dev/null +++ b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/ContainerizedKernelCase.java @@ -0,0 +1,105 @@ +package org.dflib.jjava.jupyter.kernel; + +import org.junit.jupiter.api.BeforeAll; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.ExecConfig; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.MountableFile; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public abstract class ContainerizedKernelCase { + + private static final Logger LOGGER = LoggerFactory.getLogger(ContainerizedKernelCase.class); + + protected static final GenericContainer container; + protected static final String WORKING_DIRECTORY = "/test"; + protected static final String CONTAINER_KERNELSPEC = "/usr/share/jupyter/kernels/java"; + protected static final String CONTAINER_RESOURCES = WORKING_DIRECTORY + "/resources"; + protected static final String TEST_CLASSPATH = CONTAINER_RESOURCES + "/classes"; + + private static final String BASE_IMAGE = String.format("eclipse-temurin:%s", Runtime.version().feature()); + private static final String FS_KERNELSPEC = "../kernelspec/java"; + private static final String FS_RESOURCES = "src/test/resources"; + + static { + container = new GenericContainer<>(BASE_IMAGE) + .withWorkingDirectory(WORKING_DIRECTORY) + .withCopyToContainer(MountableFile.forHostPath(FS_KERNELSPEC), CONTAINER_KERNELSPEC) + .withCopyToContainer(MountableFile.forHostPath(FS_RESOURCES), CONTAINER_RESOURCES) + .withCommand("bash", "-c", getStartupCommand()) + .withLogConsumer(new Slf4jLogConsumer(LOGGER)) + .waitingFor(Wait.forSuccessfulCommand(getSuccessfulCommand())) + .withStartupTimeout(Duration.ofMinutes(5)); + container.start(); + } + + @BeforeAll + static void compileSources() throws IOException, InterruptedException { + String source = "$(find " + CONTAINER_RESOURCES + "/src -name '*.java')"; + Container.ExecResult compileResult = executeInContainer("javac -d " + TEST_CLASSPATH + " " + source); + + assertEquals("", compileResult.getStdout()); + assertEquals("", compileResult.getStderr()); + } + + protected static Container.ExecResult executeInContainer(String... commands) throws IOException, InterruptedException { + List wrappedCommands = new ArrayList<>(); + wrappedCommands.add("bash"); + wrappedCommands.add("-c"); + wrappedCommands.addAll(List.of(commands)); + return container.execInContainer(wrappedCommands.toArray(new String[]{})); + } + + protected static Container.ExecResult executeInKernel(String snippet) throws IOException, InterruptedException { + return executeInKernel(snippet, Collections.emptyMap()); + } + + protected static Container.ExecResult executeInKernel(String snippet, Map env) throws IOException, InterruptedException { + String snippet64 = Base64.getEncoder().encodeToString(snippet.getBytes()); + String jupyterCommand = venvCommand("jupyter console --kernel=java --simple-prompt"); + String[] containerCommand = new String[]{"bash", "-c", "echo \"" + snippet64 + "\" | base64 -d | " + jupyterCommand}; + Container.ExecResult execResult = container.execInContainer(ExecConfig.builder() + .envVars(env) + .command(containerCommand) + .build() + ); + LOGGER.info("env = {}", env); + LOGGER.info("snippet = {}", snippet); + LOGGER.debug("stderr = {}", execResult.getStderr()); + LOGGER.debug("stdout = {}", execResult.getStdout()); + return execResult; + } + + private static String getStartupCommand() { + return String.join(" && ", + "apt-get update", + "apt-get install --no-install-recommends -y python3 python3-pip python3-venv", + "python3 -m venv ./venv", + venvCommand("pip install jupyter-console --progress-bar off"), + "tail -f /dev/null" + ); + } + + private static String getSuccessfulCommand() { + return venvCommand("jupyter kernelspec list") + + " | grep ' java ' && " + + venvCommand("jupyter console --version"); + } + + private static String venvCommand(String command) { + return WORKING_DIRECTORY + "/venv/bin/" + command; + } +} diff --git a/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelEnvIT.java b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelEnvIT.java new file mode 100644 index 0000000..bf1e32a --- /dev/null +++ b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelEnvIT.java @@ -0,0 +1,105 @@ +package org.dflib.jjava.jupyter.kernel; + +import org.dflib.jjava.Env; +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.Container; + +import java.util.Map; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +class KernelEnvIT extends ContainerizedKernelCase { + + @Test + void compilerOpts() throws Exception { + Map env = Map.of(Env.JJAVA_COMPILER_OPTS, "-source 9"); + String snippet = "var value = 1;"; + Container.ExecResult snippetResult = executeInKernel(snippet, env); + + assertThat(snippetResult.getStderr(), CoreMatchers.allOf( + containsString("| var value = 1;"), + containsString(Runtime.version().feature() == 11 + ? "'var' is a restricted local variable type" + : "'var' is a restricted type name") + )); + } + + @Test + void timeout() throws Exception { + Map env = Map.of(Env.JJAVA_TIMEOUT, "3000"); + String snippet = "Thread.sleep(5000);"; + Container.ExecResult snippetResult = executeInKernel(snippet, env); + + assertThat(snippetResult.getStderr(), CoreMatchers.allOf( + containsString("| " + snippet), + containsString("Evaluation timed out after 3000 milliseconds.") + )); + } + + @Test + void classpath() throws Exception { + Map env = Map.of(Env.JJAVA_CLASSPATH, TEST_CLASSPATH); + String snippet = String.join("\n", + "import org.dflib.jjava.Dummy;", + "Dummy.class.getName()" + ); + Container.ExecResult snippetResult = executeInKernel(snippet, env); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("org.dflib.jjava.Dummy")); + } + + @Test + void startUpScriptsPath() throws Exception { + Map env = Map.of(Env.JJAVA_STARTUP_SCRIPTS_PATH, CONTAINER_RESOURCES + "/test-ping.jshell"); + String snippet = "ping()"; + Container.ExecResult snippetResult = executeInKernel(snippet, env); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("pong!")); + } + + @Test + void startUpScript() throws Exception { + Map env = Map.of(Env.JJAVA_STARTUP_SCRIPT, "public String ping() { return \"pong!\"; }"); + String snippet = "ping()"; + Container.ExecResult snippetResult = executeInKernel(snippet, env); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("pong!")); + } + + @Test + void loadExtensions_Default() throws Exception { + String snippet = "printf(\"Hello, %s!\", \"world\");"; + Container.ExecResult snippetResult = executeInKernel(snippet); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("Hello, world!")); + } + + @Test + void loadExtensions_Disable() throws Exception { + Map env = Map.of(Env.JJAVA_LOAD_EXTENSIONS, "0"); + String snippet = "printf(\"Hello, %s!\", \"world\");"; + Container.ExecResult snippetResult = executeInKernel(snippet, env); + + assertThat(snippetResult.getStderr(), CoreMatchers.allOf( + containsString("| " + snippet), + containsString("cannot find symbol") + )); + } + + @Test + void jvmOpts() throws Exception { + Map env = Map.of(Env.JJAVA_JVM_OPTS, "-Xmx300m"); + String snippet = "Runtime.getRuntime().maxMemory()"; + Container.ExecResult snippetResult = executeInKernel(snippet, env); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString(String.valueOf(300 * (int) Math.pow(1024, 2)))); + } +} diff --git a/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelMagicIT.java b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelMagicIT.java new file mode 100644 index 0000000..2bf71a6 --- /dev/null +++ b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelMagicIT.java @@ -0,0 +1,85 @@ +package org.dflib.jjava.jupyter.kernel; + +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.Container; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class KernelMagicIT extends ContainerizedKernelCase { + + @Test + void jars() throws Exception { + String jar = CONTAINER_RESOURCES + "/jakarta.annotation-api-3.0.0.jar"; + Container.ExecResult fetchResult = container.execInContainer( + "curl", "-L", "-s", "-S", "-f", + "https://repo1.maven.org/maven2/jakarta/annotation/jakarta.annotation-api/3.0.0/jakarta.annotation-api-3.0.0.jar", + "-o", jar + ); + assertEquals("", fetchResult.getStderr()); + + String snippet = String.join("\n", + "%jars " + jar, + "import jakarta.annotation.Nullable;", + "Nullable.class.getName()" + ); + Container.ExecResult snippetResult = executeInKernel(snippet); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("jakarta.annotation.Nullable")); + } + + @Test + void classpath() throws Exception { + String snippet = String.join("\n", + "%classpath " + TEST_CLASSPATH, + "import org.dflib.jjava.Dummy;", + "Dummy.class.getName()" + ); + Container.ExecResult snippetResult = executeInKernel(snippet); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("org.dflib.jjava.Dummy")); + } + + @Test + void addMavenDependencies() throws Exception { + String snippet = String.join("\n", + "%maven org.dflib:dflib-jupyter:1.0.0-RC1", + "System.getProperty(\"java.class.path\")" + ); + Container.ExecResult snippetResult = executeInKernel(snippet); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("dflib-jupyter-1.0.0-RC1.jar")); + } + + @Test + void load() throws Exception { + String script = CONTAINER_RESOURCES + "/test-ping.jshell"; + String snippet = String.join("\n", + "%load " + script, + "ping()" + ); + Container.ExecResult snippetResult = executeInKernel(snippet); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("pong!")); + } + + @Test + void loadFromPOM() throws Exception { + String pom = CONTAINER_RESOURCES + "/test-pom.xml"; + String snippet = String.join("\n", + "%loadFromPOM " + pom, + "import jakarta.annotation.Nullable;", + "Nullable.class.getName()" + ); + Container.ExecResult snippetResult = executeInKernel(snippet); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("jakarta.annotation.Nullable")); + } +} diff --git a/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelStartupIT.java b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelStartupIT.java new file mode 100644 index 0000000..53ad6fd --- /dev/null +++ b/jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelStartupIT.java @@ -0,0 +1,20 @@ +package org.dflib.jjava.jupyter.kernel; + +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.Container; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +class KernelStartupIT extends ContainerizedKernelCase { + + @Test + void startUp() throws Exception { + String snippet = "1000d + 1"; + Container.ExecResult snippetResult = executeInKernel(snippet); + + assertThat(snippetResult.getStderr(), not(containsString("|"))); + assertThat(snippetResult.getStdout(), containsString("1001.0")); + } +} diff --git a/jjava/src/test/resources/src/org/dflib/jjava/Dummy.java b/jjava/src/test/resources/src/org/dflib/jjava/Dummy.java new file mode 100644 index 0000000..121a247 --- /dev/null +++ b/jjava/src/test/resources/src/org/dflib/jjava/Dummy.java @@ -0,0 +1,4 @@ +package org.dflib.jjava; + +public class Dummy { +} diff --git a/jjava/src/test/resources/test-ping.jshell b/jjava/src/test/resources/test-ping.jshell new file mode 100644 index 0000000..46fcb4d --- /dev/null +++ b/jjava/src/test/resources/test-ping.jshell @@ -0,0 +1,3 @@ +public String ping() { + return "pong!"; +} diff --git a/jjava/src/test/resources/test-pom.xml b/jjava/src/test/resources/test-pom.xml new file mode 100644 index 0000000..8e72f59 --- /dev/null +++ b/jjava/src/test/resources/test-pom.xml @@ -0,0 +1,15 @@ + + 4.0.0 + + org.dflib.jjava + jjava-test + 1.0 + + + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + + + diff --git a/jupyter-jvm-basekernel/pom.xml b/jupyter-jvm-basekernel/pom.xml index 0de849d..0ef627b 100644 --- a/jupyter-jvm-basekernel/pom.xml +++ b/jupyter-jvm-basekernel/pom.xml @@ -22,8 +22,13 @@ - junit - junit + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params test diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RenderRequestTypesResolutionTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RenderRequestTypesResolutionTest.java index b7cb0ca..fd42cfb 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RenderRequestTypesResolutionTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RenderRequestTypesResolutionTest.java @@ -1,46 +1,22 @@ package org.dflib.jjava.jupyter.kernel.display; import org.dflib.jjava.jupyter.kernel.display.mime.MIMEType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.List; +import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@RunWith(Parameterized.class) public class RenderRequestTypesResolutionTest { - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList(new Object[][]{ - { "image/svg+xml", "image/svg+xml", Collections.singletonList("image/svg+xml") }, - { "image/svg+xml", "image/svg", Collections.singletonList("image/svg") }, - { "image/svg+xml", "image/svg+xml", Collections.singletonList("image/*") }, - { "image/svg+xml", "image/svg+xml", Collections.singletonList("image") }, - { "image/svg+xml", "application/xml", Collections.singletonList("application/xml") }, - { "image/svg+xml", "application/xml", Collections.singletonList("application/*") }, - { "image/svg+xml", "application/xml", Collections.singletonList("application") }, - { "image/svg+xml", "image/svg+xml", Collections.singletonList("*") }, - - { "image/svg", "image/svg", Collections.singletonList("image/svg") }, - - { "image/svg", null, Collections.singletonList("application/xml") }, - { "image/svg+xml", null, Collections.singletonList("application/json") }, - }); - } - - private final MIMEType supported; - private final MIMEType expected; - private final RenderRequestTypes requestTypes; - - public RenderRequestTypesResolutionTest(String supported, String expected, List requestTypes) { - this.supported = supported == null ? null : MIMEType.parse(supported); - this.expected = expected == null ? null : MIMEType.parse(expected); + @ParameterizedTest + @MethodSource("data") + public void test(String supported, String expected, List requestTypes) { + MIMEType supportedMime = MIMEType.parse(supported); + MIMEType expectedMime = expected == null ? null : MIMEType.parse(expected); RenderRequestTypes.Builder builder = new RenderRequestTypes.Builder(group -> { switch (group) { case "xml": @@ -54,13 +30,26 @@ public RenderRequestTypesResolutionTest(String supported, String expected, List< requestTypes.stream() .map(MIMEType::parse) .forEach(builder::withType); - this.requestTypes = builder.build(); - } + RenderRequestTypes renderRequestTypes = builder.build(); - @Test - public void test() { - MIMEType actual = this.requestTypes.resolveSupportedType(this.supported); + MIMEType actualMime = renderRequestTypes.resolveSupportedType(supportedMime); + + assertEquals(expectedMime, actualMime); + } - assertEquals(expected, actual); + public static Stream data() { + return Stream.of( + Arguments.of("image/svg+xml", "image/svg+xml", List.of("image/svg+xml")), + Arguments.of("image/svg+xml", "image/svg", List.of("image/svg")), + Arguments.of("image/svg+xml", "image/svg+xml", List.of("image/*")), + Arguments.of("image/svg+xml", "image/svg+xml", List.of("image")), + Arguments.of("image/svg+xml", "application/xml", List.of("application/xml")), + Arguments.of("image/svg+xml", "application/xml", List.of("application/*")), + Arguments.of("image/svg+xml", "application/xml", List.of("application")), + Arguments.of("image/svg+xml", "image/svg+xml", List.of("*")), + Arguments.of("image/svg", "image/svg", List.of("image/svg")), + Arguments.of("image/svg", null, List.of("application/xml")), + Arguments.of("image/svg+xml", null, List.of("application/json")) + ); } -} \ No newline at end of file +} diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RendererTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RendererTest.java index 534e617..ac69d03 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RendererTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/RendererTest.java @@ -1,21 +1,21 @@ package org.dflib.jjava.jupyter.kernel.display; import org.dflib.jjava.jupyter.kernel.display.mime.MIMEType; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; -public class RendererTest { +class RendererTest { private Renderer renderer; - @Before + @BeforeEach public void setUp() { this.renderer = new Renderer(); this.renderer.createRegistration(D.class) @@ -49,209 +49,29 @@ public void setUp() { }); } - class A { - @Override - public String toString() { - return "A"; - } - } - - class B implements DisplayDataRenderable { - @Override - public Set getSupportedRenderTypes() { - return Collections.singleton(MIMEType.TEXT_MARKDOWN); - } - - @Override - public Set getPreferredRenderTypes() { - return Collections.singleton(MIMEType.TEXT_MARKDOWN); - } - - @Override - public void render(RenderContext context) { - context.renderIfRequested(MIMEType.TEXT_MARKDOWN, () -> "**B**"); - } - - @Override - public String toString() { - return "B"; - } - } - - class C implements DisplayDataRenderable { - private final Set supported = new LinkedHashSet<>(); - - C() { - this.supported.add(MIMEType.TEXT_MARKDOWN); - this.supported.add(MIMEType.TEXT_CSS); - } - - @Override - public Set getSupportedRenderTypes() { - return supported; - } - - @Override - public Set getPreferredRenderTypes() { - return Collections.singleton(MIMEType.TEXT_CSS); - } - - @Override - public void render(RenderContext context) { - context.renderIfRequested(MIMEType.TEXT_MARKDOWN, () -> "**C**"); - context.renderIfRequested(MIMEType.TEXT_CSS, () -> ".c{}"); - } - - @Override - public String toString() { - return "C"; - } - } - - class D { - public String html() { - return ""; - } - - @Override - public String toString() { - return "D"; - } - } - - class E implements DisplayDataRenderable { - @Override - public Set getSupportedRenderTypes() { - return Collections.singleton(MIMEType.ANY); - } - - @Override - public void render(RenderContext context) { - context.renderIfRequested(MIMEType.TEXT_HTML, () -> ""); - context.renderIfRequested(MIMEType.TEXT_CSS, () -> ".e{}"); - context.renderIfRequested(MIMEType.APPLICATION_JAVASCRIPT, () -> "e();"); - } - - @Override - public String toString() { - return "E"; - } - } - - class F { - public String html() { - return ""; - } - - public String css() { - return ".f{}"; - } - - public String js() { - return "f();"; - } - - @Override - public String toString() { - return "F"; - } - } - - class G implements DisplayDataRenderable { - @Override - public Set getSupportedRenderTypes() { - return new LinkedHashSet<>(Arrays.asList(MIMEType.parse("text/*"), MIMEType.APPLICATION_JAVASCRIPT)); - } - - @Override - public void render(RenderContext context) { - context.renderIfRequested(MIMEType.TEXT_HTML, () -> ""); - context.renderIfRequested(MIMEType.TEXT_CSS, () -> ".g{}"); - context.renderIfRequested(MIMEType.TEXT_LATEX, () -> "\\g"); - context.renderIfRequested(MIMEType.APPLICATION_JAVASCRIPT, () -> "g();"); - } - - @Override - public String toString() { - return "G"; - } - } - - class H { - public String html() { - return ""; - } - - public String css() { - return ".h{}"; - } - - public String js() { - return "h();"; - } - - @Override - public String toString() { - return "H"; - } - } - - class I implements DisplayDataRenderable { - @Override - public Set getSupportedRenderTypes() { - return new LinkedHashSet<>(Arrays.asList(MIMEType.TEXT_PLAIN, MIMEType.APPLICATION_JAVASCRIPT)); - } - - @Override - public void render(RenderContext context) { - context.renderIfRequested(MIMEType.APPLICATION_JAVASCRIPT, () -> "i();"); - context.renderIfRequested(MIMEType.TEXT_PLAIN, () -> "I!"); - } - - @Override - public String toString() { - return "I"; - } - } - - class J { - public String js() { - return "j();"; - } - - public String pretty() { - return "J!"; - } - - @Override - public String toString() { - return "J"; - } - } - @Test - public void rendersPlainText() { + void rendersPlainText() { DisplayData data = this.renderer.render(new A()); assertEquals("A", data.getData(MIMEType.TEXT_PLAIN)); } @Test - public void alwaysRendersPlainText() { + void alwaysRendersPlainText() { DisplayData data = this.renderer.render(new B()); assertEquals("B", data.getData(MIMEType.TEXT_PLAIN)); } @Test - public void rendersPreferred() { + void rendersPreferred() { DisplayData data = this.renderer.render(new B()); assertEquals("**B**", data.getData(MIMEType.TEXT_MARKDOWN)); } @Test - public void rendersJustPreferred() { + void rendersJustPreferred() { DisplayData data = this.renderer.render(new C()); assertEquals(".c{}", data.getData(MIMEType.TEXT_CSS)); @@ -260,7 +80,7 @@ public void rendersJustPreferred() { } @Test - public void rendersExternal() { + void rendersExternal() { DisplayData data = this.renderer.render(new D()); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -269,7 +89,7 @@ public void rendersExternal() { } @Test - public void rendersAs() { + void rendersAs() { DisplayData data = this.renderer.renderAs(new C(), "text/markdown"); assertEquals("**C**", data.getData(MIMEType.TEXT_MARKDOWN)); @@ -278,7 +98,7 @@ public void rendersAs() { } @Test - public void rendersAsExternal() { + void rendersAsExternal() { DisplayData data = this.renderer.renderAs(new D(), "text/latex"); assertEquals("\\d", data.getData(MIMEType.TEXT_LATEX)); @@ -287,7 +107,7 @@ public void rendersAsExternal() { } @Test - public void supportsPreferringAll() { + void supportsPreferringAll() { DisplayData data = this.renderer.render(new E()); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -297,7 +117,7 @@ public void supportsPreferringAll() { } @Test - public void supportsPreferringAllExternal() { + void supportsPreferringAllExternal() { DisplayData data = this.renderer.render(new F()); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -307,7 +127,7 @@ public void supportsPreferringAllExternal() { } @Test - public void supportsPreferringAllRequestingAll() { + void supportsPreferringAllRequestingAll() { DisplayData data = this.renderer.renderAs(new E(), "*"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -317,7 +137,7 @@ public void supportsPreferringAllRequestingAll() { } @Test - public void supportsPreferringAllRequestingAllExternal() { + void supportsPreferringAllRequestingAllExternal() { DisplayData data = this.renderer.renderAs(new F(), "*"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -327,7 +147,7 @@ public void supportsPreferringAllRequestingAllExternal() { } @Test - public void supportsPreferringAllRequestingSome() { + void supportsPreferringAllRequestingSome() { DisplayData data = this.renderer.renderAs(new E(), "text/html"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -337,7 +157,7 @@ public void supportsPreferringAllRequestingSome() { } @Test - public void supportsPreferringAllRequestingSomeExternal() { + void supportsPreferringAllRequestingSomeExternal() { DisplayData data = this.renderer.renderAs(new F(), "text/html"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -347,7 +167,7 @@ public void supportsPreferringAllRequestingSomeExternal() { } @Test - public void supportsPreferringAllRequestingGroup() { + void supportsPreferringAllRequestingGroup() { DisplayData data = this.renderer.renderAs(new E(), "text/*"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -357,7 +177,7 @@ public void supportsPreferringAllRequestingGroup() { } @Test - public void supportsPreferringAllRequestingGroupExternal() { + void supportsPreferringAllRequestingGroupExternal() { DisplayData data = this.renderer.renderAs(new F(), "text/*"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -367,7 +187,7 @@ public void supportsPreferringAllRequestingGroupExternal() { } @Test - public void supportsPreferringGroup() { + void supportsPreferringGroup() { DisplayData data = this.renderer.render(new G()); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -377,7 +197,7 @@ public void supportsPreferringGroup() { } @Test - public void supportsPreferringGroupExternal() { + void supportsPreferringGroupExternal() { DisplayData data = this.renderer.render(new H()); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -387,7 +207,7 @@ public void supportsPreferringGroupExternal() { } @Test - public void supportsPreferringGroupRequestingSome() { + void supportsPreferringGroupRequestingSome() { DisplayData data = this.renderer.renderAs(new G(), "text/html"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -397,7 +217,7 @@ public void supportsPreferringGroupRequestingSome() { } @Test - public void supportsPreferringGroupRequestingSomeExternal() { + void supportsPreferringGroupRequestingSomeExternal() { DisplayData data = this.renderer.renderAs(new H(), "text/html"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -407,7 +227,7 @@ public void supportsPreferringGroupRequestingSomeExternal() { } @Test - public void supportsPreferringGroupRequestingGroup() { + void supportsPreferringGroupRequestingGroup() { DisplayData data = this.renderer.renderAs(new G(), "text/*"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -417,7 +237,7 @@ public void supportsPreferringGroupRequestingGroup() { } @Test - public void supportsPreferringGroupRequestingGroupExternal() { + void supportsPreferringGroupRequestingGroupExternal() { DisplayData data = this.renderer.renderAs(new H(), "text/*"); assertEquals("", data.getData(MIMEType.TEXT_HTML)); @@ -427,21 +247,21 @@ public void supportsPreferringGroupRequestingGroupExternal() { } @Test - public void supportsOverridingTextRepresentation() { + void supportsOverridingTextRepresentation() { DisplayData data = this.renderer.render(new I()); assertEquals("I!", data.getData(MIMEType.TEXT_PLAIN)); } @Test - public void supportsOverridingTextRepresentationExternal() { + void supportsOverridingTextRepresentationExternal() { DisplayData data = this.renderer.render(new J()); assertEquals("J!", data.getData(MIMEType.TEXT_PLAIN)); } @Test - public void supportsOverridingTextRepresentationWhenNotRequested() { + void supportsOverridingTextRepresentationWhenNotRequested() { DisplayData data = this.renderer.renderAs(new I(), "application/javascript"); assertEquals("i();", data.getData(MIMEType.APPLICATION_JAVASCRIPT)); @@ -449,10 +269,190 @@ public void supportsOverridingTextRepresentationWhenNotRequested() { } @Test - public void supportsOverridingTextRepresentationWhenNotRequestedExternal() { + void supportsOverridingTextRepresentationWhenNotRequestedExternal() { DisplayData data = this.renderer.renderAs(new J(), "application/javascript"); assertEquals("j();", data.getData(MIMEType.APPLICATION_JAVASCRIPT)); assertEquals("J!", data.getData(MIMEType.TEXT_PLAIN)); } -} \ No newline at end of file + + class A { + @Override + public String toString() { + return "A"; + } + } + + class B implements DisplayDataRenderable { + @Override + public Set getSupportedRenderTypes() { + return Collections.singleton(MIMEType.TEXT_MARKDOWN); + } + + @Override + public Set getPreferredRenderTypes() { + return Collections.singleton(MIMEType.TEXT_MARKDOWN); + } + + @Override + public void render(RenderContext context) { + context.renderIfRequested(MIMEType.TEXT_MARKDOWN, () -> "**B**"); + } + + @Override + public String toString() { + return "B"; + } + } + + class C implements DisplayDataRenderable { + private final Set supported = new LinkedHashSet<>(); + + C() { + this.supported.add(MIMEType.TEXT_MARKDOWN); + this.supported.add(MIMEType.TEXT_CSS); + } + + @Override + public Set getSupportedRenderTypes() { + return supported; + } + + @Override + public Set getPreferredRenderTypes() { + return Collections.singleton(MIMEType.TEXT_CSS); + } + + @Override + public void render(RenderContext context) { + context.renderIfRequested(MIMEType.TEXT_MARKDOWN, () -> "**C**"); + context.renderIfRequested(MIMEType.TEXT_CSS, () -> ".c{}"); + } + + @Override + public String toString() { + return "C"; + } + } + + class D { + public String html() { + return ""; + } + + @Override + public String toString() { + return "D"; + } + } + + class E implements DisplayDataRenderable { + @Override + public Set getSupportedRenderTypes() { + return Collections.singleton(MIMEType.ANY); + } + + @Override + public void render(RenderContext context) { + context.renderIfRequested(MIMEType.TEXT_HTML, () -> ""); + context.renderIfRequested(MIMEType.TEXT_CSS, () -> ".e{}"); + context.renderIfRequested(MIMEType.APPLICATION_JAVASCRIPT, () -> "e();"); + } + + @Override + public String toString() { + return "E"; + } + } + + class F { + public String html() { + return ""; + } + + public String css() { + return ".f{}"; + } + + public String js() { + return "f();"; + } + + @Override + public String toString() { + return "F"; + } + } + + class G implements DisplayDataRenderable { + @Override + public Set getSupportedRenderTypes() { + return new LinkedHashSet<>(Arrays.asList(MIMEType.parse("text/*"), MIMEType.APPLICATION_JAVASCRIPT)); + } + + @Override + public void render(RenderContext context) { + context.renderIfRequested(MIMEType.TEXT_HTML, () -> ""); + context.renderIfRequested(MIMEType.TEXT_CSS, () -> ".g{}"); + context.renderIfRequested(MIMEType.TEXT_LATEX, () -> "\\g"); + context.renderIfRequested(MIMEType.APPLICATION_JAVASCRIPT, () -> "g();"); + } + + @Override + public String toString() { + return "G"; + } + } + + class H { + public String html() { + return ""; + } + + public String css() { + return ".h{}"; + } + + public String js() { + return "h();"; + } + + @Override + public String toString() { + return "H"; + } + } + + class I implements DisplayDataRenderable { + @Override + public Set getSupportedRenderTypes() { + return new LinkedHashSet<>(Arrays.asList(MIMEType.TEXT_PLAIN, MIMEType.APPLICATION_JAVASCRIPT)); + } + + @Override + public void render(RenderContext context) { + context.renderIfRequested(MIMEType.APPLICATION_JAVASCRIPT, () -> "i();"); + context.renderIfRequested(MIMEType.TEXT_PLAIN, () -> "I!"); + } + + @Override + public String toString() { + return "I"; + } + } + + class J { + public String js() { + return "j();"; + } + + public String pretty() { + return "J!"; + } + + @Override + public String toString() { + return "J"; + } + } +} diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/mime/MIMETypeTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/mime/MIMETypeTest.java index e61342b..b96c44c 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/mime/MIMETypeTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/display/mime/MIMETypeTest.java @@ -1,51 +1,29 @@ package org.dflib.jjava.jupyter.kernel.display.mime; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; -import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.Assert.assertEquals; - -@RunWith(Parameterized.class) public class MIMETypeTest { - @Parameters(name = "{index}: MIMEType.parse({0}) = new MIMEType({1}, {2}, {3}, {4})") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - { "application/json", "application", null, "json", null }, - { "application/xhtml+xml", "application", null, "xhtml", "xml" }, - { "image/*", "image", null, "*", null }, - { "image/", "image", null, "", null }, - { "video", "video", null, null, null }, - { "video", "video", null, null, null }, - { "application/vnd.media", "application", "vnd", "media", null }, - { "application/vnd.media.producer", "application", "vnd", "media.producer", null }, - { "application/vnd.media.producer+suffix", "application", "vnd", "media.producer", "suffix" }, - { "application/vnd.media.named+producer+suffix", "application", "vnd", "media.named+producer", "suffix" }, - }); - } - - private final String raw; - private final String type; - private final String tree; - private final String subtype; - private final String suffix; - - public MIMETypeTest(String raw, String type, String tree, String subtype, String suffix) { - this.raw = raw; - this.type = type; - this.tree = tree; - this.subtype = subtype; - this.suffix = suffix; - } - - @Test - public void test() { - MIMEType parsed = MIMEType.parse(this.raw); - MIMEType expected = new MIMEType(this.type, this.tree, this.subtype, this.suffix); - assertEquals(expected, parsed); + @ParameterizedTest(name = "{index}: MIMEType.parse({0}) = new MIMEType({1}, {2}, {3}, {4})") + @CsvSource({ + "application/json, application, , json, ", + "application/xhtml+xml, application, , xhtml, xml", + "image/*, image, , *, ", + "image/, image, , '', ", + "video, video, , , ", + "video, video, , , ", + "application/vnd.media, application, vnd, media, ", + "application/vnd.media.producer, application, vnd, media.producer, ", + "application/vnd.media.producer+suffix, application, vnd, media.producer, suffix", + "application/vnd.media.named+producer+suffix, application, vnd, media.named+producer, suffix" + }) + public void test(String raw, String type, String tree, String subtype, String suffix) { + MIMEType expected = new MIMEType(type, tree, subtype, suffix); + MIMEType actual = MIMEType.parse(raw); + + assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/MagicParserTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/MagicParserTest.java index 1747ac5..dac440b 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/MagicParserTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/MagicParserTest.java @@ -1,25 +1,19 @@ package org.dflib.jjava.jupyter.kernel.magic; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class MagicParserTest { - public static List split(String args) { - return MagicParser.split(args); - } - private MagicParser inlineParser; private MagicParser solParser; - @Before + @BeforeEach public void setUp() { this.inlineParser = new MagicParser("//%", "//%%"); this.solParser = new MagicParser("^\\s*//%", "//%%"); @@ -139,4 +133,8 @@ public void startOfLineParserSkipsInline() { assertEquals(expectedTransformedCell, transformedCell); } -} \ No newline at end of file + + public static List split(String args) { + return MagicParser.split(args); + } +} diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsArgsTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsArgsTest.java index ab40cda..9260b94 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsArgsTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsArgsTest.java @@ -2,195 +2,353 @@ import org.dflib.jjava.jupyter.kernel.magic.MagicParserTest; import org.hamcrest.Matcher; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Stream; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasEntry; -import static org.junit.Assert.assertThat; -@RunWith(Parameterized.class) public class MagicsArgsTest { - private static MagicsArgs args(Consumer config) { - MagicsArgs.MagicsArgsBuilder builder = MagicsArgs.builder(); - config.accept(builder); - return builder.build(); - } - private static List list(String... args) { - return Arrays.asList(args); + @ParameterizedTest(name = "{index}: \"{0}\" with \"{1}\"") + @MethodSource + public void values(MagicsArgs schema, String args, Matcher>> matcher) { + List rawArgs = MagicParserTest.split(args); + Map> actual = Assertions.assertDoesNotThrow(() -> schema.parse(rawArgs)); + + assertThat(actual, matcher); } - @Parameterized.Parameters(name = "{index}: \"{0}\" with \"{1}\"") - public static Collection data() { - return Arrays.asList(new Object[][]{ - { args(b -> b.required("a")), + public static Stream values() { + return Stream.of( + Arguments.of( + args(b -> b.required("a")), "value-a", - hasEntry("a", list("value-a")) }, - { args(b -> b.required("a").optional("b")), + hasEntry("a", list("value-a")) + ), + Arguments.of( + args(b -> b.required("a").optional("b")), "value-a", - allOf( - hasEntry("a", list("value-a")), - hasEntry("b", list()) - ) }, - { args(b -> b.required("a").optional("b")), + allOf(hasEntry("a", list("value-a")), hasEntry("b", list())) + ), + Arguments.of( + args(b -> b.required("a").optional("b")), "value-a value-b", allOf( hasEntry("a", list("value-a")), hasEntry("b", list("value-b")) - ) }, - { args(b -> b.required("a").optional("b").varargs("c")), + ) + ), + Arguments.of( + args(b -> b.required("a").optional("b").varargs("c")), "value-a value-b", allOf( hasEntry("a", list("value-a")), hasEntry("b", list("value-b")), hasEntry("c", list()) - ) }, - { args(b -> b.required("a").optional("b").varargs("c")), + ) + ), + Arguments.of(args(b -> b.required("a").optional("b").varargs("c")), "value-a value-b value-c", allOf( hasEntry("a", list("value-a")), hasEntry("b", list("value-b")), hasEntry("c", list("value-c")) - ) }, - { args(b -> b.required("a").optional("b").varargs("c")), + ) + ), + Arguments.of( + args(b -> b.required("a").optional("b").varargs("c")), "value-a value-b value-c-1 value-c-2", allOf( hasEntry("a", list("value-a")), hasEntry("b", list("value-b")), hasEntry("c", list("value-c-1", "value-c-2")) - ) }, - { args(b -> b.required("a").required("b").varargs("c")), + ) + ), + Arguments.of( + args(b -> b.required("a").required("b").varargs("c")), "value-a value-b value-c-1 value-c-2", allOf( hasEntry("a", list("value-a")), hasEntry("b", list("value-b")), hasEntry("c", list("value-c-1", "value-c-2")) - ) }, - { args(b -> b.optional("a")), "", hasEntry("a", list()) }, - { args(b -> b.optional("a").varargs("b")), + ) + ), + Arguments.of( + args(b -> b.optional("a")), + "", + hasEntry("a", list()) + ), + Arguments.of( + args(b -> b.optional("a").varargs("b")), "", allOf( hasEntry("a", list()), hasEntry("b", list()) - ) }, - { args(b -> b.optional("a").varargs("b")), + ) + ), + Arguments.of( + args(b -> b.optional("a").varargs("b")), + "value-a", + allOf( + hasEntry("a", list("value-a")), + hasEntry("b", list()) + ) + ), + Arguments.of( + args(b -> b.optional("a").varargs("b")), "value-a", allOf( hasEntry("a", list("value-a")), hasEntry("b", list()) - ) }, - { args(b -> b.optional("a").varargs("b")), - "value-a", allOf( - hasEntry("a", list("value-a")), - hasEntry("b", list()) - ) }, - { args(b -> b.varargs("a")), + ) + ), + Arguments.of( + args(b -> b.varargs("a")), "", - hasEntry("a", list()) }, - { args(b -> b.varargs("a")), + hasEntry("a", list()) + ), + Arguments.of( + args(b -> b.varargs("a")), "value-a", - hasEntry("a", list("value-a")) }, - { args(b -> b.required("a").optional("a")), + hasEntry("a", list("value-a")) + ), + Arguments.of( + args(b -> b.required("a").optional("a")), "value-a extra-a", - hasEntry("a", list("value-a", "extra-a")) }, - { args(b -> b.required("a").optional("a")), + hasEntry("a", list("value-a", "extra-a")) + ), + Arguments.of( + args(b -> b.required("a").optional("a")), "value-a", - hasEntry("a", list("value-a")) }, - { args(b -> b.required("a").varargs("a")), + hasEntry("a", list("value-a")) + ), + Arguments.of( + args(b -> b.required("a").varargs("a")), "value-a extra-a extra-a-2", - hasEntry("a", list("value-a", "extra-a", "extra-a-2")) }, - - // FLAGS - { args(b -> {}), "-f", hasEntry("f", list("")) }, - { args(b -> {}), "-fff", hasEntry("f", list("", "", "")) }, - { args(b -> {}), "-fg -g", allOf( - hasEntry("f", list("")), - hasEntry("g", list("", "")) - ) }, - { args(b -> b.flag("test", 'f')), "", hasEntry("test", list()) }, - { args(b -> b.flag("verbose", 'v', "true")), + hasEntry("a", list("value-a", "extra-a", "extra-a-2")) + ) + ); + } + + @ParameterizedTest(name = "{index}: \"{0}\" with \"{1}\"") + @MethodSource + public void flags(MagicsArgs schema, String args, Matcher>> matcher) { + List rawArgs = MagicParserTest.split(args); + Map> actual = Assertions.assertDoesNotThrow(() -> schema.parse(rawArgs)); + + assertThat(actual, matcher); + } + + public static Stream flags() { + return Stream.of( + Arguments.of( + args(b -> {}), + "-f", + hasEntry("f", list("")) + ), + Arguments.of( + args(b -> {}), + "-fff", + hasEntry("f", list("", "", "")) + ), + Arguments.of( + args(b -> {}), + "-fg -g", + allOf(hasEntry("f", list("")), hasEntry("g", list("", ""))) + ), + Arguments.of( + args(b -> b.flag("test", 'f')), + "", + hasEntry("test", list()) + ), + Arguments.of( + args(b -> b.flag("verbose", 'v', "true")), "-v", - hasEntry("verbose", list("true")) }, - - // KEYWORDS - { args(b -> {}), "--f=10", hasEntry("f", list("10")) }, - { args(b -> {}), "--f=10 --f=11", hasEntry("f", list("10", "11")) }, - { args(b -> {}), "--f 10 --f=11 --f 12", hasEntry("f", list("10", "11", "12")) }, - { args(b -> b.keyword("test")), "--test=10 --test 11 --test=12", hasEntry("test", list("10", "11", "12")) }, - { args(b -> b.keyword("test", MagicsArgs.KeywordSpec.REPLACE)), + hasEntry("verbose", list("true")) + ) + ); + } + + @ParameterizedTest(name = "{index}: \"{0}\" with \"{1}\"") + @MethodSource + public void keywords(MagicsArgs schema, String args, Matcher>> matcher) { + List rawArgs = MagicParserTest.split(args); + Map> actual = Assertions.assertDoesNotThrow(() -> schema.parse(rawArgs)); + + assertThat(actual, matcher); + } + + public static Stream keywords() { + return Stream.of( + Arguments.of( + args(b -> {}), + "--f=10", + hasEntry("f", list("10")) + ), + Arguments.of( + args(b -> {}), + "--f=10 --f=11", + hasEntry("f", list("10", "11")) + ), + Arguments.of( + args(b -> {}), + "--f 10 --f=11 --f 12", + hasEntry("f", list("10", "11", "12")) + ), + Arguments.of( + args(b -> b.keyword("test")), + "--test=10 --test 11 --test=12", + hasEntry("test", list("10", "11", "12")) + ), + Arguments.of( + args(b -> b.keyword("test", MagicsArgs.KeywordSpec.REPLACE)), "--test=10 --test 11 --test=12", - hasEntry("test", list("12")) }, - { args(b -> b.keyword("test")), "", hasEntry("test", list()) }, + hasEntry("test", list("12")) + ), + Arguments.of( + args(b -> b.keyword("test")), + "", + hasEntry("test", list()) + ) + ); + } + + @ParameterizedTest(name = "{index}: \"{0}\" with \"{1}\"") + @MethodSource + public void flagsAndKeyWords(MagicsArgs schema, String args, Matcher>> matcher) { + List rawArgs = MagicParserTest.split(args); + Map> actual = Assertions.assertDoesNotThrow(() -> schema.parse(rawArgs)); + + assertThat(actual, matcher); + } - // FLAGS and KEYWORDS - { args(b -> b.flag("log-level", 'v', "100").keyword("log-level")), + public static Stream flagsAndKeyWords() { + return Stream.of( + Arguments.of( + args(b -> b.flag("log-level", 'v', "100").keyword("log-level")), "-v --log-level=200 --log-level 300", - hasEntry("log-level", list("100", "200", "300")) }, + hasEntry("log-level", list("100", "200", "300")) + ) + ); + } + + @ParameterizedTest(name = "{index}: \"{0}\" with \"{1}\"") + @MethodSource + public void positionalsAndFlagsAndKeywords(MagicsArgs schema, String args, + Matcher>> matcher) { + List rawArgs = MagicParserTest.split(args); + Map> actual = Assertions.assertDoesNotThrow(() -> schema.parse(rawArgs)); + + assertThat(actual, matcher); + } - // POSITIONALS and FLAGS and KEYWORDS - { args(b -> b.required("a").optional("b").flag("log-level", 'v', "100").keyword("log-level")), + public static Stream positionalsAndFlagsAndKeywords() { + return Stream.of( + Arguments.of( + args(b -> b.required("a") + .optional("b") + .flag("log-level", 'v', "100") + .keyword("log-level")), "-v value-a --log-level=200 value-b --log-level 300", allOf( hasEntry("log-level", list("100", "200", "300")), hasEntry("a", list("value-a")), hasEntry("b", list("value-b")) - ) }, - - // Exceptions - { args(b -> b.required("a")), "", null }, - { args(b -> b.required("a")), "value-a extra-a", null }, - { args(b -> b.optional("a")), "value-a extra-a", null }, - { args(b -> b.onlyKnownKeywords()), "--unknown=val", null }, - { args(b -> b.onlyKnownKeywords()), "--unknown val", null }, - { args(b -> b.onlyKnownFlags()), "-idk", null }, - { args(b -> b.flag("test", 'i').onlyKnownFlags()), "-idk", null }, - { args(b -> b.keyword("a", MagicsArgs.KeywordSpec.ONCE)), "--a a --a not-ok...", null }, - - // Strange - { args(b -> b.keyword("a")), + )) + ); + } + + @ParameterizedTest(name = "{index}: \"{0}\" with \"{1}\"") + @MethodSource + public void strange(MagicsArgs schema, String args, Matcher>> matcher) { + List rawArgs = MagicParserTest.split(args); + Map> actual = Assertions.assertDoesNotThrow(() -> schema.parse(rawArgs)); + + assertThat(actual, matcher); + } + + public static Stream strange() { + return Stream.of( + Arguments.of( + args(b -> b.keyword("a")), "\"--a=value with spaces\"", - hasEntry("a", list("value with spaces")) }, - { args(b -> b.keyword("a")), + hasEntry("a", list("value with spaces")) + ), + Arguments.of( + args(b -> b.keyword("a")), "--a=\"value with spaces\"", - hasEntry("a", list("value with spaces")) }, - { args(b -> b.keyword("a")), + hasEntry("a", list("value with spaces")) + ), + Arguments.of( + args(b -> b.keyword("a")), "--a \"value with spaces\"", - hasEntry("a", list("value with spaces")) }, - }); + hasEntry("a", list("value with spaces")) + ) + ); } - @Rule - public final ExpectedException exception = ExpectedException.none(); + @ParameterizedTest(name = "{index}: \"{0}\" with \"{1}\"") + @MethodSource + public void any_throws(MagicsArgs schema, String args) { + List rawArgs = MagicParserTest.split(args); - private MagicsArgs schema; - private String args; - private Matcher>> test; - - public MagicsArgsTest(MagicsArgs schema, String args, Matcher>> test) { - this.schema = schema; - this.args = args; - this.test = test; + Assertions.assertThrows(MagicArgsParseException.class, () -> schema.parse(rawArgs)); } - @Test - public void test() { - List rawArgs = MagicParserTest.split(this.args); - if (this.test == null) - exception.expect(MagicArgsParseException.class); + public static Stream any_throws() { + return Stream.of( + Arguments.of( + args(b -> b.required("a")), + "" + ), + Arguments.of( + args(b -> b.required("a")), + "value-a extra-a" + ), + Arguments.of( + args(b -> b.optional("a")), + "value-a extra-a" + ), + Arguments.of( + args(b -> b.onlyKnownKeywords()), + "--unknown=val" + ), + Arguments.of( + args(b -> b.onlyKnownKeywords()), + "--unknown val" + ), + Arguments.of( + args(b -> b.onlyKnownFlags()), + "-idk" + ), + Arguments.of( + args(b -> b.flag("test", 'i').onlyKnownFlags()), + "-idk" + ), + Arguments.of( + args(b -> b.keyword("a", MagicsArgs.KeywordSpec.ONCE)), + "--a a --a not-ok..." + ) + ); + } - Map> args = this.schema.parse(rawArgs); + private static MagicsArgs args(Consumer config) { + MagicsArgs.MagicsArgsBuilder builder = MagicsArgs.builder(); + config.accept(builder); + return builder.build(); + } - if (this.test != null) - assertThat(args, this.test); + private static List list(String... args) { + return Arrays.asList(args); } -} \ No newline at end of file +} diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsTest.java index c8f8d10..b2e54c4 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/magic/registry/MagicsTest.java @@ -1,7 +1,7 @@ package org.dflib.jjava.jupyter.kernel.magic.registry; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; @@ -9,13 +9,14 @@ import java.util.List; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; public class MagicsTest { private Magics magics; - @Before + @BeforeEach public void setUp() { magics = new Magics(); } @@ -116,7 +117,7 @@ public int returnInt(List args) { } } - @Test(expected = IllegalArgumentException.class) + @Test public void badReflectionLineMagicsType() { class BadMagic { @LineMagic @@ -124,10 +125,10 @@ public void set(Set args) { } } - magics.registerMagics(new BadMagic()); + assertThrows(IllegalArgumentException.class, () -> magics.registerMagics(new BadMagic())); } - @Test(expected = IllegalArgumentException.class) + @Test public void badReflectionLineMagicsTypeParam() { class BadMagic { @LineMagic @@ -135,7 +136,7 @@ public void intList(List args) { } } - magics.registerMagics(new BadMagic()); + assertThrows(IllegalArgumentException.class, () -> magics.registerMagics(new BadMagic())); } @Test @@ -176,7 +177,7 @@ public int returnInt(List args, String body) { } } - @Test(expected = IllegalArgumentException.class) + @Test public void badReflectionCellMagicsType() { class BadMagic { @LineMagic @@ -184,10 +185,10 @@ public void set(Set args, String body) { } } - magics.registerMagics(new BadMagic()); + assertThrows(IllegalArgumentException.class, () -> magics.registerMagics(new BadMagic())); } - @Test(expected = IllegalArgumentException.class) + @Test public void badReflectionCellMagicsTypeParam() { class BadMagic { @LineMagic @@ -195,10 +196,10 @@ public void intList(List args, String body) { } } - magics.registerMagics(new BadMagic()); + assertThrows(IllegalArgumentException.class, () -> magics.registerMagics(new BadMagic())); } - @Test(expected = IllegalArgumentException.class) + @Test public void badReflectionCellMagicsBodyTypeParam() { class BadMagic { @LineMagic @@ -206,7 +207,7 @@ public void body(List args, Character body) { } } - magics.registerMagics(new BadMagic()); + assertThrows(IllegalArgumentException.class, () -> magics.registerMagics(new BadMagic())); } @Test @@ -280,4 +281,4 @@ public void staticMagic() throws Exception { assertEquals((Integer) 0, magics.applyLineMagic("staticMagic", Collections.emptyList())); assertEquals("body", magics.applyCellMagic("staticMagic", Collections.emptyList(), "body")); } -} \ No newline at end of file +} diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/GlobFinderTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/GlobFinderTest.java index 1e43bf6..e54b9db 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/GlobFinderTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/GlobFinderTest.java @@ -3,18 +3,15 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -24,9 +21,8 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@RunWith(Parameterized.class) public class GlobFinderTest { private static final Configuration WIN_FS = Configuration.windows().toBuilder().setWorkingDirectory("C:/dir-a").build(); private static final Configuration UNIX_FS = Configuration.unix().toBuilder().setWorkingDirectory("/dir-a").build(); @@ -35,6 +31,333 @@ public class GlobFinderTest { private static final Set TEST_FILES_ALL = setOf("a.txt", "b.txt", "c.txt", "a.pdf", "b.c.pdf", "abc.svg"); private static final Set TEST_DIRS_ALL = setOf("dir-a", "dir-b", "dir.c"); + @ParameterizedTest(name = "{index} :: {1}") + @MethodSource("data") + public void test(Configuration fsConfig, String glob, Set files, Set paths) throws Exception { + FileSystem fs = Jimfs.newFileSystem(fsConfig); + List roots = StreamSupport.stream(fs.getRootDirectories().spliterator(), false) + .collect(Collectors.toList()); + roots.add(fs.getPath(".")); + + for (Path dir1 : roots) { + for (String dir2 : new String[]{".", "dir-a", "dir-b", "dir.c"}) { + for (String dir3 : new String[]{".", "dir-a", "dir-b", "dir.c"}) { + for (String dir4 : new String[]{".", "dir-a", "dir-b", "dir.c"}) { + Files.createDirectories(fs.getPath(dir1.toString(), dir2, dir3, dir4)); + + for (String file : TEST_FILES_ALL) { + try { + Files.createFile(fs.getPath(dir1.toString(), dir2, dir3, dir4, file)); + } catch (FileAlreadyExistsException ignore) { + } + } + } + } + } + } + + Set expected; + Set actual; + GlobFinder finder = new GlobFinder(fs, glob); + + expected = files.stream() + .map(fs::getPath) + .map(Path::normalize) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + actual = StreamSupport.stream(finder.computeMatchingFiles().spliterator(), false) + .map(Path::normalize) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + assertEquals(expected, actual, String.format("Glob files: '%s'", glob)); + + expected = paths.stream() + .map(fs::getPath) + .map(Path::normalize) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + actual = StreamSupport.stream(finder.computeMatchingPaths().spliterator(), false) + .map(Path::normalize) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + assertEquals(expected, actual, String.format("Glob paths: '%s'", glob)); + } + + public static Stream data() { + return Stream.of( + Arguments.of( + WIN_FS, + "C:/*", + allFilesMapped(s -> "C:/" + s), + allFilesAndDirsMapped(s -> "C:/" + s) + ), + Arguments.of( + UNIX_FS, + "/*", + allFilesMapped(s -> "/" + s), + allFilesAndDirsMapped(s -> "/" + s) + ), + Arguments.of( + OSX_FS, "/*", + allFilesMapped(s -> "/" + s), + allFilesAndDirsMapped(s -> "/" + s) + ), + + // Implicit * appended with trailing / in file mode but not path mode. + Arguments.of( + WIN_FS, + "C:/*/", + allDirsFlatMapped(d -> allFilesMapped(s -> "C:/" + d + "/" + s)), + allFilesAndDirsMapped(s -> "C:/" + s) + ), + Arguments.of( + UNIX_FS, + "/*/", + allDirsFlatMapped(d -> allFilesMapped(s -> "/" + d + "/" + s)), + allFilesAndDirsMapped(s -> "/" + s)), + Arguments.of( + OSX_FS, + "/*/", + allDirsFlatMapped(d -> allFilesMapped(s -> "/" + d + "/" + s)), + allFilesAndDirsMapped(s -> "/" + s) + ), + + Arguments.of( + WIN_FS, + "C:/c.txt", + setOf("C:/c.txt"), + setOf("C:/c.txt") + ), + Arguments.of( + UNIX_FS, + "/c.txt", + setOf("/c.txt"), + setOf("/c.txt") + ), + Arguments.of( + OSX_FS, + "/c.txt", + setOf("/c.txt"), + setOf("/c.txt") + ), + + Arguments.of( + WIN_FS, + "C:/*/c.txt", + allDirsMapped(d -> "C:/" + d + "/c.txt"), + allDirsMapped(d -> "C:/" + d + "/c.txt") + ), + Arguments.of( + UNIX_FS, + "/*/c.txt", + allDirsMapped(d -> "/" + d + "/c.txt"), + allDirsMapped(d -> "/" + d + "/c.txt") + ), + Arguments.of( + OSX_FS, + "/*/c.txt", + allDirsMapped(d -> "/" + d + "/c.txt"), + allDirsMapped(d -> "/" + d + "/c.txt") + ), + + Arguments.of( + WIN_FS, + "C:/dir-b/*.txt", + allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "C:/dir-b/" + f), + allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "C:/dir-b/" + f) + ), + Arguments.of( + UNIX_FS, + "/dir-b/*.txt", + allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f), + allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f) + ), + Arguments.of( + OSX_FS, + "/dir-b/*.txt", + allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f), + allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f) + ), + + Arguments.of( + WIN_FS, + "*.pdf", + allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f), + allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f) + ), + Arguments.of( + UNIX_FS, + "*.pdf", + allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f), + allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f) + ), + Arguments.of( + OSX_FS, + "*.pdf", + allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f), + allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f) + ), + + Arguments.of( + WIN_FS, + "*/dir.c/*.svg", + allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)), + allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)) + ), + Arguments.of( + UNIX_FS, + "*/dir.c/*.svg", + allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)), + allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)) + ), + Arguments.of( + OSX_FS, + "*/dir.c/*.svg", + allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)), + allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)) + ), + + Arguments.of( + WIN_FS, + "?.pdf", + setOf("C:/dir-a/a.pdf"), + setOf("C:/dir-a/a.pdf") + ), + Arguments.of( + UNIX_FS, + "?.pdf", + setOf("/dir-a/a.pdf"), + setOf("/dir-a/a.pdf") + ), + Arguments.of( + OSX_FS, + "?.pdf", + setOf("/dir-a/a.pdf"), + setOf("/dir-a/a.pdf") + ), + + Arguments.of( + WIN_FS, + "C:/dir-?/?.pdf", + setOf("C:/dir-a/a.pdf", "C:/dir-b/a.pdf"), + setOf("C:/dir-a/a.pdf", "C:/dir-b/a.pdf") + ), + Arguments.of( + UNIX_FS, + "/dir-?/?.pdf", + setOf("/dir-a/a.pdf", "/dir-b/a.pdf"), + setOf("/dir-a/a.pdf", "/dir-b/a.pdf") + ), + Arguments.of( + OSX_FS, + "/dir-?/?.pdf", + setOf("/dir-a/a.pdf", "/dir-b/a.pdf"), + setOf("/dir-a/a.pdf", "/dir-b/a.pdf") + ), + + Arguments.of( + WIN_FS, + "C:/dir.c/abc.svg", + setOf("C:/dir.c/abc.svg"), + setOf("C:/dir.c/abc.svg") + ), + Arguments.of( + UNIX_FS, + "/dir.c/abc.svg", + setOf("/dir.c/abc.svg"), + setOf("/dir.c/abc.svg") + ), + Arguments.of( + OSX_FS, + "/dir.c/abc.svg", + setOf("/dir.c/abc.svg"), + setOf("/dir.c/abc.svg") + ), + + Arguments.of( + WIN_FS, + "C:/bad", + Collections.emptySet(), + Collections.emptySet() + ), + Arguments.of( + UNIX_FS, + "/bad", + Collections.emptySet(), + Collections.emptySet() + ), + Arguments.of( + OSX_FS, + "/bad", + Collections.emptySet(), + Collections.emptySet() + ), + + Arguments.of( + WIN_FS, + "C:/*/*/*/*/*/*/*", + Collections.emptySet(), + Collections.emptySet() + ), + Arguments.of( + UNIX_FS, + "/*/*/*/*/*/*/*", + Collections.emptySet(), + Collections.emptySet() + ), + Arguments.of( + OSX_FS, + "/*/*/*/*/*/*/*", + Collections.emptySet(), + Collections.emptySet() + ), + + Arguments.of( + WIN_FS, + "C:/dir-?/", + allDirsFlatMapped(d -> !d.startsWith("dir-") + ? Collections.emptySet() + : allFilesMapped(f -> "C:/" + d + "/" + f)), + setOf("C:/dir-a", "C:/dir-b") + ), + Arguments.of( + UNIX_FS, + "/dir-?/", + allDirsFlatMapped(d -> !d.startsWith("dir-") + ? Collections.emptySet() + : allFilesMapped(f -> "/" + d + "/" + f)), + setOf("/dir-a", "/dir-b") + ), + Arguments.of( + OSX_FS, + "/dir-?/", + allDirsFlatMapped(d -> !d.startsWith("dir-") + ? Collections.emptySet() + : allFilesMapped(f -> "/" + d + "/" + f)), + setOf("/dir-a", "/dir-b") + ), + + Arguments.of( + WIN_FS, + "C:/*c*", + allFilesFilterMapped(f -> f.contains("c"), f -> "C:/" + f), + allFilesAndDirsFilterMapped(f -> f.contains("c"), f -> "C:/" + f) + ), + Arguments.of( + UNIX_FS, + "/*c*", + allFilesFilterMapped(f -> f.contains("c"), f -> "/" + f), + allFilesAndDirsFilterMapped(f -> f.contains("c"), f -> "/" + f) + ), + Arguments.of( + OSX_FS, + "/*c*", + allFilesFilterMapped(f -> f.contains("c"), f -> "/" + f), + allFilesAndDirsFilterMapped(f -> f.contains("c"), f -> "/" + f) + ) + ); + } + private static Set allFilesMapped(Function mapper) { return TEST_FILES_ALL .stream() @@ -81,140 +404,4 @@ private static Set allFilesAndDirsFilterMapped(Predicate filter, private static Set setOf(String... files) { return Arrays.stream(files).collect(Collectors.toSet()); } - - @Parameterized.Parameters(name = "{index} :: {1}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - { WIN_FS, "C:/*", allFilesMapped(s -> "C:/" + s), allFilesAndDirsMapped(s -> "C:/" + s) }, - { UNIX_FS, "/*", allFilesMapped(s -> "/" + s), allFilesAndDirsMapped(s -> "/" + s) }, - { OSX_FS, "/*", allFilesMapped(s -> "/" + s), allFilesAndDirsMapped(s -> "/" + s) }, - - // Implicit * appended with trailing / in file mode but not path mode. - { WIN_FS, "C:/*/", allDirsFlatMapped(d -> allFilesMapped(s -> "C:/" + d + "/" + s)), allFilesAndDirsMapped(s -> "C:/" + s) }, - { UNIX_FS, "/*/", allDirsFlatMapped(d -> allFilesMapped(s -> "/" + d + "/" + s)), allFilesAndDirsMapped(s -> "/" + s) }, - { OSX_FS, "/*/", allDirsFlatMapped(d -> allFilesMapped(s -> "/" + d + "/" + s)), allFilesAndDirsMapped(s -> "/" + s) }, - - { WIN_FS, "C:/c.txt", setOf("C:/c.txt"), setOf("C:/c.txt") }, - { UNIX_FS, "/c.txt", setOf("/c.txt"), setOf("/c.txt") }, - { OSX_FS, "/c.txt", setOf("/c.txt"), setOf("/c.txt") }, - - { WIN_FS, "C:/*/c.txt", allDirsMapped(d -> "C:/" + d + "/c.txt"), allDirsMapped(d -> "C:/" + d + "/c.txt") }, - { UNIX_FS, "/*/c.txt", allDirsMapped(d -> "/" + d + "/c.txt"), allDirsMapped(d -> "/" + d + "/c.txt") }, - { OSX_FS, "/*/c.txt", allDirsMapped(d -> "/" + d + "/c.txt"), allDirsMapped(d -> "/" + d + "/c.txt") }, - - { WIN_FS, "C:/dir-b/*.txt", allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "C:/dir-b/" + f), allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "C:/dir-b/" + f) }, - { UNIX_FS, "/dir-b/*.txt", allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f), allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f) }, - { OSX_FS, "/dir-b/*.txt", allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f), allFilesFilterMapped(f -> f.endsWith(".txt"), f -> "/dir-b/" + f) }, - - { WIN_FS, "*.pdf", allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f), allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f) }, - { UNIX_FS, "*.pdf", allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f), allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f) }, - { OSX_FS, "*.pdf", allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f), allFilesFilterMapped(f -> f.endsWith(".pdf"), f -> "./" + f) }, - - { WIN_FS, "*/dir.c/*.svg", allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)), allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)) }, - { UNIX_FS, "*/dir.c/*.svg", allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)), allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)) }, - { OSX_FS, "*/dir.c/*.svg", allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)), allDirsFlatMapped(d -> allFilesFilterMapped(f -> f.endsWith(".svg"), f -> "./" + d + "/dir.c/" + f)) }, - - { WIN_FS, "?.pdf", setOf("C:/dir-a/a.pdf"), setOf("C:/dir-a/a.pdf") }, - { UNIX_FS, "?.pdf", setOf("/dir-a/a.pdf"), setOf("/dir-a/a.pdf") }, - { OSX_FS, "?.pdf", setOf("/dir-a/a.pdf"), setOf("/dir-a/a.pdf") }, - - { WIN_FS, "C:/dir-?/?.pdf", setOf("C:/dir-a/a.pdf", "C:/dir-b/a.pdf"), setOf("C:/dir-a/a.pdf", "C:/dir-b/a.pdf") }, - { UNIX_FS, "/dir-?/?.pdf", setOf("/dir-a/a.pdf", "/dir-b/a.pdf"), setOf("/dir-a/a.pdf", "/dir-b/a.pdf") }, - { OSX_FS, "/dir-?/?.pdf", setOf("/dir-a/a.pdf", "/dir-b/a.pdf"), setOf("/dir-a/a.pdf", "/dir-b/a.pdf") }, - - { WIN_FS, "C:/dir.c/abc.svg", setOf("C:/dir.c/abc.svg"), setOf("C:/dir.c/abc.svg") }, - { UNIX_FS, "/dir.c/abc.svg", setOf("/dir.c/abc.svg"), setOf("/dir.c/abc.svg") }, - { OSX_FS, "/dir.c/abc.svg", setOf("/dir.c/abc.svg"), setOf("/dir.c/abc.svg") }, - - { WIN_FS, "C:/bad", Collections.emptySet(), Collections.emptySet() }, - { UNIX_FS, "/bad", Collections.emptySet(), Collections.emptySet() }, - { OSX_FS, "/bad", Collections.emptySet(), Collections.emptySet() }, - - { WIN_FS, "C:/*/*/*/*/*/*/*", Collections.emptySet(), Collections.emptySet() }, - { UNIX_FS, "/*/*/*/*/*/*/*", Collections.emptySet(), Collections.emptySet() }, - { OSX_FS, "/*/*/*/*/*/*/*", Collections.emptySet(), Collections.emptySet() }, - - { WIN_FS, "C:/dir-?/", allDirsFlatMapped(d -> !d.startsWith("dir-") ? Collections.emptySet() : allFilesMapped(f -> "C:/" + d + "/" + f)), setOf("C:/dir-a", "C:/dir-b") }, - { UNIX_FS, "/dir-?/", allDirsFlatMapped(d -> !d.startsWith("dir-") ? Collections.emptySet() : allFilesMapped(f -> "/" + d + "/" + f)), setOf("/dir-a", "/dir-b") }, - { OSX_FS, "/dir-?/", allDirsFlatMapped(d -> !d.startsWith("dir-") ? Collections.emptySet() : allFilesMapped(f -> "/" + d + "/" + f)), setOf("/dir-a", "/dir-b") }, - - { WIN_FS, "C:/*c*", allFilesFilterMapped(f -> f.contains("c"), f -> "C:/" + f), allFilesAndDirsFilterMapped(f -> f.contains("c"), f -> "C:/" + f) }, - { UNIX_FS, "/*c*", allFilesFilterMapped(f -> f.contains("c"), f -> "/" + f), allFilesAndDirsFilterMapped(f -> f.contains("c"), f -> "/" + f) }, - { OSX_FS, "/*c*", allFilesFilterMapped(f -> f.contains("c"), f -> "/" + f), allFilesAndDirsFilterMapped(f -> f.contains("c"), f -> "/" + f) }, - }); - } - - private final Configuration fsConfig; - private final String glob; - private final Set files; - private final Set paths; - - private FileSystem fs; - - public GlobFinderTest(Configuration fsConfig, String glob, Set files, Set paths) { - this.fsConfig = fsConfig; - this.glob = glob; - this.files = files; - this.paths = paths; - } - - @Before - public void setUp() throws Exception { - this.fs = Jimfs.newFileSystem(this.fsConfig); - - List roots = StreamSupport.stream(this.fs.getRootDirectories().spliterator(), false).collect(Collectors.toList()); - roots.add(this.fs.getPath(".")); - - for (Path dir1 : roots) { - for (String dir2 : new String[]{ ".", "dir-a", "dir-b", "dir.c" }) { - for (String dir3 : new String[]{ ".", "dir-a", "dir-b", "dir.c" }) { - for (String dir4 : new String[]{ ".", "dir-a", "dir-b", "dir.c" }) { - Files.createDirectories(this.fs.getPath(dir1.toString(), dir2, dir3, dir4)); - - for (String file : TEST_FILES_ALL) { - try { - Files.createFile(this.fs.getPath(dir1.toString(), dir2, dir3, dir4, file)); - } catch (FileAlreadyExistsException ignore) {} - } - } - } - } - } - } - - @After - public void tearDown() throws Exception { - this.fs = null; - } - - @Test - public void test() throws Exception { - GlobFinder finder = new GlobFinder(this.fs, this.glob); - - assertEquals( - String.format("Glob files: '%s'", this.glob), - this.files.stream() - .map(this.fs::getPath) - .map(Path::normalize) - .map(Path::toAbsolutePath) - .collect(Collectors.toSet()), - StreamSupport.stream(finder.computeMatchingFiles().spliterator(), false) - .map(Path::normalize) - .map(Path::toAbsolutePath) - .collect(Collectors.toSet()) - ); - - assertEquals( - String.format("Glob paths: '%s'", this.glob), - this.paths.stream() - .map(this.fs::getPath) - .map(Path::normalize) - .map(Path::toAbsolutePath) - .collect(Collectors.toSet()), - StreamSupport.stream(finder.computeMatchingPaths().spliterator(), false) - .map(Path::normalize) - .map(Path::toAbsolutePath) - .collect(Collectors.toSet()) - ); - } -} \ No newline at end of file +} diff --git a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/InheritanceIteratorTest.java b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/InheritanceIteratorTest.java index 08eeec8..46e2bfc 100644 --- a/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/InheritanceIteratorTest.java +++ b/jupyter-jvm-basekernel/src/test/java/org/dflib/jjava/jupyter/kernel/util/InheritanceIteratorTest.java @@ -1,87 +1,121 @@ package org.dflib.jjava.jupyter.kernel.util; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@RunWith(Parameterized.class) public class InheritanceIteratorTest { - interface I {} - interface J extends I {} + @ParameterizedTest + @MethodSource("data") + public void test(Class root, List> expected) { + List> actual = collectIteration(root); - interface K extends J, I {} - - interface L extends J, K {} + assertEquals(expected, actual); + } - class A {} + public static Stream data() { + return Stream.of( + Arguments.of( + A.class, + List.of(A.class, Object.class) + ), + Arguments.of( + B.class, + List.of(B.class, A.class, Object.class) + ), + Arguments.of( + C.class, + List.of(C.class, B.class, A.class, Object.class) + ), + Arguments.of( + int.class, + List.of(int.class) + ), + Arguments.of( + D.class, + List.of(D.class, Object.class) + ), + Arguments.of( + E.class, + List.of(E.class, L.class, J.class, K.class, I.class, D.class, Object.class) + ), + Arguments.of( + F.class, + List.of(F.class, J.class, K.class, I.class, E.class, L.class, D.class, Object.class) + ), + Arguments.of( + G.class, + List.of(G.class, N.class, O.class, Object.class) + ), + Arguments.of( + H.class, + List.of(H.class, Q.class, P.class, M.class, N.class, Object.class) + ) + ); + } - class B extends A {} + private List> collectIteration(Class root) { + List> data = new LinkedList<>(); + InheritanceIterator it = new InheritanceIterator(root); + while (it.hasNext()) data.add(it.next()); + return data; + } - class C extends B {} + interface I { + } - class D {} + interface J extends I { + } - class E extends D implements L {} + interface K extends J, I { + } - class F extends E implements J, K {} + interface L extends J, K { + } - interface M {} + class A { + } - interface N {} + class B extends A { + } - interface O extends N {} + class C extends B { + } - interface P extends N, M {} + class D { + } - interface Q extends P, M {} + class E extends D implements L { + } - class G implements N, O {} + class F extends E implements J, K { + } - class H implements Q {} + interface M { + } - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList(new Object[][]{ - { A.class, Arrays.asList(A.class, Object.class) }, - { B.class, Arrays.asList(B.class, A.class, Object.class) }, - { C.class, Arrays.asList(C.class, B.class, A.class, Object.class) }, - { int.class, Collections.singletonList(int.class) }, - { D.class, Arrays.asList(D.class, Object.class) }, - { E.class, Arrays.asList(E.class, L.class, J.class, K.class, I.class, D.class, Object.class) }, - { F.class, Arrays.asList(F.class, J.class, K.class, I.class, E.class, L.class, D.class, Object.class) }, - { G.class, Arrays.asList(G.class, N.class, O.class, Object.class) }, - { H.class, Arrays.asList(H.class, Q.class, P.class, M.class, N.class, Object.class) }, - }); + interface N { } - private final Class root; - private final List expectedOrder; + interface O extends N { + } - public InheritanceIteratorTest(Class root, List expectedOrder) { - this.root = root; - this.expectedOrder = expectedOrder; + interface P extends N, M { } - private List collectIteration(Class root) { - List data = new LinkedList<>(); - InheritanceIterator it = new InheritanceIterator(root); - while (it.hasNext()) data.add(it.next()); - return data; + interface Q extends P, M { } - @Test - public void test() { - List actual = collectIteration(this.root); + class G implements N, O { + } - assertEquals(this.expectedOrder, actual); + class H implements Q { } -} \ No newline at end of file +} diff --git a/pom.xml b/pom.xml index cb5804f..9b4e057 100644 --- a/pom.xml +++ b/pom.xml @@ -44,15 +44,16 @@ UTF-8 11 - 2.3.0 2.5.2 3.6.0 2.11.0 0.6.0 - 4.13.1 + 5.11.2 1.3 1.3.0 + 1.20.2 + 1.7.36 https://github.com/dflib/jjava @@ -101,10 +102,14 @@ ${jeromq.version} - - junit - junit - ${junit4.version} + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + + + org.junit.jupiter + junit-jupiter-params + ${junit5.version} org.hamcrest @@ -116,6 +121,16 @@ jimfs ${jimfs.version} + + org.testcontainers + testcontainers + ${testcontainers.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} +