Skip to content

Commit b206754

Browse files
sbrannenmarcphilipp
andcommitted
Support @⁠Nullable reasons in ConditionEvaluationResult APIs
Closes #4698 Co-authored-by: Marc Philipp <[email protected]>
1 parent 495ef75 commit b206754

File tree

2 files changed

+133
-9
lines changed

2 files changed

+133
-9
lines changed

junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ConditionEvaluationResult.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Optional;
1616

1717
import org.apiguardian.api.API;
18+
import org.jspecify.annotations.Nullable;
1819
import org.junit.platform.commons.util.StringUtils;
1920
import org.junit.platform.commons.util.ToStringBuilder;
2021

@@ -29,34 +30,44 @@ public class ConditionEvaluationResult {
2930
/**
3031
* Factory for creating <em>enabled</em> results.
3132
*
32-
* @param reason the reason why the container or test should be enabled
33+
* @param reason the reason why the container or test should be enabled; may
34+
* be {@code null} if the reason is unknown
3335
* @return an enabled {@code ConditionEvaluationResult} with the given reason
36+
* or an <em>empty</em> reason
3437
*/
35-
public static ConditionEvaluationResult enabled(String reason) {
38+
public static ConditionEvaluationResult enabled(@Nullable String reason) {
3639
return new ConditionEvaluationResult(true, reason);
3740
}
3841

3942
/**
4043
* Factory for creating <em>disabled</em> results.
4144
*
42-
* @param reason the reason why the container or test should be disabled
45+
* @param reason the reason why the container or test should be disabled; may
46+
* be {@code null} if the reason is unknown
4347
* @return a disabled {@code ConditionEvaluationResult} with the given reason
48+
* or an <em>empty</em> reason
4449
*/
45-
public static ConditionEvaluationResult disabled(String reason) {
50+
public static ConditionEvaluationResult disabled(@Nullable String reason) {
4651
return new ConditionEvaluationResult(false, reason);
4752
}
4853

4954
/**
5055
* Factory for creating <em>disabled</em> results with custom reasons
5156
* added by the user.
5257
*
53-
* @param reason the default reason why the container or test should be disabled
54-
* @param customReason the custom reason why the container or test should be disabled
58+
* @param reason the default reason why the container or test should be disabled;
59+
* may be {@code null} if the default reason is unknown
60+
* @param customReason the custom reason why the container or test should be
61+
* disabled; may be {@code null} if the custom reason is unknown
5562
* @return a disabled {@code ConditionEvaluationResult} with the given reasons
63+
* or an <em>empty</em> reason
5664
* @since 5.7
5765
*/
5866
@API(status = STABLE, since = "5.7")
59-
public static ConditionEvaluationResult disabled(String reason, String customReason) {
67+
public static ConditionEvaluationResult disabled(@Nullable String reason, @Nullable String customReason) {
68+
if (StringUtils.isBlank(reason)) {
69+
return disabled(customReason);
70+
}
6071
if (StringUtils.isBlank(customReason)) {
6172
return disabled(reason);
6273
}
@@ -67,9 +78,9 @@ public static ConditionEvaluationResult disabled(String reason, String customRea
6778

6879
private final Optional<String> reason;
6980

70-
private ConditionEvaluationResult(boolean enabled, String reason) {
81+
private ConditionEvaluationResult(boolean enabled, @Nullable String reason) {
7182
this.enabled = enabled;
72-
this.reason = Optional.ofNullable(reason);
83+
this.reason = (reason == null || reason.isBlank()) ? Optional.empty() : Optional.of(reason.strip());
7384
}
7485

7586
/**
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright 2015-2025 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.jupiter.api.condition;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
import java.lang.annotation.Retention;
16+
import java.lang.annotation.RetentionPolicy;
17+
18+
import org.jspecify.annotations.Nullable;
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
21+
import org.junit.jupiter.params.ParameterizedTest;
22+
import org.junit.jupiter.params.provider.NullSource;
23+
import org.junit.jupiter.params.provider.ValueSource;
24+
25+
/**
26+
* Unit tests for {@link ConditionEvaluationResult}.
27+
*
28+
* @since 6.0
29+
*/
30+
class ConditionEvaluationResultTests {
31+
32+
@Test
33+
void enabledWithReason() {
34+
var result = ConditionEvaluationResult.enabled("reason");
35+
36+
assertThat(result.isDisabled()).isFalse();
37+
assertThat(result.getReason()).contains("reason");
38+
assertThat(result).asString().isEqualTo("ConditionEvaluationResult [enabled = true, reason = 'reason']");
39+
}
40+
41+
@EmptyReasonsTest
42+
void enabledWithoutReason(@Nullable String reason) {
43+
var result = ConditionEvaluationResult.enabled(reason);
44+
45+
assertThat(result.isDisabled()).isFalse();
46+
assertThat(result.getReason()).isEmpty();
47+
assertThat(result).asString().isEqualTo("ConditionEvaluationResult [enabled = true, reason = '<unknown>']");
48+
}
49+
50+
@Test
51+
void disabledWithDefaultReason() {
52+
var result = ConditionEvaluationResult.disabled("default");
53+
54+
assertThat(result.isDisabled()).isTrue();
55+
assertThat(result.getReason()).contains("default");
56+
assertThat(result).asString().isEqualTo("ConditionEvaluationResult [enabled = false, reason = 'default']");
57+
}
58+
59+
@EmptyReasonsTest
60+
void disabledWithoutDefaultReason(@Nullable String reason) {
61+
var result = ConditionEvaluationResult.disabled(reason);
62+
63+
assertThat(result.isDisabled()).isTrue();
64+
assertThat(result.getReason()).isEmpty();
65+
assertThat(result).asString().isEqualTo("ConditionEvaluationResult [enabled = false, reason = '<unknown>']");
66+
}
67+
68+
@EmptyReasonsTest
69+
void disabledWithDefaultReasonButWithoutCustomReason(@Nullable String customReason) {
70+
var result = ConditionEvaluationResult.disabled("default", customReason);
71+
72+
assertThat(result.isDisabled()).isTrue();
73+
assertThat(result.getReason()).contains("default");
74+
assertThat(result).asString().isEqualTo("ConditionEvaluationResult [enabled = false, reason = 'default']");
75+
}
76+
77+
@EmptyReasonsTest
78+
void disabledWithoutDefaultReasonButWithCustomReason(@Nullable String reason) {
79+
var result = ConditionEvaluationResult.disabled(reason, "custom");
80+
81+
assertThat(result.isDisabled()).isTrue();
82+
assertThat(result.getReason()).contains("custom");
83+
assertThat(result).asString().isEqualTo("ConditionEvaluationResult [enabled = false, reason = 'custom']");
84+
}
85+
86+
@EmptyReasonsTest
87+
void disabledWithoutDefaultReasonOrCustomReason(@Nullable String reason) {
88+
// We intentionally use the reason as both the default and custom reason.
89+
var result = ConditionEvaluationResult.disabled(reason, reason);
90+
91+
assertThat(result.isDisabled()).isTrue();
92+
assertThat(result.getReason()).isEmpty();
93+
assertThat(result).asString().isEqualTo("ConditionEvaluationResult [enabled = false, reason = '<unknown>']");
94+
}
95+
96+
@Test
97+
void disabledWithDefaultReasonAndCustomReason() {
98+
var result = ConditionEvaluationResult.disabled("default", "custom");
99+
100+
assertThat(result.isDisabled()).isTrue();
101+
assertThat(result.getReason()).contains("default ==> custom");
102+
assertThat(result).asString().isEqualTo(
103+
"ConditionEvaluationResult [enabled = false, reason = 'default ==> custom']");
104+
}
105+
106+
@Retention(RetentionPolicy.RUNTIME)
107+
@ParameterizedTest
108+
@NullSource
109+
@ValueSource(strings = { "", " ", " ", "\t", "\n" })
110+
@interface EmptyReasonsTest {
111+
}
112+
113+
}

0 commit comments

Comments
 (0)