Skip to content

Commit

Permalink
Provide env IT cases
Browse files Browse the repository at this point in the history
  • Loading branch information
m-dzianishchyts committed Oct 25, 2024
1 parent 6ceefe7 commit b47eac0
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 77 deletions.
3 changes: 1 addition & 2 deletions jjava/src/main/java/org/dflib/jjava/Env.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.dflib.jjava.jupyter.kernel;

import org.junit.jupiter.api.BeforeAll;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.ExecConfig;
import org.testcontainers.containers.GenericContainer;
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 {

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().version().get(0));
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())
.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<String> 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<String, String> 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", "base64 -d <<< " + snippet64 + " | " + jupyterCommand};
return container.execInContainer(ExecConfig.builder()
.envVars(env)
.command(containerCommand)
.build()
);
}

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;
}
}
103 changes: 103 additions & 0 deletions jjava/src/test/java/org/dflib/jjava/jupyter/kernel/KernelEnvIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
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<String, String> 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("'var' is a restricted local variable type")
));
}

@Test
void timeout() throws Exception {
Map<String, String> 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<String, String> 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<String, String> env = Map.of(Env.JJAVA_STARTUP_SCRIPTS_PATH, CONTAINER_RESOURCES + "/test-init.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<String, String> 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<String, String> 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<String, String> 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))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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 KernelStartupIT extends ContainerizedKernelCase {

@Test
void startUp() throws Exception {
String snippet = "1000d + 1";
Container.ExecResult snippetResult = executeInKernel(snippet);

assertEquals(0, snippetResult.getExitCode(), snippetResult.getStderr());
assertThat(snippetResult.getStderr(), not(containsString("|")));
assertThat(snippetResult.getStdout(), containsString("1001.0"));
}
}

This file was deleted.

This file was deleted.

4 changes: 4 additions & 0 deletions jjava/src/test/resources/src/org/dflib/jjava/Dummy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.dflib.jjava;

public class Dummy {
}
3 changes: 3 additions & 0 deletions jjava/src/test/resources/test-init.jshell
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public String ping() {
return "pong!";
}

0 comments on commit b47eac0

Please sign in to comment.