Skip to content

Commit 6ddd47f

Browse files
committed
fix(examples): guard the baseline resource and repair what the extraction left behind
Removing majorMinor from the deck deleted the method but not its javadoc, so the block was left heading logo() and documenting an @param version that method does not take. The showcase javadoc promised "three pages of designed content"; the document renders two. The examples job gates on the `code` path filter, which did not list baselines/. Now that the module renders figures from the committed baseline, a baseline-only refresh would have changed generated output while skipping the job that checks it. PerfBaseline degraded inconsistently: a missing resource was swallowed while a malformed one threw from a static initializer and would have taken down every example in the module, not just the one quoting a figure. Loading now never throws, and a scenario whose figures are absent or non-numeric is treated as absent rather than read as 0.0 - which would have rendered a plausible "0.0 ms avg, 0 docs/sec" instead of admitting nothing was measured. That fallback is honest at render time and wrong to discover in a published PDF, so PerfBaselineTest asserts the resource really is on the classpath with the scenarios the showcase quotes. Verified by pointing the pom entry at a filename that does not exist: on a clean build two of its three tests fail with the intended message.
1 parent d964579 commit 6ddd47f

5 files changed

Lines changed: 80 additions & 17 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ jobs:
7979
- '**/pom.xml'
8080
- '**/src/main/resources/**'
8181
- '**/src/test/resources/**'
82+
# The examples module renders figures from the committed perf
83+
# baseline, so a baseline-only refresh changes generated output.
84+
- 'baselines/**'
8285
- '.mvn/**'
8386
- 'mvnw'
8487
- 'mvnw.cmd'

examples/src/main/java/com/demcha/examples/flagships/EngineDeckV2Example.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -909,15 +909,6 @@ private static String snapshotDate(EngineDeckData.BenchRun bench) {
909909
return timestamp.substring(0, Math.min(10, timestamp.length()));
910910
}
911911

912-
/**
913-
* Reduces a version to its {@code major.minor} line for display.
914-
*
915-
* @param version the resolved build version, possibly a qualifier or {@code "dev"}
916-
* @return {@code "2.1"} for {@code "2.1.0"} / {@code "2.1.0-SNAPSHOT"}; the input
917-
* unchanged when it carries no dotted numeric prefix
918-
*/
919-
920-
921912
private static SvgIcon logo() {
922913
return icon("logo");
923914
}

examples/src/main/java/com/demcha/examples/flagships/MasterShowcaseExample.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* QR + Code 128 barcodes, document metadata, and header / footer chrome.
3838
*
3939
* <p>The rendered PDF reads like a fictional "GraphCompose Q2 Sample
40-
* Report" — three pages of designed content meant to look at a glance
40+
* Report" — designed content meant to look at a glance
4141
* like the kind of business document GraphCompose was built to
4242
* generate, not a feature checklist. Use it as a reference when
4343
* composing your own multi-page documents.</p>

examples/src/main/java/com/demcha/examples/support/PerfBaseline.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.ObjectMapper;
55

