Skip to content

Add JUnit 5 test support for the JavaSE simulator#5032

Merged
shai-almog merged 8 commits into
masterfrom
junit-test-support
May 25, 2026
Merged

Add JUnit 5 test support for the JavaSE simulator#5032
shai-almog merged 8 commits into
masterfrom
junit-test-support

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • App developers can now write standard @Test methods against the Codename One simulator instead of (or alongside) the legacy AbstractTest / DeviceRunner framework. New com.codename1.testing.junit package lives in the JavaSE port -- simulator-only, so tests get full JVM reflection and can use Mockito/AssertJ/etc. that ParparVM cannot run on device.
  • Annotations: @CodenameOneTest (meta @ExtendWith), @RunOnEdt, @SimulatorProperty (+ container @SimulatorProperties), and a visual-config set -- @Theme, @DarkMode, @LargerText, @Orientation, @RTL -- resolved method-level over class-level, applied on the EDT in one batch followed by a single theme refresh.
  • Small JavaSEPort additions to support the visual annotations cleanly without reflection: setSimulatorPortrait, setSimulatorLargerTextScale, and an isPortrait() override that honors an explicit-portrait flag (the canvas-derived inference reads as landscape on any wide host window, breaking the orientation case in tests).
  • junit-jupiter moves from test to provided scope in the javase pom so the support classes compile but the JUnit dependency does not leak onto the simulator's runtime classpath for apps that do not opt in.

Example

@CodenameOneTest
@SimulatorProperty(name = "feature.flag", value = "on")
class GreetingFormTest {

    @Test
    @RunOnEdt
    @LargerText(scale = 1.6f)
    @DarkMode
    void formStillFitsAtAccessibilityScaleInDark() {
        Form f = new GreetingForm();
        f.show();
        assertEquals("Hello", Display.getInstance().getCurrent().getTitle());
    }
}

Test plan

  • mvn -pl javase test -Plocal-dev-javase passes (63 tests total: 48 pre-existing + 15 new in CodenameOneExtensionTest)
  • New tests cover @SimulatorProperty at class/method/container, @RunOnEdt on/off, @Theme (against bundled iOSModernTheme.res), @LargerText at 1.0/1.6, @Orientation both directions, @DarkMode enabled/disabled, @RTL enabled/disabled
  • Run a downstream app with a JUnit test to confirm the dependency-scope story (apps must declare their own junit-jupiter in test scope)
  • Verify behavior on a true headless CI runner -- the sample test is currently gated with @DisabledIfSystemProperty(named="java.awt.headless", matches="true") because JavaSEPort.init(null) constructs a JFrame; a follow-up could thread a headless-friendly init path

Notes

  • Source level on the javase port is 1.7, which is why @SimulatorProperty is not @Repeatable and @SimulatorProperties exists as the explicit container.
  • Display is booted lazily on the first test that uses the extension and stays warm for the JVM. Cross-test state cleanup (e.g. removing a shown form) is left to the user's @AfterEach; the extension never resets state the caller did not ask for.

🤖 Generated with Claude Code

shai-almog and others added 2 commits May 24, 2026 18:56
App developers can now write standard @test methods against the Codename
One simulator instead of (or alongside) the legacy AbstractTest /
DeviceRunner framework. The new com.codename1.testing.junit package lives
in the JavaSE port -- simulator-only, so tests get full JVM reflection
and can use Mockito/AssertJ/etc. that ParparVM cannot run on device.

Annotations:
- @CodenameOneTest -- meta @ExtendWith(CodenameOneExtension.class)
- @RunOnEdt -- dispatch the test body (and lifecycle when class-level)
  through CN.callSerially with a latch so throwables surface on the
  JUnit thread
- @SimulatorProperty (+ @SimulatorProperties container, since the port
  is source 1.7 and predates @repeatable) -- SYSTEM scope before
  Display init, DISPLAY scope after
