Skip to content

Commit 90b2cae

Browse files
authored
Redesign reporting (#378)
- Adds new fields to StageTimeMeasurement - Removes aggregate launch stats from JSON export - Change boosters to capture additional details in StageTimeMeasurement - Add new test cases for each booster - Rewrites report generation logic - Replaces report UI with new views - Migrates UI generation to use Node.js - Adds dark mode/light mode toggle - Adds advanced filtering and additional telemetry to report - Skips release if only NPM package versions were changed - Updates documentation Resolves #314 {major} Signed-off-by: Esta Nagy <[email protected]>
1 parent 1db37ff commit 90b2cae

File tree

181 files changed

+39109
-4535
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+39109
-4535
lines changed

.github/workflows/gradle-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ name: JavaCI-PR
55

66
on:
77
push:
8-
branches: [ main ]
8+
branches: [ main]
99
paths:
1010
- 'gradle/libs.versions.toml'
1111
- 'gradle/verification-metadata.xml'
@@ -134,7 +134,7 @@ jobs:
134134
if: ${{ matrix.os == 'ubuntu-latest' }}
135135
with:
136136
token: ${{ secrets.CODECOV_TOKEN }}
137-
file: ./mission-report/flight-evaluation-report/build/reports/jacoco/report.xml
137+
files: ./mission-report/flight-evaluation-report/build/reports/jacoco/report.xml,./mission-report/flight-evaluation-report/node/build/coverage/cobertura-coverage.xml
138138
flags: flighteval
139139
- name: Upload coverage to Codecov - Strongback - Base
140140
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4

.github/workflows/gradle.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ on:
2828
- 'gradle/wrapper/gradle-wrapper.properties'
2929
- 'config/ossindex/exclusions.txt'
3030
- 'gradle/verification-metadata-clean.xml'
31+
- 'mission-report/flight-evaluation-report/node/package.json'
32+
- 'mission-report/flight-evaluation-report/node/package-lock.json'
3133

3234
permissions: read-all
3335

@@ -114,7 +116,7 @@ jobs:
114116
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
115117
with:
116118
token: ${{ secrets.CODECOV_TOKEN }}
117-
file: ./mission-report/flight-evaluation-report/build/reports/jacoco/report.xml
119+
files: ./mission-report/flight-evaluation-report/build/reports/jacoco/report.xml,./mission-report/flight-evaluation-report/node/build/coverage/cobertura-coverage.xml
118120
flags: flighteval
119121
- name: Upload coverage to Codecov - Strongback - Base
120122
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ out/
6262

6363
### VS Code ###
6464
*/.vscode/
65+
66+
### Node modules ###
67+
mission-report/flight-evaluation-report/node/node_modules/
68+

boosters/booster-cucumber-jvm/src/main/java/com/github/nagyesta/abortmission/booster/cucumber/CucumberLaunchSequenceTemplate.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ public CucumberLaunchSequenceTemplate(final Runnable abortSequence,
4545
public Optional<StageTimeStopwatch> launchImminent(final Scenario scenario) {
4646
LOGGER.debug("Preparing mission for scenario from URI: {} named: {}", scenario.getUri(), scenario.getName());
4747
final Set<MissionHealthCheckEvaluator> evaluators = scenarioBasedEvaluatorLookup.apply(scenario);
48-
final StageTimeStopwatch countdownStopwatch = new StageTimeStopwatch(scenario.getUri().toString(), StageTimeMeasurement.CLASS_ONLY);
48+
final StageTimeStopwatch countdownStopwatch = new StageTimeStopwatch(scenario.getUri().toString(), StageTimeMeasurement.CLASS_ONLY)
49+
.overrideDisplayName(scenario.getName() + " (" + scenario.getUri() + ":" + scenario.getLine() + ")");
4950
evaluators.forEach(e -> e.countdownLogger().logAndIncrement(countdownStopwatch.stop().apply(StageResult.SUCCESS)));
5051
return evaluateLaunchAbort(evaluators,
51-
new StageTimeStopwatch(scenario.getUri().toString(), scenario.getName()),
52+
new StageTimeStopwatch(scenario.getUri().toString(), scenario.getName())
53+
.overrideDisplayName(scenario.getName() + " (" + scenario.getUri() + ":" + scenario.getLine() + ")"),
5254
() -> scenario.getSourceTagNames().stream().anyMatch("AbortMission_SuppressAbort"::equalsIgnoreCase)
5355
);
5456
}

boosters/booster-cucumber-jvm/src/main/java/com/github/nagyesta/abortmission/booster/cucumber/LaunchAbortHook.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.github.nagyesta.abortmission.booster.cucumber.matcher.ScenarioNameMatcher;
44
import com.github.nagyesta.abortmission.booster.cucumber.matcher.ScenarioUriMatcher;
55
import com.github.nagyesta.abortmission.core.AbortMissionCommandOps;
6+
import com.github.nagyesta.abortmission.core.AbortMissionGlobalConfiguration;
67
import com.github.nagyesta.abortmission.core.MissionControl;
78
import com.github.nagyesta.abortmission.core.healthcheck.MissionHealthCheckEvaluator;
89
import com.github.nagyesta.abortmission.core.matcher.MissionHealthCheckMatcher;
@@ -106,6 +107,7 @@ public static MissionHealthCheckMatcher scenarioNameMatcher(final String regex)
106107
*/
107108
protected void doBeforeScenario(final Scenario scenario) {
108109
missionOutline.initialBriefing();
110+
overrideGlobalConfig(MissionControl.globalConfiguration());
109111
storeOptionalStopwatch(scenario, launchSequenceTemplate.launchImminent(scenario));
110112
}
111113

@@ -122,6 +124,15 @@ protected void doAfterScenario(final Scenario scenario) {
122124
}
123125
}
124126

127+
/**
128+
* This method should be overridden if you want to provide custom global configuration.
129+
*
130+
* @param config The global configuration.
131+
*/
132+
protected void overrideGlobalConfig(final AbortMissionGlobalConfiguration config) {
133+
//noop
134+
}
135+
125136
@SuppressWarnings("unchecked")
126137
private Optional<Throwable> getThrowable(final Scenario scenario) {
127138
try {

boosters/booster-cucumber-jvm/src/test/java/com/github/nagyesta/abortmission/booster/cucumber/FuelTankTest.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,30 @@
22

33
import com.github.nagyesta.abortmission.booster.cucumber.fueltank.FuelTankTestContext;
44
import com.github.nagyesta.abortmission.core.MissionControl;
5+
import org.junit.jupiter.api.Assertions;
56
import org.junit.jupiter.api.Tag;
67
import org.junit.jupiter.api.Test;
78
import org.junit.platform.testkit.engine.EngineTestKit;
89

10+
import java.util.List;
11+
import java.util.stream.Collectors;
12+
import java.util.stream.IntStream;
13+
14+
import static com.github.nagyesta.abortmission.testkit.LaunchEvaluationUtil.*;
915
import static com.github.nagyesta.abortmission.testkit.vanilla.FuelTankTestAssets.*;
1016
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
1118
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
1219

1320
public class FuelTankTest {
1421

22+
private static final int FIRST_LINE_OF_EXAMPLES = 14;
23+
private static final int LAST_LINE_OF_EXAMPLES = 18;
24+
private static final String RESOURCE = "classpath:com/github/nagyesta/abortmission/booster/cucumber/fueltank/FuelTank.feature";
25+
private static final List<String> EXPECTED_DISPLAY_NAMES = IntStream.rangeClosed(FIRST_LINE_OF_EXAMPLES, LAST_LINE_OF_EXAMPLES)
26+
.mapToObj(i -> "Fuel_1 Fuel tank should fill (" + RESOURCE + ":" + i + ")")
27+
.collect(Collectors.toList());
28+
1529
@Test
1630
@Tag("integration")
1731
@SuppressWarnings("checkstyle:MagicNumber")
@@ -28,8 +42,18 @@ public void testAssumption() {
2842
.aborted(ABORTED_CASES)
2943
.failed(FAILED_CASES));
3044
MissionControl.commandOps(CONTEXT_NAME).allEvaluators()
31-
.forEach(evaluator -> assertEquals(FUEL_TANK_NOMINAL_STATS.getReadOnlyMission().getSnapshot(),
32-
evaluator.getStats().getReadOnlyMission().getSnapshot()));
45+
.forEach(evaluator -> {
46+
assertEquals(FUEL_TANK_NOMINAL_STATS.getReadOnlyMission().getSnapshot(),
47+
evaluator.getStats().getReadOnlyMission().getSnapshot());
48+
//check display names
49+
assertIterableEquals(EXPECTED_DISPLAY_NAMES, findCountdownDisplayNamesForMeasurementsOf(evaluator));
50+
assertIterableEquals(EXPECTED_DISPLAY_NAMES, findMissionDisplayNamesForMeasurementsOf(evaluator));
51+
//check exception details
52+
assertIterableEquals(EXPECTED_EXCEPTIONS, findExceptionsForMissionFailuresOf(evaluator));
53+
assertIterableEquals(EXPECTED_MESSAGES, findThrowableMessagesForMissionFailuresOf(evaluator));
54+
forEachNonFilteredStackTraceElementOfMissionFailures(evaluator,
55+
e -> Assertions.assertTrue(e.startsWith("com.github.nagyesta"), "Unexpected stack trace: " + e));
56+
});
3357
}
3458

3559
}

boosters/booster-cucumber-jvm/src/test/java/com/github/nagyesta/abortmission/booster/cucumber/ParachuteDropTest.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,31 @@
22

33
import com.github.nagyesta.abortmission.booster.cucumber.parachute.ParachuteTestContext;
44
import com.github.nagyesta.abortmission.core.MissionControl;
5+
import org.junit.jupiter.api.Assertions;
56
import org.junit.jupiter.api.Tag;
67
import org.junit.jupiter.api.Test;
78
import org.junit.platform.testkit.engine.EngineTestKit;
89

10+
import java.util.List;
11+
import java.util.stream.Collectors;
12+
import java.util.stream.IntStream;
13+
14+
import static com.github.nagyesta.abortmission.testkit.LaunchEvaluationUtil.*;
915
import static com.github.nagyesta.abortmission.testkit.vanilla.ParachuteDropTestAssets.*;
1016
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
1118
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
1219

1320
public class ParachuteDropTest {
1421

22+
private static final int FIRST_LINE_OF_EXAMPLES = 10;
23+
private static final int LAST_LINE_OF_EXAMPLES = 19;
24+
private static final String RESOURCE_NAME =
25+
"classpath:com/github/nagyesta/abortmission/booster/cucumber/parachute/ParachuteDrop.feature";
26+
private static final List<String> EXPECTED_DISPLAY_NAMES = IntStream.rangeClosed(FIRST_LINE_OF_EXAMPLES, LAST_LINE_OF_EXAMPLES)
27+
.mapToObj(i -> "Parachute_1 Parachutes should open (" + RESOURCE_NAME + ":" + i + ")")
28+
.collect(Collectors.toList());
29+
1530
@Test
1631
@Tag("integration")
1732
@SuppressWarnings("checkstyle:MagicNumber")
@@ -28,8 +43,18 @@ public void testAssumption() {
2843
.aborted(ABORTED_CASES)
2944
.failed(FAILED_CASES));
3045
MissionControl.commandOps().allEvaluators()
31-
.forEach(evaluator -> assertEquals(PARACHUTE_NOMINAL_STATS.getReadOnlyMission().getSnapshot(),
32-
evaluator.getStats().getReadOnlyMission().getSnapshot()));
46+
.forEach(evaluator -> {
47+
assertEquals(PARACHUTE_NOMINAL_STATS.getReadOnlyMission().getSnapshot(),
48+
evaluator.getStats().getReadOnlyMission().getSnapshot());
49+
//check display names
50+
assertIterableEquals(EXPECTED_DISPLAY_NAMES, findCountdownDisplayNamesForMeasurementsOf(evaluator));
51+
assertIterableEquals(EXPECTED_DISPLAY_NAMES, findMissionDisplayNamesForMeasurementsOf(evaluator));
52+
//check exception details
53+
assertIterableEquals(EXPECTED_EXCEPTIONS, findExceptionsForMissionFailuresOf(evaluator));
54+
assertIterableEquals(EXPECTED_MESSAGES, findThrowableMessagesForMissionFailuresOf(evaluator));
55+
forEachNonFilteredStackTraceElementOfMissionFailures(evaluator,
56+
e -> Assertions.assertTrue(e.startsWith("com.github.nagyesta"), "Unexpected stack trace: " + e));
57+
});
3358
}
3459

3560
}

boosters/booster-cucumber-jvm/src/test/java/com/github/nagyesta/abortmission/booster/cucumber/StaticFireBoosterTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,51 @@
11
package com.github.nagyesta.abortmission.booster.cucumber;
22

33
import com.github.nagyesta.abortmission.booster.cucumber.staticfire.StaticFireTestContext;
4+
import com.github.nagyesta.abortmission.core.AbortMissionGlobalConfiguration;
45
import com.github.nagyesta.abortmission.core.MissionControl;
6+
import com.github.nagyesta.abortmission.core.telemetry.StageResult;
7+
import com.github.nagyesta.abortmission.core.telemetry.StageTimeMeasurement;
8+
import org.junit.jupiter.api.Assertions;
59
import org.junit.jupiter.api.Tag;
610
import org.junit.jupiter.api.Test;
711
import org.junit.platform.testkit.engine.EngineTestKit;
812

13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.function.Function;
16+
import java.util.stream.Collectors;
17+
import java.util.stream.IntStream;
18+
import java.util.stream.Stream;
19+
20+
import static com.github.nagyesta.abortmission.testkit.LaunchEvaluationUtil.*;
921
import static com.github.nagyesta.abortmission.testkit.spring.StaticFireTestAssets.*;
1022
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
1124
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
1225

1326
public class StaticFireBoosterTest {
1427

28+
private static final String CENTER_RESOURCE =
29+
"classpath:com/github/nagyesta/abortmission/booster/cucumber/staticfire/StaticFireCenterCoreOnly.feature";
30+
private static final String HEAVY_RESOURCE =
31+
"classpath:com/github/nagyesta/abortmission/booster/cucumber/staticfire/StaticFireHeavy.feature";
32+
private static final int FIRST_LINE_OF_EXAMPLES_SIDE = 12;
33+
private static final int LAST_LINE_OF_EXAMPLES_SIDE = 511;
34+
private static final List<String> EXPECTED_CENTER_DISPLAY_NAMES = List
35+
.of("StaticFire_1 Center core static fire is burning (" + CENTER_RESOURCE + ":10)");
36+
private static final List<String> EXPECTED_SIDE_DISPLAY_NAMES = Stream.of(
37+
IntStream.rangeClosed(FIRST_LINE_OF_EXAMPLES_SIDE, LAST_LINE_OF_EXAMPLES_SIDE)
38+
.mapToObj(i -> "StaticFire_3 Side booster static fire is burning (" + HEAVY_RESOURCE + ":" + i + ")"),
39+
Stream.of("StaticFire_4 Center core and side booster static fire is burning (" + HEAVY_RESOURCE + ":515)")
40+
)
41+
.flatMap(Function.identity())
42+
.collect(Collectors.toList());
43+
1544
@Test
1645
@Tag("integration")
1746
@SuppressWarnings("checkstyle:MagicNumber")
1847
public void testAssumption() {
48+
AbortMissionGlobalConfiguration.shared().setStackTraceFilter(e -> !e.getClassName().contains("org.springframework"));
1949
EngineTestKit
2050
.engine("junit-vintage")
2151
.selectors(selectClass(StaticFireTestContext.class))
@@ -31,10 +61,38 @@ public void testAssumption() {
3161
if (evaluator.getMatcher().getName().contains(CENTER_CORE)) {
3262
assertEquals(CENTER_CORE_NOMINAL_STATS.getReadOnlyMission().getSnapshot(),
3363
evaluator.getStats().getReadOnlyMission().getSnapshot());
64+
//check display names
65+
final List<String> actualCountdownNames = evaluator.getStats().getReadOnlyCountdown().timeSeriesStream()
66+
.map(StageTimeMeasurement::getDisplayName)
67+
.collect(Collectors.toList());
68+
assertIterableEquals(EXPECTED_CENTER_DISPLAY_NAMES, actualCountdownNames);
69+
final List<String> actualMissionNames = evaluator.getStats().getReadOnlyMission().timeSeriesStream()
70+
.map(StageTimeMeasurement::getDisplayName)
71+
.collect(Collectors.toList());
72+
assertIterableEquals(EXPECTED_CENTER_DISPLAY_NAMES, actualMissionNames);
73+
//check exception details
74+
final List<String> actualExceptions = evaluator.getStats().getReadOnlyMission().timeSeriesStream()
75+
.filter(s -> s.getResult() == StageResult.FAILURE)
76+
.map(StageTimeMeasurement::getThrowableClass)
77+
.collect(Collectors.toList());
78+
final List<String> actualMessages = evaluator.getStats().getReadOnlyMission().timeSeriesStream()
79+
.filter(s -> s.getResult() == StageResult.FAILURE)
80+
.map(StageTimeMeasurement::getThrowableMessage)
81+
.collect(Collectors.toList());
82+
assertIterableEquals(Collections.emptyList(), actualExceptions);
83+
assertIterableEquals(Collections.emptyList(), actualMessages);
3484
} else {
3585
//As there is no countdown abort here, we need to compare the regular countdown with our mission stats
3686
assertEquals(SIDE_BOOSTER_NOMINAL_STATS.getReadOnlyCountdown().getSnapshot(),
3787
evaluator.getStats().getReadOnlyMission().getSnapshot());
88+
//check display names
89+
assertIterableEquals(EXPECTED_SIDE_DISPLAY_NAMES, findCountdownDisplayNamesForMeasurementsOf(evaluator));
90+
assertIterableEquals(EXPECTED_SIDE_DISPLAY_NAMES, findMissionDisplayNamesForMeasurementsOf(evaluator));
91+
//check exception details
92+
assertIterableEquals(EXPECTED_EXCEPTIONS, findExceptionsForMissionFailuresOf(evaluator));
93+
assertIterableEquals(EXPECTED_MESSAGES, findThrowableMessagesForMissionFailuresOf(evaluator));
94+
forEachNonFilteredStackTraceElementOfMissionFailures(evaluator,
95+
e -> Assertions.assertTrue(e.startsWith("com.github.nagyesta"), "Unexpected stack trace: " + e));
3896
}
3997
});
4098
}

boosters/booster-cucumber-jvm/src/test/java/com/github/nagyesta/abortmission/booster/cucumber/fueltank/AbortMissionHook.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.github.nagyesta.abortmission.booster.cucumber.LaunchAbortHook;
44
import com.github.nagyesta.abortmission.core.AbortMissionCommandOps;
5+
import com.github.nagyesta.abortmission.core.AbortMissionGlobalConfiguration;
56
import io.cucumber.java.After;
67
import io.cucumber.java.Before;
78
import io.cucumber.java.Scenario;
@@ -25,6 +26,11 @@ protected Map<String, Consumer<AbortMissionCommandOps>> defineOutline() {
2526
});
2627
}
2728

29+
@Override
30+
protected void overrideGlobalConfig(final AbortMissionGlobalConfiguration config) {
31+
config.setStackTraceFilter(stackTraceElement -> stackTraceElement.getClassName().startsWith("com.github."));
32+
}
33+
2834
@Before
2935
@Override
3036
public void beforeScenario(final Scenario scenario) {

boosters/booster-cucumber-jvm/src/test/java/com/github/nagyesta/abortmission/booster/cucumber/fueltank/FuelTankStepDefs.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ public void amountIsLoadedIntoTheTank(final int amount) {
2424

2525
@Then("Fuel tank is intact")
2626
public void fuelTankIsIntact() {
27-
Assertions.assertTrue(success);
27+
Assertions.assertTrue(success, "Fuel tank should fill successfully.");
2828
}
2929
}

0 commit comments

Comments
 (0)