6+
import java.io.IOException;
67
import java.io.InputStream;
7-
import java.io.UncheckedIOException;
88
import java.util.HashMap;
99
import java.util.Map;
1010
import java.util.Optional;
@@ -21,11 +21,19 @@
2121
* <p>The figures are one machine's median over several runs. Absolute
2222
* milliseconds do not travel between machines, so anything rendering them
2323
* should show {@link #capturedOn()} beside them and let the reader judge.</p>
24+
*
25+
* <p>Loading never throws: a resource that is missing, unreadable or malformed
26+
* yields an empty baseline, and callers render a plain "not measured" rather
27+
* than a number nobody measured. {@code PerfBaselineTest} keeps that fallback
28+
* from becoming the normal case by asserting the resource really is on the
29+
* classpath with the scenarios the examples quote.</p>
2430
*/
2531
public final class PerfBaseline {
2632

2733
private static final String RESOURCE = "/baselines/current-speed-full.json";
2834

35+
private static final PerfBaseline EMPTY = new PerfBaseline("undated", Map.of());
36+
2937
private static final PerfBaseline INSTANCE = load();
3038

3139
private final String capturedOn;
@@ -74,23 +82,36 @@ public Optional<Scenario> scenario(String scenario) {
7482
}
7583

7684
private static PerfBaseline load() {
85+
// Never throws. This runs in a static initializer, so a malformed file
86+
// would otherwise fail class-load and take down every example in the
87+
// module, not just the one quoting a figure. Absent and unreadable
88+
// degrade the same way, and the caller renders the honest fallback.
7789
try (InputStream in = PerfBaseline.class.getResourceAsStream(RESOURCE)) {
7890
if (in == null) {
79-
return new PerfBaseline("undated", Map.of());
91+
return EMPTY;
8092
}
8193
JsonNode root = new ObjectMapper().readTree(in);
8294
Map<String, Scenario> scenarios = new HashMap<>();
8395
for (JsonNode row : root.path("latency")) {
84-
scenarios.put(row.path("scenario").asText(),
85-
new Scenario(row.path("avgMillis").asDouble(),
86-
row.path("docsPerSecond").asDouble()));
96+
String name = row.path("scenario").asText("");
97+
// Require both figures to be present and numeric: a renamed or
98+
// dropped field would otherwise read as 0.0 and render a
99+
// plausible "0.0 ms avg, 0 docs/sec" instead of admitting the
100+
// measurement is missing.
101+
if (name.isEmpty()
102+
|| !row.path("avgMillis").isNumber()
103+
|| !row.path("docsPerSecond").isNumber()) {
104+
continue;
105+
}
106+
scenarios.put(name, new Scenario(row.path("avgMillis").asDouble(),
107+
row.path("docsPerSecond").asDouble()));
87108
}
88109
String timestamp = root.path("timestamp").asText("");
89110
return new PerfBaseline(
90111
timestamp.length() >= 10 ? timestamp.substring(0, 10) : "undated",
91112
Map.copyOf(scenarios));
92-
} catch (java.io.IOException e) {
93-
throw new UncheckedIOException("Cannot read " + RESOURCE, e);
113+
} catch (IOException | RuntimeException e) {
114+
return EMPTY;
94115
}
95116
}
96117
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.demcha.examples.support;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
7+
/**
8+
* Keeps the honest fallback from quietly becoming the normal case.
9+
*
10+
* <p>{@link PerfBaseline} degrades to "not measured" when its resource is
11+
* absent, which is the right behaviour at render time and the wrong thing to
12+
* discover in a published PDF. The baseline reaches the classpath through a
13+
* resource directory in {@code examples/pom.xml} that points outside the
14+
* module; rename the file, move the directory, or drop that pom entry and every
15+
* figure in the master showcase silently turns into a placeholder while the
16+
* build stays green. This is the test that would go red instead.</p>
17+
*/
18+
class PerfBaselineTest {
19+
20+
@Test
21+
void theCommittedBaselineIsOnTheClasspath() {
22+
assertThat(PerfBaseline.get().capturedOn())
23+
.describedAs("baseline resource missing from the examples classpath - "
24+
+ "check the baselines resource entry in examples/pom.xml")
25+
.matches("\\d{4}-\\d{2}-\\d{2}");
26+
}
27+
28+
@Test
29+
void itCarriesTheScenariosTheShowcaseQuotes() {
30+
for (String scenario : new String[] {"invoice-template", "feature-rich"}) {
31+
assertThat(PerfBaseline.get().scenario(scenario))
32+
.describedAs("MasterShowcaseExample renders '%s', so the baseline must carry it",
33+
scenario)
34+
.hasValueSatisfying(measured -> {
35+
assertThat(measured.avgMillis()).isPositive();
36+
assertThat(measured.docsPerSecond()).isPositive();
37+
});
38+
}
39+
}
40+
41+
@Test
42+
void anUnknownScenarioIsAbsentRatherThanZero() {
43+
assertThat(PerfBaseline.get().scenario("no-such-scenario"))
44+
.describedAs("an unknown scenario must be empty so the caller can say "
45+
+ "'not measured' instead of rendering 0.0 ms")
46+
.isEmpty();
47+
}
48+
}

0 commit comments

Comments
 (0)