Skip to content

Commit ea44303

Browse files
authored
improve test evidence coverage (via #1291)
1 parent 0fbe494 commit ea44303

75 files changed

Lines changed: 1789 additions & 1242 deletions

File tree

Some content is hidden

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

AGENTS.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
# Project Guide
22

3-
Use [Allure Agent Mode](docs/allure-agent-mode.md) for all test-related work in this repository.
3+
Never create pull requests or push git branches without explicit confirmation from the user.
44

5-
- Read `docs/allure-agent-mode.md` before designing, writing, reviewing, validating, debugging, or enriching tests.
6-
- Run test-executing commands through `allure run`, including smoke checks after small edits.
7-
- Use `./gradlew` for repo-local test commands and scope runs to the smallest relevant module or task.
8-
- If agent-mode output is missing or incomplete, debug that first rather than relying on console-only conclusions.
5+
## Test Work
6+
7+
Use [Allure Test Agent](docs/allure-test-agent.md) for test-related work in this repository.
8+
9+
- Read `docs/allure-test-agent.md` before designing, writing, reviewing, validating, debugging, or enriching tests.
10+
- Use the `$allure-test-agent` skill as the durable behavior guide when it is installed; this project file contains local commands and conventions.
11+
- If a command executes tests and its result will be used for smoke checking, reasoning, review, coverage analysis, debugging, or a user-facing conclusion, run it through `allure agent`.
12+
- Use agent-mode execution for smoke checks too, even when the change is small or mechanical.
13+
- If agent output is missing or incomplete, debug that first and treat console-only conclusions as provisional.
14+
15+
## Validation
16+
17+
After making changes, run the applicable validation checks before reporting done.
18+
19+
- For Java, Groovy, or Scala production/test changes, run the relevant module-scoped quality checks when practical: `:<module>:spotlessCheck`, `:<module>:checkstyleMain`, `:<module>:pmdMain`, and `:<module>:spotbugsMain`.
20+
- For shared test-support, build logic, root configuration, or broad cross-module changes, run the aggregate quality command: `./gradlew --no-daemon spotlessCheck checkstyleMain pmdMain spotbugsMain`.
21+
- If Spotless reports formatting issues, run the matching `spotlessApply` task, such as `:<module>:spotlessApply` or `./gradlew --no-daemon spotlessApply`, then rerun `spotlessCheck`.
22+
- If a quality check fails, fix the smallest relevant issue and rerun the failed check or the original quality command until it passes.
23+
- For docs-only changes, run `git diff --check` and any configured documentation lint before reporting completion.
24+
- If validation cannot be run, state exactly which checks were skipped and why.

allure-awaitility/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
compileOnly("org.awaitility:awaitility:$awaitilityVersion")
1111
testImplementation("jakarta.annotation:jakarta.annotation-api")
1212
testImplementation("org.assertj:assertj-core")
13+
testImplementation(project(":allure-assertj"))
1314
testImplementation("org.awaitility:awaitility:$awaitilityVersion")
1415
testImplementation("org.junit.jupiter:junit-jupiter-api")
1516
testImplementation("org.slf4j:slf4j-simple")

allure-awaitility/src/test/java/io/qameta/allure/awaitility/ConditionListenersPositiveTest.java

Lines changed: 185 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -15,167 +15,242 @@
1515
*/
1616
package io.qameta.allure.awaitility;
1717

18+
import io.qameta.allure.Description;
1819
import io.qameta.allure.model.Status;
1920
import io.qameta.allure.model.StepResult;
2021
import io.qameta.allure.model.TestResult;
2122
import org.awaitility.Awaitility;
2223
import org.junit.jupiter.api.BeforeAll;
23-
import org.junit.jupiter.api.DynamicNode;
24-
import org.junit.jupiter.api.DynamicTest;
2524
import org.junit.jupiter.api.Test;
26-
import org.junit.jupiter.api.TestFactory;
2725

2826
import java.time.Duration;
2927
import java.time.temporal.ChronoUnit;
3028
import java.util.List;
3129
import java.util.concurrent.atomic.AtomicInteger;
32-
import java.util.stream.Stream;
3330

3431
import static io.qameta.allure.test.RunUtils.runWithinTestContext;
3532
import static org.assertj.core.api.Assertions.assertThat;
3633
import static org.awaitility.Awaitility.await;
37-
import static org.hamcrest.Matchers.is;
38-
import static org.junit.jupiter.api.Assertions.assertEquals;
39-
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
4034

4135
class ConditionListenersPositiveTest {
4236

37+
private static final String AWAITILITY_EVALUATION_DESCRIPTION = "Awaitility condition satisfied or not, but awaiting still in progress";
38+
4339
@BeforeAll
4440
static void setup() {
4541
Awaitility.pollInSameThread();
4642
}
4743

4844
/**
49-
* Positive test to check proper allure steps generation.
50-
* <p>
51-
* Precondition: listener into condition declaration, await without alias
52-
* <p>
53-
* Test should check that:
54-
* <li>1. Allure has exactly 1 top-level step for 1 await condition</li>
55-
* <li>2. Top level step has passed status</li>
56-
* <li>3. Top level step has default name</li>
45+
* Verifies that a successful Awaitility condition with an inline Allure listener is reported as one top-level step
46+
* when no await alias is provided.
5747
*/
58-
@TestFactory
59-
Stream<DynamicNode> globalSettingsAwaitWoAliasCheckTopLevelPassedStep() {
60-
final List<TestResult> testResult = runWithinTestContext(() -> {
61-
final AtomicInteger atomicInteger = new AtomicInteger(0);
62-
await().with()
63-
.conditionEvaluationListener(new AllureAwaitilityListener())
64-
.atMost(Duration.of(1000, ChronoUnit.MILLIS))
65-
.pollInterval(Duration.of(50, ChronoUnit.MILLIS))
66-
.until(atomicInteger::getAndIncrement, is(3));
67-
},
68-
AllureAwaitilityListener::setLifecycle
69-
).getTestResults();
48+
@Description
49+
@Test
50+
void awaitWithoutAliasShouldCreateSingleTopLevelStep() {
51+
final List<StepResult> steps = runAwaitWithoutAliasTopLevelSteps();
52+
53+
assertThat(steps)
54+
.as("Exactly 1 top level step for 1 awaitility condition")
55+
.hasSize(1);
56+
}
57+
58+
/**
59+
* Verifies that the top-level report step for a successful Awaitility condition with an inline listener is marked
60+
* passed.
61+
*/
62+
@Description
63+
@Test
64+
void awaitWithoutAliasShouldMarkTopLevelStepPassed() {
65+
final StepResult step = awaitWithoutAliasTopLevelStep();
66+
67+
assertThat(step.getStatus())
68+
.as("Top level step has passed status")
69+
.isEqualTo(Status.PASSED);
70+
}
71+
72+
/**
73+
* Verifies that a successful Awaitility condition without an alias keeps the default top-level step name in the
74+
* Allure report.
75+
*/
76+
@Description
77+
@Test
78+
void awaitWithoutAliasShouldUseDefaultTopLevelStepName() {
79+
final StepResult step = awaitWithoutAliasTopLevelStep();
7080

71-
return Stream.of(
72-
DynamicTest.dynamicTest(
73-
"Exactly 1 top level step for 1 awaitility condition", () -> assertThat(testResult.get(0).getSteps())
74-
.hasSize(1)
75-
),
76-
DynamicTest.dynamicTest(
77-
"Top level step has passed status", () -> assertThat(testResult.get(0).getSteps())
78-
.allMatch(step -> Status.PASSED.equals(step.getStatus()))
79-
),
80-
DynamicTest.dynamicTest(
81-
"Top level step has default name because await() wo alias", () -> assertThat(testResult.get(0).getSteps())
82-
.extracting(StepResult::getName)
83-
.containsExactly("Awaitility: Starting evaluation")
84-
)
85-
);
81+
assertThat(step.getName())
82+
.as("Top level step has default name because await() wo alias")
83+
.isEqualTo("Awaitility: Starting evaluation");
8684
}
8785

8886
/**
89-
* Positive test to check proper allure steps generation.
90-
* <p>
91-
* Precondition: listener into condition declaration, await with alias
92-
* <p>
93-
* Test should check that:
94-
* <li>1. Top level step has name with alias from await('alias')</li>
87+
* Verifies that an Awaitility alias supplied on a condition with an inline Allure listener is visible in the
88+
* top-level report step name.
9589
*/
90+
@Description
9691
@Test
9792
void globalSettingsAwaitWithAliasCheckTopLevelPassedStep() {
93+
final StepResult step = awaitWithAliasTopLevelStep();
94+
95+
assertThat(step.getName())
96+
.as("Top level step has name with alias")
97+
.isEqualTo("Awaitility: Counter should be at least 3");
98+
}
99+
100+
/**
101+
* Verifies that report consumers can see one condition-evaluation child step for each poll performed before a
102+
* successful Awaitility condition completes.
103+
*/
104+
@Description
105+
@Test
106+
void awaitWithoutAliasShouldCreateSecondLevelStepForEachPoll() {
107+
final List<StepResult> steps = awaitWithoutAliasPollSteps();
108+
109+
assertThat(steps)
110+
.as("Exactly 4 second level steps for 4 polling iterations")
111+
.hasSize(4);
112+
}
113+
114+
/**
115+
* Verifies that all poll-level report steps for a successfully completed Awaitility condition are marked passed.
116+
*/
117+
@Description
118+
@Test
119+
void awaitWithoutAliasShouldMarkAllSecondLevelStepsPassed() {
120+
final List<StepResult> steps = awaitWithoutAliasPollSteps();
121+
122+
assertThat(steps)
123+
.as("All second level steps has passed statuses")
124+
.allMatch(x -> x.getStatus().equals(Status.PASSED));
125+
}
126+
127+
/**
128+
* Verifies that the first failed poll records the evaluated condition, expected value, actual value, and timing
129+
* context in the report.
130+
*/
131+
@Description
132+
@Test
133+
void awaitWithoutAliasShouldDescribeFirstFailedPoll() {
134+
final StepResult step = firstFailedPollStep();
135+
136+
assertThat(step.getName())
137+
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
138+
.contains("expected: 3")
139+
.contains("but was: 0")
140+
.contains("elapsed time")
141+
.contains("remaining time")
142+
.contains("last poll interval was");
143+
}
144+
145+
/**
146+
* Verifies that the second failed poll records the next observed value together with the evaluated condition and
147+
* timing context.
148+
*/
149+
@Description
150+
@Test
151+
void awaitWithoutAliasShouldDescribeSecondFailedPoll() {
152+
final StepResult step = secondFailedPollStep();
153+
154+
assertThat(step.getName())
155+
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
156+
.contains("expected: 3")
157+
.contains("but was: 1")
158+
.contains("elapsed time")
159+
.contains("remaining time")
160+
.contains("last poll interval was");
161+
}
162+
163+
/**
164+
* Verifies that the third failed poll records the final unsuccessful value before the Awaitility condition succeeds.
165+
*/
166+
@Description
167+
@Test
168+
void awaitWithoutAliasShouldDescribeThirdFailedPoll() {
169+
final StepResult step = thirdFailedPollStep();
170+
171+
assertThat(step.getName())
172+
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
173+
.contains("expected: 3")
174+
.contains("but was: 2")
175+
.contains("elapsed time")
176+
.contains("remaining time")
177+
.contains("last poll interval was");
178+
}
179+
180+
/**
181+
* Verifies that the successful poll reports the reached value and timing context so consumers can identify why the
182+
* wait completed.
183+
*/
184+
@Description
185+
@Test
186+
void awaitWithoutAliasShouldDescribeSuccessfulPoll() {
187+
final StepResult step = successfulPollStep();
188+
189+
assertThat(step.getName())
190+
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
191+
.contains("reached its end value after")
192+
.contains("remaining time")
193+
.contains("last poll interval was");
194+
}
195+
196+
private List<StepResult> runAwaitWithoutAliasTopLevelSteps() {
98197
final List<TestResult> testResult = runWithinTestContext(() -> {
99198
final AtomicInteger atomicInteger = new AtomicInteger(0);
100-
await("Counter should be at least 3").with()
199+
await().with()
101200
.conditionEvaluationListener(new AllureAwaitilityListener())
102201
.atMost(Duration.of(1000, ChronoUnit.MILLIS))
103202
.pollInterval(Duration.of(50, ChronoUnit.MILLIS))
104-
.until(atomicInteger::getAndIncrement, is(3));
203+
.untilAsserted(() -> assertThat(atomicInteger.getAndIncrement()).isEqualTo(3));
105204
},
106205
AllureAwaitilityListener::setLifecycle
107206
).getTestResults();
108-
assertEquals(
109-
"Awaitility: Counter should be at least 3",
110-
testResult.get(0).getSteps().get(0).getName(),
111-
"Top level step has name with alias"
112-
);
207+
208+
return testResult.get(0).getSteps();
113209
}
114210

115-
/**
116-
* Positive test to check proper allure steps generation.
117-
* <p>
118-
* Precondition: listener into condition declaration, await without alias
119-
* <p>
120-
* Test should check that:
121-
* <li>1. Allure has exactly 4 second-level steps for condition with 4 polls iteration</li>
122-
* <li>2. All second-level steps should have passed status for successful condition evaluation</li>
123-
* <li>3. All second-level steps should have information about polling intervals and evaluation</li>
124-
*/
125-
@TestFactory
126-
Stream<DynamicNode> globalSettingsCheckAwaitWoAliasSecondLevelPassedSteps() {
211+
private StepResult awaitWithoutAliasTopLevelStep() {
212+
return runAwaitWithoutAliasTopLevelSteps().get(0);
213+
}
214+
215+
private StepResult awaitWithAliasTopLevelStep() {
127216
final List<TestResult> testResult = runWithinTestContext(() -> {
128217
final AtomicInteger atomicInteger = new AtomicInteger(0);
129-
await().with()
218+
await("Counter should be at least 3").with()
130219
.conditionEvaluationListener(new AllureAwaitilityListener())
131220
.atMost(Duration.of(1000, ChronoUnit.MILLIS))
132221
.pollInterval(Duration.of(50, ChronoUnit.MILLIS))
133-
.until(atomicInteger::getAndIncrement, is(3));
222+
.untilAsserted(() -> assertThat(atomicInteger.getAndIncrement()).isEqualTo(3));
134223
},
135224
AllureAwaitilityListener::setLifecycle
136225
).getTestResults();
137226

138-
return Stream.of(
139-
dynamicTest(
140-
"Exactly 4 second level steps for 4 polling iterations", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps())
141-
.hasSize(4)
142-
),
143-
dynamicTest(
144-
"All second level steps has passed statuses", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps())
145-
.allMatch(x -> x.getStatus().equals(Status.PASSED))
146-
),
147-
dynamicTest(
148-
"Second level step 1 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getName())
149-
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
150-
.contains("expected <3> but was <0>")
151-
.contains("elapsed time")
152-
.contains("remaining time")
153-
.contains("last poll interval was")
154-
),
155-
dynamicTest(
156-
"Second level step 2 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getName())
157-
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
158-
.contains("expected <3> but was <1>")
159-
.contains("elapsed time")
160-
.contains("remaining time")
161-
.contains("last poll interval was")
162-
),
163-
dynamicTest(
164-
"Second level step 3 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(2).getName())
165-
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
166-
.contains("expected <3> but was <2>")
167-
.contains("elapsed time")
168-
.contains("remaining time")
169-
.contains("last poll interval was")
170-
),
171-
dynamicTest(
172-
"Second level step 4 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(3).getName())
173-
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
174-
.contains("reached its end value of <3> after")
175-
.contains("remaining time")
176-
.contains("last poll interval was")
177-
)
178-
);
227+
return testResult.get(0).getSteps().get(0);
228+
}
229+
230+
private List<StepResult> awaitWithoutAliasPollSteps() {
231+
return awaitWithoutAliasTopLevelStep().getSteps().stream()
232+
.filter(ConditionListenersPositiveTest::isAwaitilityEvaluationStep)
233+
.toList();
234+
}
235+
236+
private static boolean isAwaitilityEvaluationStep(final StepResult step) {
237+
return AWAITILITY_EVALUATION_DESCRIPTION.equals(step.getDescription());
238+
}
239+
240+
private StepResult firstFailedPollStep() {
241+
return awaitWithoutAliasPollSteps().get(0);
242+
}
243+
244+
private StepResult secondFailedPollStep() {
245+
return awaitWithoutAliasPollSteps().get(1);
246+
}
247+
248+
private StepResult thirdFailedPollStep() {
249+
return awaitWithoutAliasPollSteps().get(2);
250+
}
251+
252+
private StepResult successfulPollStep() {
253+
return awaitWithoutAliasPollSteps().get(3);
179254
}
180255

181256
}

0 commit comments

Comments
 (0)