Skip to content

Commit c4c194e

Browse files
core: actual exception capture (#733)
1 parent ff07f48 commit c4c194e

10 files changed

Lines changed: 148 additions & 21 deletions

File tree

webtau-core/src/main/java/org/testingisdocumenting/webtau/documentation/CoreDocumentationAssertion.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright 2020 webtau maintainers
23
* Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +17,7 @@
1617

1718
package org.testingisdocumenting.webtau.documentation;
1819

19-
import org.testingisdocumenting.webtau.expectation.ActualPath;
20-
import org.testingisdocumenting.webtau.expectation.ExpectationHandler;
21-
import org.testingisdocumenting.webtau.expectation.ExpectedValuesAware;
22-
import org.testingisdocumenting.webtau.expectation.ValueMatcher;
20+
import org.testingisdocumenting.webtau.expectation.*;
2321

2422
import java.util.List;
2523
import java.util.stream.Collectors;
@@ -34,6 +32,12 @@ public void onValueMatch(ValueMatcher valueMatcher, ActualPath actualPath, Objec
3432
lastExpectedValues.set(extractExpectedValues(valueMatcher));
3533
}
3634

35+
@Override
36+
public void onCodeMatch(CodeMatcher codeMatcher) {
37+
lastActualValue.set(extractActualValue(codeMatcher));
38+
lastExpectedValues.set(extractExpectedValues(codeMatcher));
39+
}
40+
3741
Object actualValue() {
3842
return lastActualValue.get();
3943
}
@@ -42,12 +46,12 @@ Object expectedValue() {
4246
return lastExpectedValues.get();
4347
}
4448

45-
private static Object extractExpectedValues(ValueMatcher valueMatcher) {
46-
if (!(valueMatcher instanceof ExpectedValuesAware)) {
49+
private static Object extractExpectedValues(Object matcher) {
50+
if (!(matcher instanceof ExpectedValuesAware)) {
4751
return null;
4852
}
4953

50-
List<Object> list = ((ExpectedValuesAware) valueMatcher).expectedValues().collect(Collectors.toList());
54+
List<Object> list = ((ExpectedValuesAware) matcher).expectedValues().collect(Collectors.toList());
5155
if (list.isEmpty()) {
5256
return null;
5357
}
@@ -58,4 +62,12 @@ private static Object extractExpectedValues(ValueMatcher valueMatcher) {
5862

5963
return list.get(0);
6064
}
65+
66+
private static Object extractActualValue(CodeMatcher codeMatcher) {
67+
if (!(codeMatcher instanceof ActualValueAware)) {
68+
return null;
69+
}
70+
71+
return ((ActualValueAware) codeMatcher).actualValue();
72+
}
6173
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/documentation/CoreDocumentationAssertionValue.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright 2020 webtau maintainers
23
* Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,6 +33,6 @@ public class CoreDocumentationAssertionValue {
3233
* @param artifactName artifact name (file name without extension)
3334
*/
3435
public void capture(Class<?> testClass, String artifactName) {
35-
DocumentationArtifacts.createAsJson(testClass, artifactName, valueSupplier.get());
36+
DocumentationArtifacts.createTextOrJson(testClass, artifactName, valueSupplier.get());
3637
}
3738
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/documentation/DocumentationArtifacts.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ public static void create(Class<?> testClass, String artifactName, String text)
4545
FileUtils.writeTextContent(path, text);
4646
}
4747

48+
public static void createTextOrJson(Class<?> testClass, String artifactName, Object value) {
49+
if (value instanceof String) {
50+
Path path = DocumentationArtifactsLocation.classBasedLocation(testClass).resolve(artifactName + ".txt");
51+
FileUtils.writeTextContent(path, value.toString());
52+
} else {
53+
createAsJson(testClass, artifactName, value);
54+
}
55+
}
56+
4857
public static void createAsJson(Class<?> testClass, String artifactName, Object value) {
4958
artifactName += ".json";
5059

webtau-core/src/main/java/org/testingisdocumenting/webtau/expectation/ActualCode.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright 2020 webtau maintainers
23
* Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +18,7 @@
1718
package org.testingisdocumenting.webtau.expectation;
1819

1920
public class ActualCode implements ActualCodeExpectations {
20-
private CodeBlock actual;
21+
private final CodeBlock actual;
2122

2223
public ActualCode(CodeBlock actual) {
2324
this.actual = actual;
@@ -26,8 +27,23 @@ public ActualCode(CodeBlock actual) {
2627
@Override
2728
public void should(CodeMatcher codeMatcher) {
2829
boolean matches = codeMatcher.matches(actual);
29-
if (! matches) {
30-
throw new AssertionError("\n" + codeMatcher.mismatchedMessage(actual));
30+
31+
if (matches) {
32+
handleMatch(codeMatcher);
33+
} else {
34+
handleMismatch(codeMatcher, codeMatcher.mismatchedMessage(actual));
35+
}
36+
}
37+
38+
private void handleMatch(CodeMatcher codeMatcher) {
39+
ExpectationHandlers.onCodeMatch(codeMatcher);
40+
}
41+
42+
private void handleMismatch(CodeMatcher codeMatcher, String message) {
43+
final ExpectationHandler.Flow flow = ExpectationHandlers.onCodeMismatch(codeMatcher, message);
44+
45+
if (flow != ExpectationHandler.Flow.Terminate) {
46+
throw new AssertionError("\n" + message);
3147
}
3248
}
3349
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/expectation/ActualValue.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright 2020 webtau maintainers
23
* Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +25,7 @@
2425
import static org.testingisdocumenting.webtau.WebTauCore.createActualPath;
2526

2627
public class ActualValue implements ActualValueExpectations {
27-
private Object actual;
28+
private final Object actual;
2829

2930
public ActualValue(Object actual) {
3031
this.actual = actual;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2020 webtau maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.webtau.expectation;
18+
19+
public interface ActualValueAware {
20+
Object actualValue();
21+
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/expectation/ExpectationHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,10 @@ default Flow onValueMismatch(ValueMatcher valueMatcher, ActualPath actualPath, O
2727
}
2828

2929
default void onValueMatch(ValueMatcher valueMatcher, ActualPath actualPath, Object actualValue) {}
30+
31+
default Flow onCodeMismatch(CodeMatcher codeMatcher, String message) {
32+
return Flow.PassToNext;
33+
}
34+
35+
default void onCodeMatch(CodeMatcher codeMatcher) {}
3036
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/expectation/ExpectationHandlers.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright 2020 webtau maintainers
23
* Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,8 +26,8 @@
2526
import java.util.stream.Stream;
2627

2728
public class ExpectationHandlers {
28-
private static List<ExpectationHandler> globalHandlers = ServiceLoaderUtils.load(ExpectationHandler.class);
29-
private static ThreadLocal<List<ExpectationHandler>> localHandlers = ThreadLocal.withInitial(ArrayList::new);
29+
private static final List<ExpectationHandler> globalHandlers = ServiceLoaderUtils.load(ExpectationHandler.class);
30+
private static final ThreadLocal<List<ExpectationHandler>> localHandlers = ThreadLocal.withInitial(ArrayList::new);
3031

3132
public static void add(ExpectationHandler handler) {
3233
globalHandlers.add(handler);
@@ -60,6 +61,18 @@ public static Flow onValueMismatch(ValueMatcher valueMatcher, ActualPath actualP
6061
.findFirst().orElse(Flow.PassToNext);
6162
}
6263

64+
public static void onCodeMatch(CodeMatcher codeMatcher) {
65+
handlersStream().forEach(h -> h.onCodeMatch(codeMatcher));
66+
}
67+
68+
public static Flow onCodeMismatch(CodeMatcher codeMatcher, String message) {
69+
return handlersStream()
70+
.map(h -> h.onCodeMismatch(codeMatcher, message))
71+
.filter(flow -> flow == Flow.Terminate)
72+
.findFirst().orElse(Flow.PassToNext);
73+
}
74+
75+
6376
private static void addLocal(ExpectationHandler handler) {
6477
localHandlers.get().add(handler);
6578
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/expectation/code/ThrowExceptionMatcher.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright 2020 webtau maintainers
23
* Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,18 +19,21 @@
1819

1920
import static org.testingisdocumenting.webtau.WebTauCore.createActualPath;
2021

22+
import org.testingisdocumenting.webtau.expectation.ActualValueAware;
2123
import org.testingisdocumenting.webtau.expectation.CodeBlock;
2224
import org.testingisdocumenting.webtau.expectation.CodeMatcher;
25+
import org.testingisdocumenting.webtau.expectation.ExpectedValuesAware;
2326
import org.testingisdocumenting.webtau.expectation.equality.CompareToComparator;
2427
import java.lang.reflect.UndeclaredThrowableException;
2528
import java.util.regex.Pattern;
29+
import java.util.stream.Stream;
2630

27-
public class ThrowExceptionMatcher implements CodeMatcher {
31+
public class ThrowExceptionMatcher implements CodeMatcher, ExpectedValuesAware, ActualValueAware {
2832
private String expectedMessage;
2933
private Pattern expectedMessageRegexp;
30-
private Class expectedClass;
34+
private Class<?> expectedClass;
3135
private String thrownMessage;
32-
private Class thrownClass;
36+
private Class<?> thrownClass;
3337
private CompareToComparator comparator;
3438

3539
public ThrowExceptionMatcher(String expectedMessage) {
@@ -40,16 +44,16 @@ public ThrowExceptionMatcher(Pattern expectedMessageRegexp) {
4044
this.expectedMessageRegexp = expectedMessageRegexp;
4145
}
4246

43-
public ThrowExceptionMatcher(Class expectedClass) {
47+
public ThrowExceptionMatcher(Class<?> expectedClass) {
4448
this.expectedClass = expectedClass;
4549
}
4650

47-
public ThrowExceptionMatcher(Class expectedClass, Pattern expectedMessageRegexp) {
51+
public ThrowExceptionMatcher(Class<?> expectedClass, Pattern expectedMessageRegexp) {
4852
this.expectedClass = expectedClass;
4953
this.expectedMessageRegexp = expectedMessageRegexp;
5054
}
5155

52-
public ThrowExceptionMatcher(Class expectedClass, String expectedMessage) {
56+
public ThrowExceptionMatcher(Class<?> expectedClass, String expectedMessage) {
5357
this.expectedClass = expectedClass;
5458
this.expectedMessage = expectedMessage;
5559
}
@@ -112,4 +116,22 @@ private void extractExceptionDetails(Throwable t) {
112116
thrownClass = t.getClass();
113117
}
114118
}
119+
120+
@Override
121+
public Stream<Object> expectedValues() {
122+
if (expectedMessage != null) {
123+
return Stream.of(expectedMessage);
124+
}
125+
126+
if (expectedClass != null) {
127+
return Stream.of(expectedClass);
128+
}
129+
130+
return Stream.empty();
131+
}
132+
133+
@Override
134+
public Object actualValue() {
135+
return thrownMessage;
136+
}
115137
}

webtau-core/src/test/groovy/org/testingisdocumenting/webtau/documentation/CoreDocumentationAssertionTest.groovy

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,35 @@ class CoreDocumentationAssertionTest {
6464
'} ]')
6565
}
6666

67+
@Test
68+
void "should capture actual exception"() {
69+
code {
70+
throw new RuntimeException("actual full\nexception")
71+
} should throwException(~/actual/)
72+
73+
doc.actual.capture(CoreDocumentationAssertionTest, "actual-exception")
74+
validateArtifactContent("actual-exception", "txt",
75+
"actual full\nexception")
76+
}
77+
78+
@Test
79+
void "should capture expected exception"() {
80+
code {
81+
throw new RuntimeException("actual full\nexception")
82+
} should throwException(RuntimeException)
83+
84+
doc.expected.capture(CoreDocumentationAssertionTest, "expected-exception")
85+
validateArtifactContent("expected-exception", "json",
86+
'"java.lang.RuntimeException"')
87+
}
88+
6789
private static void validateArtifactContent(String artifactName, String expectedFileContent) {
90+
validateArtifactContent(artifactName, "json", expectedFileContent)
91+
}
92+
93+
private static void validateArtifactContent(String artifactName, String extension, String expectedFileContent) {
6894
def path = DocumentationArtifactsLocation.classBasedLocation(CoreDocumentationAssertionTest)
69-
.resolve(artifactName + ".json")
95+
.resolve(artifactName + "." + extension)
7096

7197
actual(FileUtils.fileTextContent(path)).should(equal(expectedFileContent))
7298
}

0 commit comments

Comments
 (0)