Skip to content

acquireInstanceLock() — extract exit/dialog seam to enable unit testing #169

Description

@jamesmarkchan

Background

App.acquireInstanceLock() uses NIO FileLock to prevent multiple GUI instances from running simultaneously. When a second instance is detected, it calls System.exit(0) inside a SwingUtilities.invokeLater lambda:

SwingUtilities.invokeLater(() -> {
    JOptionPane.showMessageDialog(...);
    System.exit(0);  // ← terminates the test JVM if called during tests
});

This is correct production behavior, but it creates a test hazard: any future test that exercises the GUI startup path and encounters an already-held lock will terminate the entire Maven Surefire JVM rather than failing the individual test cleanly.

Problem

  1. System.exit in test JVM — if a test triggers the "already running" branch, the whole test suite crashes with a cryptic Surefire failure instead of a clean test failure.
  2. No coverage of the lock lifecycle — there are no tests verifying: first call acquires lock → true; second call finds it held → false; lock releases on channel close; next startup succeeds.
    Proposed fix
    Extract the exit and dialog side-effects behind an injectable strategy (e.g. a Runnable or BiConsumer parameter defaulting to the real System.exit + JOptionPane call). This makes acquireInstanceLock() unit-testable by passing a no-op or capture lambda in tests.
// Example seam
static boolean acquireInstanceLock(Runnable onAlreadyRunning) { ... }
// Production call (unchanged behavior)
acquireInstanceLock(() -> {
    SwingUtilities.invokeLater(() -> {
        JOptionPane.showMessageDialog(...);
        System.exit(0);
    });
});

Acceptance criteria

  • acquireInstanceLock() does not call System.exit directly — exit/dialog behavior is injectable
  • Unit test: first call acquires lock, returns true
  • Unit test: second call with lock held returns false and invokes the onAlreadyRunning callback
  • Unit test: releasing the channel allows a subsequent call to succeed
  • Existing GUI startup behavior unchanged

References

  • Flagged in PR code review (Copilot, medium severity) on icon-base branch, App.java lines 312–314

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions