Skip to content

Commit b47eac0

Browse files
Provide env IT cases
1 parent 6ceefe7 commit b47eac0

File tree

8 files changed

+227
-77
lines changed

8 files changed

+227
-77
lines changed

jjava/src/main/java/org/dflib/jjava/Env.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ public final class Env {
1212
public static final String JJAVA_STARTUP_SCRIPT = "JJAVA_STARTUP_SCRIPT";
1313
public static final String JJAVA_LOAD_EXTENSIONS = "JJAVA_LOAD_EXTENSIONS";
1414

15-
// not used by Java, but rather by the Python kernel boot script
15+
// not used by JJava, but rather by the kernel launcher script
1616
public static final String JJAVA_JVM_OPTS = "JJAVA_JVM_OPTS";
17-
1817
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package org.dflib.jjava.jupyter.kernel;
2+
3+
import org.junit.jupiter.api.BeforeAll;
4+
import org.testcontainers.containers.Container;
5+
import org.testcontainers.containers.ExecConfig;
6+
import org.testcontainers.containers.GenericContainer;
7+
import org.testcontainers.containers.wait.strategy.Wait;
8+
import org.testcontainers.utility.MountableFile;
9+
10+
import java.io.IOException;
11+
import java.time.Duration;
12+
import java.util.ArrayList;
13+
import java.util.Base64;
14+
import java.util.Collections;
15+
import java.util.List;
16+
import java.util.Map;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
20+
public abstract class ContainerizedKernelCase {
21+
22+
protected static final GenericContainer<?> container;
23+
protected static final String WORKING_DIRECTORY = "/test";
24+
protected static final String CONTAINER_KERNELSPEC = "/usr/share/jupyter/kernels/java";
25+
protected static final String CONTAINER_RESOURCES = WORKING_DIRECTORY + "/resources";
26+
protected static final String TEST_CLASSPATH = CONTAINER_RESOURCES + "/classes";
27+
28+
private static final String BASE_IMAGE = String.format("eclipse-temurin:%s", Runtime.version().version().get(0));
29+
private static final String FS_KERNELSPEC = "../kernelspec/java";
30+
private static final String FS_RESOURCES = "src/test/resources";
31+
32+
static {
33+
container = new GenericContainer<>(BASE_IMAGE)
34+
.withWorkingDirectory(WORKING_DIRECTORY)
35+
.withCopyToContainer(MountableFile.forHostPath(FS_KERNELSPEC), CONTAINER_KERNELSPEC)
36+
.withCopyToContainer(MountableFile.forHostPath(FS_RESOURCES), CONTAINER_RESOURCES)
37+
.withCommand("bash", "-c", getStartupCommand())
38+
.waitingFor(Wait.forSuccessfulCommand(getSuccessfulCommand()))
39+
.withStartupTimeout(Duration.ofMinutes(5));
40+
container.start();
41+
}
42+
43+
@BeforeAll
44+
static void compileSources() throws IOException, InterruptedException {
45+
String source = "$(find " + CONTAINER_RESOURCES + "/src -name '*.java')";
46+
Container.ExecResult compileResult = executeInContainer("javac -d " + TEST_CLASSPATH + " " + source);
47+
48+
assertEquals("", compileResult.getStdout());
49+
assertEquals("", compileResult.getStderr());
50+
}
51+
52+
protected static Container.ExecResult executeInContainer(String... commands) throws IOException, InterruptedException {
53+
List<String> wrappedCommands = new ArrayList<>();
54+
wrappedCommands.add("bash");
55+
wrappedCommands.add("-c");
56+
wrappedCommands.addAll(List.of(commands));
57+
return container.execInContainer(wrappedCommands.toArray(new String[]{}));
58+
}
59+
60+
protected static Container.ExecResult executeInKernel(String snippet) throws IOException, InterruptedException {
61+
return executeInKernel(snippet, Collections.emptyMap());
62+
}
63+
64+
protected static Container.ExecResult executeInKernel(String snippet, Map<String, String> env) throws IOException, InterruptedException {
65+
String snippet64 = Base64.getEncoder().encodeToString(snippet.getBytes());
66+
String jupyterCommand = venvCommand("jupyter console --kernel=java --simple-prompt");
67+
String[] containerCommand = new String[]{"bash", "-c", "base64 -d <<< " + snippet64 + " | " + jupyterCommand};
68+
return container.execInContainer(ExecConfig.builder()
69+
.envVars(env)
70+
.command(containerCommand)
71+
.build()
72+
);
73+
}
74+
75+
private static String getStartupCommand() {
76+
return String.join(" && ",
77+
"apt-get update",
78+
"apt-get install --no-install-recommends -y python3 python3-pip python3-venv",
79+
"python3 -m venv ./venv",
80+
venvCommand("pip install jupyter-console --progress-bar off"),
81+
"tail -f /dev/null"
82+
);
83+
}
84+
85+
private static String getSuccessfulCommand() {
86+
return venvCommand("jupyter kernelspec list")
87+
+ " | grep ' java ' && "
88+
+ venvCommand("jupyter console --version");
89+
}
90+
91+
private static String venvCommand(String command) {
92+
return WORKING_DIRECTORY + "/venv/bin/" + command;
93+
}
94+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package org.dflib.jjava.jupyter.kernel;
2+
3+
import org.dflib.jjava.Env;
4+
import org.hamcrest.CoreMatchers;
5+
import org.junit.jupiter.api.Test;
6+
import org.testcontainers.containers.Container;
7+
8+
import java.util.Map;
9+
10+
import static org.hamcrest.CoreMatchers.containsString;
11+
import static org.hamcrest.CoreMatchers.not;
12+
import static org.hamcrest.MatcherAssert.assertThat;
13+
14+
class KernelEnvIT extends ContainerizedKernelCase {
15+
16+
@Test
17+
void compilerOpts() throws Exception {
18+
Map<String, String> env = Map.of(Env.JJAVA_COMPILER_OPTS, "-source 9");
19+
String snippet = "var value = 1";
20+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
21+
22+
assertThat(snippetResult.getStderr(), CoreMatchers.allOf(
23+
containsString("| var value = 1;"),
24+
containsString("'var' is a restricted local variable type")
25+
));
26+
}
27+
28+
@Test
29+
void timeout() throws Exception {
30+
Map<String, String> env = Map.of(Env.JJAVA_TIMEOUT, "3000");
31+
String snippet = "Thread.sleep(5000);";
32+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
33+
34+
assertThat(snippetResult.getStderr(), CoreMatchers.allOf(
35+
containsString("| " + snippet),
36+
containsString("Evaluation timed out after 3000 milliseconds.")
37+
));
38+
}
39+
40+
@Test
41+
void classpath() throws Exception {
42+
Map<String, String> env = Map.of(Env.JJAVA_CLASSPATH, TEST_CLASSPATH);
43+
String snippet = String.join("\n",
44+
"import org.dflib.jjava.Dummy;",
45+
"Dummy.class.getName()"
46+
);
47+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
48+
49+
assertThat(snippetResult.getStderr(), not(containsString("|")));
50+
assertThat(snippetResult.getStdout(), containsString("org.dflib.jjava.Dummy"));
51+
}
52+
53+
@Test
54+
void startUpScriptsPath() throws Exception {
55+
Map<String, String> env = Map.of(Env.JJAVA_STARTUP_SCRIPTS_PATH, CONTAINER_RESOURCES + "/test-init.jshell");
56+
String snippet = "ping()";
57+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
58+
59+
assertThat(snippetResult.getStderr(), not(containsString("|")));
60+
assertThat(snippetResult.getStdout(), containsString("pong!"));
61+
}
62+
63+
@Test
64+
void startUpScript() throws Exception {
65+
Map<String, String> env = Map.of(Env.JJAVA_STARTUP_SCRIPT, "public String ping() { return \"pong!\"; }");
66+
String snippet = "ping()";
67+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
68+
69+
assertThat(snippetResult.getStderr(), not(containsString("|")));
70+
assertThat(snippetResult.getStdout(), containsString("pong!"));
71+
}
72+
73+
@Test
74+
void loadExtensions_Default() throws Exception {
75+
String snippet = "printf(\"Hello, %s!\", \"world\");";
76+
Container.ExecResult snippetResult = executeInKernel(snippet);
77+
78+
assertThat(snippetResult.getStderr(), not(containsString("|")));
79+
assertThat(snippetResult.getStdout(), containsString("Hello, world!"));
80+
}
81+
82+
@Test
83+
void loadExtensions_Disable() throws Exception {
84+
Map<String, String> env = Map.of(Env.JJAVA_LOAD_EXTENSIONS, "0");
85+
String snippet = "printf(\"Hello, %s!\", \"world\");";
86+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
87+
88+
assertThat(snippetResult.getStderr(), CoreMatchers.allOf(
89+
containsString("| " + snippet),
90+
containsString("cannot find symbol")
91+
));
92+
}
93+
94+
@Test
95+
void jvmOpts() throws Exception {
96+
Map<String, String> env = Map.of(Env.JJAVA_JVM_OPTS, "-Xmx300m");
97+
String snippet = "Runtime.getRuntime().maxMemory()";
98+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
99+
100+
assertThat(snippetResult.getStderr(), not(containsString("|")));
101+
assertThat(snippetResult.getStdout(), containsString(String.valueOf(300 * (int) Math.pow(1024, 2))));
102+
}
103+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.dflib.jjava.jupyter.kernel;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.testcontainers.containers.Container;
5+
6+
import static org.hamcrest.CoreMatchers.containsString;
7+
import static org.hamcrest.CoreMatchers.not;
8+
import static org.hamcrest.MatcherAssert.assertThat;
9+
import static org.junit.jupiter.api.Assertions.assertEquals;
10+
11+
class KernelStartupIT extends ContainerizedKernelCase {
12+
13+
@Test
14+
void startUp() throws Exception {
15+
String snippet = "1000d + 1";
16+
Container.ExecResult snippetResult = executeInKernel(snippet);
17+
18+
assertEquals(0, snippetResult.getExitCode(), snippetResult.getStderr());
19+
assertThat(snippetResult.getStderr(), not(containsString("|")));
20+
assertThat(snippetResult.getStdout(), containsString("1001.0"));
21+
}
22+
}

jjava/src/test/java/org/dflib/jjava/jupyter/kernel/testcontainers/KernelIT.java

Lines changed: 0 additions & 57 deletions
This file was deleted.

jjava/src/test/java/org/dflib/jjava/jupyter/kernel/testcontainers/KernelStartupTestIT.java

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.dflib.jjava;
2+
3+
public class Dummy {
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public String ping() {
2+
return "pong!";
3+
}

0 commit comments

Comments
 (0)