Checkpoint Lab — CLI Mode, Headless Execution, Result Files, and Reproducible Runs
The checkpoint treats a CLI run as an immutable evidence bundle. You will first prove a successful eight-sample run, then create a deliberate missing-JMX engine failure and preserve its non-success evidence, then restore a second successful run from a clean shell. The comparison must show that only run identity/output paths changed—not input hashes, workload or target behavior.
Learning objectives
- Execute a cross-platform-oriented launcher with bounded local workload.
- Produce isolated JTL/log/report/version/hash/command/gate/manifest artifacts.
- Predict successful and missing-file execution outcomes before running.
- Trigger and preserve one genuine JMeter missing-JMX process failure.
- Restore a clean successful run without deleting or mutating failed evidence.
- Verify configured versus achieved sample counts and target events independently.
1. Exact assumptions and hard ceilings
| Item | Checkpoint baseline |
|---|---|
| JMeter | Apache JMeter 5.6.3. |
| Java | Java 17 JDK; JMeter 5.6.3 requires Java 8+. |
| Plugins | None. |
| Target | http://127.0.0.1:8021 only. |
| Fixture | Python stdlib prompt21-cli-fixture-v1. |
| Plan | plans/cli-local.jmx. |
| Properties |
config/local.properties via -q.
|
| Run property | -Jrun.id=<unique>. |
| Load | 2 threads ×4 loops = 8 work samples. |
| Pacing | 20 ms Constant Timer. |
| Result | CSV JTL + explicit jmeter.log. |
| Report | -e -o <new run-dir>/html-report. |
| Gate | expected samples=8; failures=0; lab p95≤500 ms. |
| Failure case | missing JMX direct CLI; no production target or failure injection. |
-f on an unchecked path.
2. Setup / preflight
- Create fixture, property file, JMX, launcher, gate/manifest/analyzer scripts from Lesson 2.
- Start fixture on 127.0.0.1:8021 with a fresh JSONL event log.
-
GET
/health//statsand record baseline work count. - Record JMeter/Java version and SHA-256 of JMX/properties.
- Verify
results/p21-check-adoes not exist.
3. Predictions before the successful run
Prediction A — artifacts: launcher creates one new
run directory containing command/version/pre-manifest, JTL,
jmeter.log, engine/gate exit files, HTML report and
final manifest.
Prediction B — workload: exactly eight successful Work samples appear in JTL and exactly eight matching target events occur: four for T1 and four for T2.
Prediction C — status: engine exit=0 and gate exit=0 when row count, failures and lab p95 are valid.
4. Successful run A
PowerShell:
Set-Location "F:\Labs\p21-cli-lab"
.\tools\run-local.ps1 -RunId p21-check-a
$LASTEXITCODE
Or Bash:
cd /path/to/p21-cli-lab
./tools/run-local.sh p21-check-a
printf 'wrapper_exit=%s
' "$?"
5. Verify run A
engine-exit.txt= 0;gate-exit.txt= 0;-
gate.json= PASS, samples=8, failures=0, p95≤500; - JTL has header + 8 sample data rows;
jmeter.loghas no unexplained ERROR/FATAL;html-report/index.htmlexists;-
run-manifest.jsoncontains exact command, versions, hashes and paths.
python tools/analyze_events.py results/server-events.jsonl p21-check-a
Require eight target work events with four per thread.
6. Predict the intentional missing-file failure
Prediction D: directing raw JMeter at
plans/missing-plan.jmx produces non-success
engine/process evidence before the normal eight-sample workload. The
target should receive zero work events for the missing-run ID, and
the safe launcher's historical run A must remain untouched.
7. Execute the missing-JMX diagnostic
PowerShell:
$ProjectRoot = "F:\Labs\p21-cli-lab"
$FailDir = Join-Path $ProjectRoot "results\p21-missing"
if (Test-Path $FailDir) { throw "Failure evidence directory already exists." }
New-Item -ItemType Directory $FailDir | Out-Null
$Command = @(
"$env:JMETER_HOME\bin\jmeter.bat","-n",
"-t",(Join-Path $ProjectRoot "plans\missing-plan.jmx"),
"-q",(Join-Path $ProjectRoot "config\local.properties"),
"-Jrun.id=p21-missing",
"-l",(Join-Path $FailDir "results.jtl"),
"-j",(Join-Path $FailDir "jmeter.log")
)
$Command -join " " | Set-Content (Join-Path $FailDir "command.txt")
& $Command[0] $Command[1..($Command.Count-1)]
$EngineExit = $LASTEXITCODE
$EngineExit | Set-Content (Join-Path $FailDir "engine-exit.txt")
Bash:
PROJECT_ROOT=/path/to/p21-cli-lab
FAIL_DIR="$PROJECT_ROOT/results/p21-missing"
[[ ! -e "$FAIL_DIR" ]] || { echo "Failure dir exists" >&2; exit 2; }
mkdir -p "$FAIL_DIR"
"$JMETER_HOME/bin/jmeter" -n -t "$PROJECT_ROOT/plans/missing-plan.jmx" -q "$PROJECT_ROOT/config/local.properties" -Jrun.id=p21-missing -l "$FAIL_DIR/results.jtl" -j "$FAIL_DIR/jmeter.log"
ENGINE_EXIT=$?
printf '%s
' "$ENGINE_EXIT" >"$FAIL_DIR/engine-exit.txt"
Do not use -f. Do not delete the failure directory
after observing the expected error.
8. Verify non-success evidence
Require:
- non-zero engine/process exit;
-
jmeter.logor console evidence identifies the missing/unloadable JMX path; - no valid eight-row workload for
p21-missing; -
python tools/analyze_events.py results/server-events.jsonl p21-missingreports zero work events; - run A artifacts/hashes are unchanged.
This is an engine/input failure, not an SLO failure.
9. Restore with the validated launcher from a clean shell
PowerShell:
Set-Location "$env:TEMP"
& "F:\Labs\p21-cli-lab\tools\run-local.ps1" -RunId p21-check-b
$LASTEXITCODE
Run B must create a new directory and use the real
cli-local.jmx without touching
p21-missing.
10. Compare successful run A and B
Verify in both manifests:
- same JMX SHA-256;
- same property-file SHA-256;
- same JMeter/Java expected baseline;
- same configured 2×4/20 ms load and eight achieved successful samples;
- same target operation distribution;
- different run IDs/output directories and possibly caller cwd;
- both reports reconstruct from their retained JTL.
11. Prove report reproducibility from raw JTL
Generate a second report from run A:
& "$env:JMETER_HOME\bin\jmeter.bat" `
-g "F:\Labs\p21-cli-lab\results\p21-check-a\results.jtl" `
-o "F:\Labs\p21-cli-lab\results\p21-check-a\html-report-rebuilt"
The rebuilt report is derived from preserved raw results; no target traffic is generated.
12. Generator/measurement validity
Record generator CPU/memory/network/disk during one successful run. The workload is intentionally tiny; the expected conclusion is only that the CLI/evidence mechanics are valid under local headroom.
Configured load = 8 work samples. Achieved load = 8 successful JTL samples and 8 target events. Never turn this localhost number into a production capacity statement.
13. Required evidence packet
| Artifact | Required content |
|---|---|
| Exact CLI/command | command.txt or manifest tokens for each successful/failure invocation. |
| Versions | JMeter and Java captured for successful runs. |
| Input hashes | SHA-256 JMX + property file. |
| Run directory | Unique p21-check-a, p21-missing, p21-check-b paths. |
| JTL | 8 successful rows for each valid run; partial/absent for missing JMX as expected. |
| jmeter.log | Run-specific engine log; failure log preserved. |
| HTML report | At-end report plus one later -g rebuild. |
| Process exit | engine-exit.txt; missing-JMX run non-zero. |
| Gate | gate-exit/gate.json separate from engine process. |
| Manifest | command/paths/hashes/versions/artifacts/exits/gate summary. |
| Target evidence | 8 work events for each valid run; zero for missing-JMX run ID. |
14. Validity statement
cli-local.jmx against
prompt21-cli-fixture-v1 at 127.0.0.1:8021 through a
wrapper that created immutable run directories, captured the exact
CLI, tool versions and SHA-256 input hashes, wrote CSV JTL and
run-specific jmeter.log, generated an HTML report with
-e -o, recorded the engine exit, and independently
gated expected row count/failures/lab p95. Two successful runs from
different caller directories produced the same input hashes, eight
valid JTL samples and eight target work events. A direct CLI
invocation of a nonexistent JMX produced preserved non-success
engine/log evidence and zero matching target work; no prior
artifacts were deleted. A later -g command rebuilt a
report from retained JTL without target traffic. This validates the
local CLI/reproducibility/evidence workflow, not production service
capacity.”
15. Verification checklist
- Target exactly 127.0.0.1:8021.
- JMeter 5.6.3 / Java 17 / no plugins recorded.
- No load run in GUI.
-
Safe wrapper never uses
-fand refuses existing run IDs. - JMX/property SHA-256 recorded.
- Valid runs have engine exit=0 and independent gate=0.
- Each valid run has exactly eight JTL samples and eight target work events.
- Missing-JMX run has non-success engine evidence and zero target work.
- HTML report exists and can be regenerated later from JTL.
- No secrets appear in commands, properties, logs or manifests.
16. Cleanup / rollback
- Stop the localhost fixture after target evidence is captured.
-
Keep
JTL/
jmeter.log/reports/manifests/version/hash/failure evidence until review completes. - Delete only disposable local run directories after review; never delete historical evidence as part of rerun startup.
- No public/production API, credential, recorder certificate, remote RMI engine, container, database/message service, paid platform, CI secret or OS/JVM global tuning was changed.
17. What Chapter 21 adds to the operating model
The production performance-testing operating model now has a
headless execution/evidence contract: approved
JMeter/Java versions; explicit JMX/property/data hashes; safe CLI
flags; immutable run directory; raw JTL; run-specific
jmeter.log; optional reproducible dashboard; exact
command; process exit; independent correctness/performance gate;
generator resource evidence; target operation evidence; and
retention/cleanup rules are required before a run becomes
CI/scheduled release evidence.
Chapter 22 moves to Listeners, Result Collection, Memory Cost, and Safe Debugging. It deepens the result-side contract by showing which listeners are safe during authoring/load, what they retain in memory, how result fields/save settings change cost, and how to debug without invalidating the generator.
Knowledge check
Why must the missing-JMX failure remain in a separate directory?
It preserves diagnostic provenance and guarantees a recovery run cannot overwrite the original failure evidence.
What proves run A and B are reproducible equivalents?
Same JMX/property hashes and resolved load plus eight valid JTL/target events under the same tool baseline, despite different run IDs/cwd.
Why is a rebuilt -g report useful evidence?
It proves the dashboard can be reproduced from retained raw JTL without rerunning target load.
If engine exit is zero but gate exit is 30, what failed?
The JMeter process completed, but JTL correctness/sample-count/performance criteria failed.
What is Chapter 22's bridge?
Understand listener/result-retention memory and save-service costs so raw evidence remains useful without saturating the load generator.
Official references and version notes
-
JMeter Getting Started — CLI mode
—
-n,-t,-l,-j,-g,-e,-o,-J,-L, remote flags, and CLI/load guidance. -
JMeter Listeners / Result files
— CSV versus XML, save-service defaults, CLI
-llistener, result fields, and memory guidance. -
JMeter Generating Dashboard Report
— dashboard-required CSV fields,
-g,-e -o, output-folder rules, graphs, and report properties. - JMeter Best Practices — GUI authoring/debugging and CLI execution for load.
- Apache JMeter downloads — current stable release and Java requirement.
Version-sensitive statements were rechecked against current Apache
JMeter primary documentation on 2026-09-05. The course baseline
remains Apache JMeter 5.6.3 with a Java 17 JDK;
JMeter 5.6.3 requires Java 8+. JMeter's own manual says GUI mode
is for building/debugging while CLI mode must be used for load
testing. Current CLI flags include -n (CLI),
-t (JMX), -l (JTL), -j (run
log), -g <CSV> (report only),
-e (report after test), and -o (report
output). The report output folder must not exist or must be empty.
-f force-deletes an existing result file/report
folder before a test, so the safe launcher in this chapter
intentionally does not use it; instead it refuses to reuse an
existing run directory. CSV result files are smaller than XML and
are the normal large-run choice. Current CSV defaults include
timing/status/thread and byte fields such as
timeStamp, elapsed, label,
responseCode, success,
bytes, sentBytes,
grpThreads, and allThreads when their
save-service fields are enabled (the current defaults required by
the dashboard are documented as correct unless changed). Response
data is not supported in CSV. JMeter process/engine completion is
therefore treated separately from a post-run SLO/result gate: a
launcher first records the engine exit code, then validates JTL
sample counts/failures/latency. A zero engine exit is not used as
evidence that every sample met an SLO.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.