- cleanupStaleContainers 45 -> 2, UiFormats.cpuTime 43.8 -> 7, monitoredDisks 42 -> 2: tests reaching every branch; `ContainerInfo.isWerkatorLeftover`, `monitoredDisksOf`, shared `InstancePath.resolve`. - Fix for the failed gated mutation build (2 INCONCLUSIVE from MEMORY_ERROR): `ConsoleBuildRunner.awaitTerminal` decides the end of the wait once; the nginx test sleeper fails after 100 rounds. Local PIT: 0 MEMORY_ERROR, 0 INCONCLUSIVE. - Nightly gate 46 -> 36 (highest left: SystemMetricsCollector.sample 35.36). PR-doc: `docs/prs/2026-10-04-PR#110-nginx-cleanup-cpu-time-disks.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #110
6.4 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
After PR#109 the three highest Mutation-CRAP scores of Werkator were unrelated, each small:
| Method | Mutation-CRAP | Killed / eligible | Why |
|---|---|---|---|
NginxProxyManager.cleanupStaleContainers |
45.04 | 1 / 6 | tested, but the tests passed whatever it did: the start removes the container by name once more later |
UiFormats.cpuTime |
43.81 | 1 / 11 | no test of its own |
MetricsConfiguration.monitoredDisks |
42 | 0 / 9 | read the configuration through Spring beans, no test reached it |
The first mutation build with an active gate (PR#104) failed on an analysis error instead: two methods were INCONCLUSIVE, because a mutant had made a bounded wait endless and filled the memory of PIT's minion (MEMORY_ERROR), which werkcrappit refuses to count as killed (its ADR 0006).
In NginxProxyManager.waitForPortsFree the mutant turned repeat(4) into an endless loop, and the test's sleeper did not sleep; in ConsoleBuildRunner.buildAndStream the end of the wait was asked twice, and a mutant of the first question alone made it endless and without a pause.
Tonight's mutation build would have failed the same way.
The rule for a path in the instance file — ~ is the home directory, anything relative is relative to it — was written twice, for the registry's repositories and for the System page's disks.
Non-Goals
- No change in behaviour.
The Scenarios
Feature: Three small decisions, held by tests
Scenario#110.01: Processor time reads with its factor against the wall clock
So that 4:07 (2.4x) stays what the artifact page shows.
- Given processor times with and without a duration, a zero duration, nothing measured, and a negative time
- When they are formatted
- Then the factor appears only with a duration above zero, and nothing measured shows nothing
Verified by
- UiViewsUnitTest — Scenario#110.01
Scenario#110.02: The System page shows the configured disks, their paths read like every path of the instance file
So that ~, ~/… and relative paths mean the same for a disk as for a repository.
- Given no
metrics.disks, and disks with~, an absolute path,~/…with a blank label, and a relative path - When the monitored disks are resolved
- Then without the section the checkout's filesystem is
Disk; otherwise the disks keep their order, paths resolve against the home directory, and a blank label falls back to the key - And only
~and~/expand,~otheris a name like any other
Verified by
- MonitoredDisksUnitTest — Scenario#110.02
Scenario#110.03: Only Werkator's own leftovers are removed before the managed nginx starts
So that a stale container of Werkator never blocks a port, and someone else's container is never touched.
- Given a container of the configured name, labelled nginx containers of this repository, and containers on and off the nginx ports
- When the managed nginx starts
- Then the container of the name goes first, then the labelled ones; a failed or empty listing removes nothing by label
- And on an nginx port only a container labelled as Werkator's, or named
werkator-…orgit-watch-origin-and-test-nginx-…, is removed
Verified by
- NginxProxyManagerUnitTest — Scenario#110.03
Scenario#110.04: A wait that a mutant makes endless fails a test instead of filling the memory
So that the mutation gate judges test quality, not memory, and the nightly build can pass.
- Given the port wait of the managed nginx and the console's wait for a build
- When PIT runs on both classes
- Then no mutant ends with
MEMORY_ERROR, and no method isINCONCLUSIVE
Verified by
- NginxProxyManagerUnitTest: its sleeper fails after 100 rounds
- ConsoleBuildRunnerUnitTest
- A local PIT run on both classes: 0
MEMORY_ERROR, 0INCONCLUSIVE
The Solution
NginxProxyManager.ContainerInfo.isWerkatorLeftovernames the rule for a stale Werkator container;removeLabelledContainersthe removal by label;cleanupStaleContainersreads as the three steps.MetricsConfiguration.monitoredDisksOfresolves the disks without Spring;InstancePath.resolveis the one rule for a path in the instance file, used byRepositoryEntry.directoryas well.UiFormats.cpuTimegets the test it lacked.ConsoleBuildRunner.awaitTerminaldecides the end of the wait at one place; the test sleeper ofNginxProxyManagerUnitTestfails after 100 rounds.
Measured with PIT as the nightly build runs it:
| Method | Before | After |
|---|---|---|
NginxProxyManager.cleanupStaleContainers |
45.04 | 2 |
UiFormats.cpuTime |
43.81 | 7.04 |
MetricsConfiguration.monitoredDisks |
42 | 2 |
MetricsConfiguration.monitoredDisksOf |
— | 4 |
ContainerInfo.isWerkatorLeftover |
— | 3.03 |
The gate maxMutationCrap falls from 46 to 36: the highest score left is SystemMetricsCollector.sample at 35.36.
Additional Changes
- Version 1.34.7 with its release note.
Follow-up PRs
- The methods above 15 by complexity alone:
SystemMetricsCollector.sample(35),BuildDefinition.applyTo(34),ForgejoWorkflowExporter.settingNotes(27),SystemMetricsView.from(24), and theConfigLoaderchecks.