- @theme, @DarkMode, @LargerText, @orientation, @rtl -- per-test visual
  config applied on the EDT in one batch followed by a single theme
  refresh; method-level overrides class-level

JavaSEPort gains two public, non-persisting setters
(setSimulatorPortrait, setSimulatorLargerTextScale) so the extension
can drive accessibility / orientation without reflection, and an
isPortrait() override that honors an explicit-portrait flag (the
default canvas-derived inference reads as landscape on any wide host
window, which broke the orientation case in tests).

JUnit Jupiter moves from test to provided scope in the javase pom so
the support classes compile but the JUnit dependency does not leak
onto the simulator's runtime classpath for apps that do not opt in.

15 new tests in CodenameOneExtensionTest cover every annotation
end-to-end; full javase suite stays green at 63 tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setup-workspace.sh runs two mvn invocations: the first passes
-Dcodename1.platform=javase (which activates local-dev-javase and pulls
jcef.jar + jfxrt.jar onto the simulator's compile classpath), the
second doesn't. As long as nothing forced javase to recompile in that
second pass, master got away with it -- target/classes from the first
pass already had CEF / JavaFX .class files. The JUnit-support work in
this branch adds new source files under Ports/JavaSE/src/com/codename1/
testing/junit/ which is enough to flip maven-compiler-plugin's
incremental detection into a full rebuild, and the rebuild then fails
because jcef.jar is no longer visible.

Switch the activation to mirror the maven/android compile-android
profile -- fire whenever cn1.binaries is a real directory and the
property is set. The script passes -Dcn1.binaries to both invocations,
so the profile now activates in both passes and the recompile (when it
does happen) has everything it needs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 24, 2026

Compared 19 screenshots: 19 matched.
✅ JavaScript-port screenshot tests passed.

scripts/run-javase-simulator-integration-tests.sh (and the
top-level ant test-javase target used by PR CI) build the JavaSE
port through the NetBeans-style Ports/JavaSE/build.xml. That Ant
build has its own javac.classpath -- it does not see Maven scopes
-- and JUnit Jupiter is not on it.

The new com/codename1/testing/junit support classes are meant to
ship via the codenameone-javase Maven artifact, where junit-jupiter
is a "provided" dep. They are not needed for the simulator's
screenshot integration tests, so just skip them on the Ant side.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 24, 2026

Compared 110 screenshots: 110 matched.

Native Android coverage

  • 📊 Line coverage: 11.84% (6804/57442 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.63% (34159/354705), branch 4.15% (1398/33724), complexity 5.18% (1677/32362), method 9.01% (1364/15142), class 14.49% (304/2098)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 11.84% (6804/57442 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.63% (34159/354705), branch 4.15% (1398/33724), complexity 5.18% (1677/32362), method 9.01% (1364/15142), class 14.49% (304/2098)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 735.000 ms
Base64 CN1 encode 118.000 ms
Base64 encode ratio (CN1/native) 0.161x (83.9% faster)
Base64 native decode 823.000 ms
Base64 CN1 decode 189.000 ms
Base64 decode ratio (CN1/native) 0.230x (77.0% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 24, 2026

Compared 110 screenshots: 110 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 191 seconds

Build and Run Timing

Metric Duration
Simulator Boot 93000 ms
Simulator Boot (Run) 1000 ms
App Install 19000 ms
App Launch 17000 ms
Test Execution 360000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 936.000 ms
Base64 CN1 encode 1541.000 ms
Base64 encode ratio (CN1/native) 1.646x (64.6% slower)
Base64 native decode 481.000 ms
Base64 CN1 decode 1075.000 ms
Base64 decode ratio (CN1/native) 2.235x (123.5% slower)
Base64 SIMD encode 476.000 ms
Base64 encode ratio (SIMD/native) 0.509x (49.1% faster)
Base64 encode ratio (SIMD/CN1) 0.309x (69.1% faster)
Base64 SIMD decode 425.000 ms
Base64 decode ratio (SIMD/native) 0.884x (11.6% faster)
Base64 decode ratio (SIMD/CN1) 0.395x (60.5% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 65.000 ms
Image createMask (SIMD on) 14.000 ms
Image createMask ratio (SIMD on/off) 0.215x (78.5% faster)
Image applyMask (SIMD off) 171.000 ms
Image applyMask (SIMD on) 75.000 ms
Image applyMask ratio (SIMD on/off) 0.439x (56.1% faster)
Image modifyAlpha (SIMD off) 131.000 ms
Image modifyAlpha (SIMD on) 72.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.550x (45.0% faster)
Image modifyAlpha removeColor (SIMD off) 175.000 ms
Image modifyAlpha removeColor (SIMD on) 103.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.589x (41.1% faster)
Image PNG encode (SIMD off) 1145.000 ms
Image PNG encode (SIMD on) 1032.000 ms
Image PNG encode ratio (SIMD on/off) 0.901x (9.9% faster)
Image JPEG encode 646.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 24, 2026

Compared 11 screenshots: 11 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 24, 2026

Compared 110 screenshots: 110 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 176 seconds

Build and Run Timing

Metric Duration
Simulator Boot 63000 ms
Simulator Boot (Run) 0 ms
App Install 12000 ms
App Launch 9000 ms
Test Execution 252000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 432.000 ms
Base64 CN1 encode 1273.000 ms
Base64 encode ratio (CN1/native) 2.947x (194.7% slower)
Base64 native decode 235.000 ms
Base64 CN1 decode 865.000 ms
Base64 decode ratio (CN1/native) 3.681x (268.1% slower)
Base64 SIMD encode 376.000 ms
Base64 encode ratio (SIMD/native) 0.870x (13.0% faster)
Base64 encode ratio (SIMD/CN1) 0.295x (70.5% faster)
Base64 SIMD decode 385.000 ms
Base64 decode ratio (SIMD/native) 1.638x (63.8% slower)
Base64 decode ratio (SIMD/CN1) 0.445x (55.5% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 53.000 ms
Image createMask (SIMD on) 9.000 ms
Image createMask ratio (SIMD on/off) 0.170x (83.0% faster)
Image applyMask (SIMD off) 109.000 ms
Image applyMask (SIMD on) 47.000 ms
Image applyMask ratio (SIMD on/off) 0.431x (56.9% faster)
Image modifyAlpha (SIMD off) 111.000 ms
Image modifyAlpha (SIMD on) 56.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.505x (49.5% faster)
Image modifyAlpha removeColor (SIMD off) 139.000 ms
Image modifyAlpha removeColor (SIMD on) 62.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.446x (55.4% faster)
Image PNG encode (SIMD off) 934.000 ms
Image PNG encode (SIMD on) 1153.000 ms
Image PNG encode ratio (SIMD on/off) 1.234x (23.4% slower)
Image JPEG encode 560.000 ms

The PR CI's build-test matrix runs Surefire inside a Linux container
with no X11 display. AWT's GraphicsEnvironment auto-detects headless
mode but never sets the java.awt.headless system property, so the
@DisabledIfSystemProperty guard on CodenameOneExtensionTest didn't
fire. The extension then tried Display.init(null), which goes through
JavaSEPort.init -> new JFrame() and threw HeadlessException inside
@BeforeAll. JUnit marked the class as errored, and worse the Display
singleton was left half-initialized, hanging the next test class
(SimulatorHookLoaderTest) until the CI 6-hour timeout fired.

Fix: have CodenameOneExtension.beforeAll check
GraphicsEnvironment.isHeadless() first and throw TestAbortedException
with an explanatory message. JUnit reports that as "skipped" instead
of "failed", and crucially the JVM never enters Display.init so the
shared singleton stays clean for any later tests. Verified locally:
mvn test -DargLine="-Djava.awt.headless=true" reports
"Tests run: 15, Skipped: 15" instead of erroring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 24, 2026

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

End-user-facing changes to make the new com.codename1.testing.junit
API usable out of the box:

* maven/cn1app-archetype/.../common/pom.xml: add codenameone-javase
  and junit-jupiter at test scope so common can compile JUnit tests
  that live in common/src/test/java. Surefire stays skipped here to
  avoid running each JUnit test twice (javase/pom.xml mounts the same
  sources via testSourceDirectory).
* maven/cn1app-archetype/.../javase/pom.xml: add junit-jupiter at
  test scope. Surefire runs from this module and now actually has
  the JUnit Jupiter engine on its classpath. The codenameone-javase
  artifact's "provided" scope for junit-jupiter still keeps it out
  of user app classpaths that do not opt in.
* scripts/initializr/.../skill/references/junit-testing.md: new
  skill reference covering the JUnit 5 path end-to-end -- when to
  use it vs AbstractTest, dependency setup, every annotation with a
  worked example, EDT dispatch semantics, the headless behavior and
  why CodenameOneExtensionTest is gated for it, coexistence with
  cn1:test, side-by-side example.
* scripts/initializr/.../skill/SKILL.md: index the new reference and
  expand the Testing section so the choice between AbstractTest and
  @CodenameOneTest is the first thing a contributor sees.
* scripts/initializr/.../skill/references/testing-and-screenshots.md:
  add a "Two ways to write tests" preamble that points at the new
  junit-testing.md so users find the JUnit option from either entry.
* docs/developer-guide/Testing-with-JUnit.adoc: long-form chapter
  for the manual covering the same material as the skill reference
  but in AsciiDoc, with a comparison table, dependency snippets,
  annotation reference, headless explanation, and side-by-side
  worked example. Wired into developer-guide.asciidoc next to the
  performance chapter so it sits near the existing screenshot
  testing material.

Verified by mvn install of the archetype, archetype:generate of a
new app from it, and javac-compiling a sample @CodenameOneTest
against the locally-installed codenameone-javase 8.0-SNAPSHOT +
junit-jupiter-api 5.9.3 artifacts. JavaSE port suite remains at
63 passing tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@github-actions
Copy link
Copy Markdown
Contributor

Cloudflare Preview

shai-almog and others added 3 commits May 25, 2026 07:52
The dev-guide CI runs Vale with the Microsoft style and fails the
build on any error-level alert. The new chapter tripped four errors
(Contractions, Foreign 'e.g.', two hyphenated 'auto-...') and five
warnings (HeadingPunctuation, Adverbs, We). Reworded:

* "vs." -> "versus" in the section heading.
* Dropped the "freely" / "partially" / "auto-" adverbs and rewrote
  the sentences without them.
* "e.g." -> "for example".
* "our own internal" -> "the framework's internal" to avoid the
  first-person plural.

Vale now reports 0 errors, 0 warnings, 0 suggestions for this file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dev-guide aggregate quality gate fails when LANGUAGETOOL_COUNT != 0
(not just when LANGUAGETOOL_STATUS != 0). The Testing-with-JUnit chapter
introduced four spelling-rule matches that LanguageTool's English
dictionary doesn't carry:

* rethrown -- past tense of "rethrow" (already accepted) and "rethrows"
  (already accepted). Adding the third form for completeness.
* Throwable / Throwables -- java.lang.Throwable and its plural, used
  when describing the EDT dispatch semantics.
* javase -- the lowercase Maven module name.
* Xvfb -- X virtual framebuffer, named in the headless-runner section.

These are all legitimate technical terms used throughout the chapter,
so they belong in the accept list rather than reworded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The @theme annotation now accepts a native-theme alias in addition to a
resource path. The new NativeTheme enum mirrors the simulator's
Simulate > Native Theme menu (iOS Modern / iOS Flat / iPhone Pre-Flat /
Android Material / Android Holo Light / Android Legacy) and carries
both the .res resource path and the human-readable label that menu
displays. The extension prefers the enum form when both are set; a
non-empty value() is used only when nativeTheme is left at NONE.

  @test @theme(nativeTheme = NativeTheme.IOS_MODERN)        ...
  @test @theme(nativeTheme = NativeTheme.ANDROID_MATERIAL)  ...
  @test @theme("/MyAppTheme.res")                            ...

Docs and skill files updated to lead with the enum form, since it is
the recommended path for cross-platform look-and-feel coverage; the
resource-path form remains for app themes shipped under
src/main/resources.

Same commit removes the "legacy" framing from the AbstractTest /
cn1:test path across all docs (dev-guide chapter, junit-testing.md
skill, testing-and-screenshots.md skill, SKILL.md), and drops the
version-anchored language ("Starting with Codename One 8",
"releases >= 8.0") that I had guessed wrong. AbstractTest is the only
framework that runs on a device via ParparVM and isn't going anywhere
-- the two frameworks are peers and the docs now describe them by
their tradeoffs (device vs. simulator-only, device subset vs. full
JVM) rather than by recency.

64 javase tests pass (was 63 + a new NativeTheme test that asserts
the enum's resourcePath() and displayName() carry the right values).
Vale prose-lint clean on the updated chapter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog shai-almog merged commit 8c02961 into master May 25, 2026
26 of 27 checks passed
shai-almog added a commit that referenced this pull request May 30, 2026
* Blog: developer workflow (on-device debugging + JUnit 5)

Consolidated follow-up to the May 29 weekly index. Combines what
were previously two separate posts into one themed post about how
the dev iteration loop changes this release:

- On-device debugging (PRs #4999 iOS + #5012 Android): JDWP through
  to real devices and the iOS / Android simulators, so jdb,
  IntelliJ, VS Code, Eclipse, and NetBeans attach against Java
  source. The three iOS pieces (translator instrumentation, device
  runtime, JDWP proxy) and the smaller adb-orchestration Android
  half.
- JUnit 5 against the JavaSE simulator (PR #5032): @CodenameOneTest,
  @RunOnEdt, @SimulatorProperty, and the visual-config annotations
  (@theme, @DarkMode, @LargerText, @orientation, @rtl). junit-jupiter
  scope change so JUnit does not leak onto the simulator runtime for
  apps that do not opt in.

Both together push the dev loop on device closer to what it looks
like on a regular JVM application.

* Developer-workflow post: style pass

- "I" voice replaced with "we" or "Codename One"-flavoured phrasing
  throughout. Opening hook, "this is the Java code I wrote" line,
  the "thing that surprised me" section, the wrap-up.
- Heading and prose for "the thing that surprised me" reworked so
  the surprise is framed as something the team noticed rather than a
  personal anecdote.
- Wrap-up rephrased so the "two pieces of paving" sentence reads as
  a team observation rather than a personal opinion.

* Developer-workflow post: full rewrite per review

- New opening that does not start with the "Two things this post"
  fragment.
- Supported targets clarified: iOS = simulator or real iPhone over
  Wi-Fi from a Mac (the "USB pairing" language is gone). Android =
  USB, wireless adb, or the emulator (all three).
- New paragraph spelling out that iOS still needs a Mac in the
  pipeline because the bits come from Xcode (locally or via the
  build server).
- Dev-guide references go to the HTML version on the website
  (/developer-guide/#_on_device_debugging_ios etc.), not to the
  asciidoc source.
- Step-by-step IntelliJ tutorial for iOS: enable the four build
  hints (using the in-settings names, no codename1.arg. prefix),
  recommend leaving the block-on-load option on so the
  on-device-debug variant is visible, run the proxy, attach the
  debugger, launch the app.
- Same shape Android tutorial.
- Device-console behaviour and its caveat called out explicitly.
- Capability note up front: Android can step through native C/C++
  via Android Studio's LLDB alongside the JDWP attach; iOS cannot.
- "The thing that surprised us" section removed.
- "Why the two changes pair" section removed.
- JUnit section rewritten: explicit that it is standard JUnit 5
  (not a fork), com.codename1.testing.junit holds annotations +
  CodenameOneExtension, AbstractTest stays for on-device tests.
- Tutorial: file location, minimal example, plain-validator
  variant, visual-config variant, @SimulatorProperty variant.
- Testcontainers / WireMock no longer in the example list.
- Hero image lands at /blog/developer-workflow-debug-and-junit.jpg.
- IntelliJ debugger screenshot lands at
  /blog/developer-workflow-debug-and-junit/intellij-debugger-on-device.png.
- "Back to the weekly index" link to the intro post in the wrap-up.

* Workflow post: fix Mac framing, add Mermaid for flow diagrams

Two reviewer-driven fixes.

1. The "iOS still needs a Mac somewhere in the pipeline" framing
   was technically true but misleading -- the build cloud has a Mac,
   so a developer working on Linux or Windows does not need a local
   one to debug a real iPhone. The supported-targets section now
   says that explicitly: the Mac is only required for the local
   Xcode build path and for the iOS Simulator. The Build Cloud
   handles the iOS build and the JDWP attach works over Wi-Fi from
   any OS.

2. The Android paragraph no longer talks about not-needing-a-Mac
   (redundant). It now talks about needing the Android SDK platform
   tools (adb) on the developer machine, and that those are
   available on macOS, Linux, and Windows.

3. Replaced both ASCII data-flow boxes with real Mermaid diagrams.
   New {{< mermaid >}} shortcode + an inline ESM loader (gated by
   window.__cn1MermaidLoaded so multiple diagrams on one page only
   initialise once).

The loader can't live in extend_footer.html because PaperMod calls
that partial via partialCached keyed on layout/kind, so any per-page
conditional content there ends up shared across every page sharing
the same cache key. Inlining the loader in the shortcode dodges
that completely; pages without a mermaid shortcode never see the
loader.

Verified locally with `hugo --buildFuture`: the developer-workflow
post is the only page on the site that emits the loader.

* website: render future-dated posts on PR previews

The Cloudflare PR preview build was hiding the new on-device-debug
post because its front-matter date (2026-05-30) is one day past the
current build date. Hugo skips future-dated posts unless --buildFuture
is set.

- scripts/website/build.sh now reads HUGO_BUILD_FUTURE (default
  "false") and HUGO_BUILD_DRAFTS (default "false") and passes
  --buildFuture / --buildDrafts to Hugo when set. Production
  behaviour unchanged.
- .github/workflows/website-docs.yml sets HUGO_BUILD_FUTURE=true on
  pull_request runs and "false" on the merged-to-master deploy. So
  reviewers see staged posts on the *.pages.dev preview; the live
  site still only shows posts on or after their publish date.

* Workflow post: review fixes

- JUnit example now boots the project's app class through init() /
  start() instead of constructing a Form in the test. Asserts
  against the form the app actually opens; that exercises the same
  startup path the simulator runs, which is what most apps want to
  test.
- Maven command is "mvn -Ptest test" (the project's test profile),
  not "mvn -pl javase test".
- Added IntelliJ-gutter screenshot and JUnit-results screenshot to
  the test tutorial. The screenshots make the IDE flow the primary
  example; the command-line version is below.
- The "fastest iteration loop" remark on the local-Xcode-build
  bullet is removed; both build paths are presented as equivalent.
- Block-on-load reasoning rewritten. The concern is not "an app
  that does nothing"; it is (a) launching an on-device-debug build
  while the proxy is not running and not realising it is silently
  waiting, and (b) mistaking the on-device-debug build for a
  release build and being surprised when it does not perform as
  smoothly.
- Native-interface capability section rewritten. The previous
  "Android Studio's LLDB lets you step into C / C++" framing was
  wrong; Codename One native interfaces are Java on Android, so
  the JDWP attach steps through them like any other class. On iOS
  the Impl is Objective-C; JDWP can't step through it, but framework
  code, your own Java, and the values the call returns are all still
  available, so most workflows still hold. Attach Xcode in parallel
  for the Objective-C body if you need it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant