-
Notifications
You must be signed in to change notification settings - Fork 1
Testing
JNode utilizes a combination of standard JUnit testing on the host JVM and full-system boot testing using virtual machines to verify functionality.
Testing an operating system is challenging because much of the code cannot run in a standard environment. JNode divides testing into two distinct phases:
- Unit Testing (Host JVM): Fast, automated tests that run on the standard Java JVM during the build process.
- Boot Testing (Target VM): Manual or scripted tests that run the compiled JNode OS inside a virtual machine (QEMU, VirtualBox, VMware) to verify low-level system behavior.
JNode uses JUnit 4.5, along with JMock and Mockito for mocking dependencies.
-
Location: Test files are located in the
src/test/directory of each subproject (e.g.,core/src/test/,fs/src/test/). There are nearly 400 test files across the repository. -
Execution:
- To run all tests across the entire project:
sh build.sh tests - To run tests for a specific subproject:
cd <subproject> && ant test
- To run all tests across the entire project:
Because unit tests run on the host JVM (the JVM you use to build JNode, usually OpenJDK on Linux/Windows), they cannot test:
- Classes that use
@MagicPermissionororg.jnode.vm.Unsafe. - Code that relies on the JNode JIT compilers or specific
VmProcessorbehaviors. - Native hardware interactions.
For these, developers use mocks, or rely on Boot Testing.
Every subproject that has tests ships a build-tests.xml with an all-junit target. all/build.xml's tests target discovers them automatically:
<target name="tests" depends="assemble">
<subant><fileset dir="${root.dir}" includes="**/build-tests.xml"/></subant>
<antcall target="check-plugins"/>
</target>sh build.sh tests # every subproject
sh build.sh -f <subproject>/build-tests.xml all-junit # one subprojectCurrently: core, fs, net, shell, cli, gui. Each uses <junit fork="on" haltonfailure="on"> with <classpath refid="cp-test"/>, compiling via the jnode.compile.test preset (host JDK against the JNode core jar) into <subproject>/build/testclasses and writing JUnit XML into <subproject>/build/reports/junit.
shell is unusual in having two independent host-JVM tiers:
| Tier | Entry point | Target | What it runs |
|---|---|---|---|
| JUnit | org.jnode.test.shell.AllTests |
all-junit |
@Test classes, e.g. DefaultInterpreterSequenceTest, DefaultTokenizerTest, ExecuteJavaPackagePrivateTest
|
| Black-box XML |
TestHarness (shell/src/test/org/jnode/shell/harness/TestHarness.java) |
all-blackbox, bjorne
|
XML testSpec scripts run in AS_SCRIPT/INTERACTIVE modes |
Neither tier runs on JNode itself. TestEmu.initEmu (TestEmu.java:50-77) picks org.jnode.emu.Emu on the host vs ShellUtils.getCurrentShell() on JNode. See Shell-Commands for the harness's temp-directory lifecycle and fixture semantics.
gui/build-tests.xml is the newest build-tests.xml (added 2026-09-24) and the first host-JVM unit-test infrastructure for gui. Everything previously under gui/src/test/org/jnode/test/gui/*.java (JInternalFrameTest, JDPTest, SwingTest, AWTFrameTest, …) is a manual main() demo app in no suite.
gui/src/test/org/jnode/awt/swingpeers/GuiTestSuite.java is a @RunWith(Suite.class) over SwingToolkitClippingTest, which lives in the same package as SwingToolkit so it can call the package-private getWindowPaintRegions. It is headless-safe: the fixture uses a plain JInternalFrame("source") on an 800×600 JDesktopPane, because constructing a real java.awt.Frame (or SwingBaseWindow, whose constructor chain is super(title, true, true, true, true)) requires a display the forked test JVM does not have. That is why getWindowPaintRegions takes a JInternalFrame rather than a SwingBaseWindow.
The four tests cover: null when the window is fully visible, one uncovered band for a single occluder, region collapse for two occluders (the subtract chain 1 → 2 → 1), and the empty-list "paint nothing" contract. The desktop-bounds intersection branch and the SwingWindow/null/z < 0 early returns are not covered. See GUI-AWT and JNode-Graphics2D.
Some VM-side tests are main()-style programs in the guest rather than JUnit classes, in core/src/test/org/jnode/test/bugs/ (same style as bug778001.java):
| Program | Covers |
|---|---|
ConversionTest |
19 assertions on JLS §5.1.3 float/double → int/long for NaN, ±Infinity, ±1.5, ±0.0, 1e20, -1e20, Long.MAX_VALUE/MIN_VALUE
|
RintTest |
strictfp Math.rint ties-to-even and the twoToThe52 trick, which depend on the x87 control word |
StrictMathTest |
In-VM sin/cos/tan, including the large-argument path that used to livelock |
See X87-FPU-Support.
core/src/test/org/jnode/test/util/VersionTest.java went from 41 to 95 @Test methods (282 → 772 lines) with no production change, and is the best template for a well-organised expansion. The 54 new tests are grouped by comment banners:
| Lines | Banner | Tests | Pins down |
|---|---|---|---|
| 289-400 | Expanded assertions: getters | 11 | all four constructors with/without a tag; getMinor() is Math.max(0, minor) so the internal Undefined = -1 sentinel surfaces as 0; tag parsing from "1.2-foo-bar", "1.2.3-foo.bar", "5-foo", "4.3.2.1-rel"
|
| 401-554 | Expanded assertions: compareTo | 19 | lexicographic ordering per field, untagged < tagged, numeric-dominates-tag, reflexive, symmetric, String-vs-int equivalence, "5" < "5.0" because Undefined sorts below a defined value |
| 555-626 | Expanded assertions: toString / parsing | 8 |
"0.0.0.0", "0.0", 10-sample round trip, tag-with-hyphen vs tag-with-dot |
| 627-694 | Expanded assertions: hashCode / equals | 8 | the exact formula 16909060 == new Version(1,2,3,4).hashCode() (i.e. 0x01020304); equals is not overridden, so testEqualsDistinctEqualValue asserts compareTo == 0 and equals == false
|
| 695-772 | Expanded assertions: error cases | 8 |
@Test(expected=IllegalArgumentException.class) for empty tags and >4 parts, @Test(expected=NullPointerException.class) for new Version((String) null), per-field negative-int checks, and isCompatibleViaCompareTo (the idiom plugins use to check a minimum version) |
Boot testing involves actually building the JNode ISO and booting it.
You must build a "lite" bootable CD-ROM image.
-
x86 (32-bit):
sh build.sh cd-x86-lite -
x86_64 (64-bit):
sh build.sh cd-x86_64-lite
This produces all/build/cdroms/jnode-x86-lite.iso.
JNode is regularly tested against standard virtual machines.
The fastest way to test is using QEMU. The project provides a helper script:
./qemu.shThis script automatically configures QEMU with the correct memory settings, network cards, and serial ports expected by JNode, and boots the ISO.
You can also create a new Virtual Machine in VirtualBox or VMware.
- OS Type: Other / Unknown
- Memory: 512MB to 1GB
-
Storage: Mount the generated
.isoas the primary CD-ROM drive.
If JNode crashes during early boot, it will usually trigger an exception handler in kernel.asm or ints.asm, which dumps the CPU state (Registers, Stack Trace) to the VGA text console and the serial port.
- Serial Console: Output is echoed to COM1. In QEMU, this can be redirected to the terminal or a file for easier analysis.
-
Debugger (
kdb): JNode has a very rudimentary kernel debugger built in, but it is primarily used for post-mortem analysis of crashes.
JNode includes a Python-based test suite for the JDWP debugging backend:
Location: tests/jdwp/
cd tests/jdwp
python3 -m pytest -v- JNode VM running with JDWP listener on port 8000
- Network connectivity between host and VM
- Python with pytest installed on host
The test suite includes 52 tests covering:
- VirtualMachine command set (version, classes, capabilities)
- ReferenceType command set (signature, fields, methods)
- ThreadReference command set (status, suspend/resume, stack trace)
- ObjectReference command set (field values, monitor info)
- StackFrame command set (local variables, this object)
- ClassType command set (superclass, instances, values)
- Method invocation via reflection
- Build-System — Details on how the tests integrate into the Ant build pipeline.
- Boot-Sequence — Understanding the boot process helps diagnose boot test failures.
-
GitHub-Actions-Workflows — The
Java CIworkflow that runsbuild.sh,./test.sh all, and the QEMU boot jobs. -
Agent-Issue-Pipeline — The
healjob andpostCiFixOncethat react to a red CI run. -
Shell-Commands —
TestHarnesstemp-directory lifecycle and black-box spec semantics. -
GUI-AWT — The
guiJUnit suite and headless-safety constraints. -
X87-FPU-Support — Guest test programs covering float→int conversion,
rint, andStrictMath.