Skip to content

Commit 00ebe83

Browse files
committed
fix(java-dsl): Print warning if the verifier hasn't been closed at the time the shutdown hook for the core process is triggered
1 parent 7a6c410 commit 00ebe83

8 files changed

Lines changed: 539 additions & 44 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
{
2+
"contractType": "case::contract",
3+
"description": {
4+
"consumerName": "Java Function Caller Example",
5+
"providerName": "Java Function Implementer Example"
6+
},
7+
"metadata": {
8+
"_case": {
9+
"version": "0.24.0"
10+
}
11+
},
12+
"matcherLookup": {
13+
"matcher:\"2 pages\"": "2 pages",
14+
"matcher:a function invoked with 1 argument ( <any integer> )": {
15+
"_case:matcher:type": "_case:FunctionArgumentsMatcher",
16+
"arguments": [
17+
{
18+
"_case:context:matchBy": "type",
19+
"_case:matcher:example": 2,
20+
"_case:matcher:resolvesTo": "number",
21+
"_case:matcher:type": "_case:Integer"
22+
}
23+
]
24+
},
25+
"matcher:null": {
26+
"_case:context:matchBy": "type",
27+
"_case:matcher:example": null,
28+
"_case:matcher:resolvesTo": "null",
29+
"_case:matcher:type": "_case:MatchNull"
30+
},
31+
"matcher:a function invoked with no arguments": {
32+
"_case:matcher:type": "_case:FunctionArgumentsMatcher",
33+
"arguments": []
34+
},
35+
"matcher:\"bar\"": "bar",
36+
"matcher:a function invoked with 1 argument ( \"foo\" )": {
37+
"_case:matcher:type": "_case:FunctionArgumentsMatcher",
38+
"arguments": [
39+
"foo"
40+
]
41+
}
42+
},
43+
"examples": [
44+
{
45+
"states": [
46+
{
47+
"_case:state:type": "_case:NamedState",
48+
"stateName": "The map is null"
49+
}
50+
],
51+
"mock": {
52+
"_case:mock:type": "_case:MockFunctionExecution",
53+
"_case:run:context:setup": {
54+
"read": {
55+
"stateVariables": "state",
56+
"triggers": "generated",
57+
"type": "_case:MockFunctionCaller"
58+
},
59+
"write": {
60+
"stateVariables": "default",
61+
"triggers": "provided",
62+
"type": "_case:MockFunctionExecution"
63+
}
64+
},
65+
"functionName": "PageNumbers",
66+
"request": {
67+
"_case:matcher:type": "_case:Lookup",
68+
"_case:matcher:uniqueName": "a function invoked with 1 argument ( <any integer> )",
69+
"_case:matcher:child": {
70+
"_case:matcher:type": "_case:FunctionArgumentsMatcher",
71+
"arguments": [
72+
{
73+
"_case:context:matchBy": "type",
74+
"_case:matcher:example": 2,
75+
"_case:matcher:resolvesTo": "number",
76+
"_case:matcher:type": "_case:Integer"
77+
}
78+
]
79+
}
80+
},
81+
"response": {
82+
"_case:matcher:type": "_case:Lookup",
83+
"_case:matcher:uniqueName": "\"2 pages\"",
84+
"_case:matcher:child": "2 pages"
85+
}
86+
},
87+
"result": "VERIFIED"
88+
},
89+
{
90+
"states": [
91+
{
92+
"_case:state:type": "_case:NamedState",
93+
"stateName": "The map is null"
94+
}
95+
],
96+
"mock": {
97+
"_case:mock:type": "_case:MockFunctionExecution",
98+
"_case:run:context:setup": {
99+
"read": {
100+
"stateVariables": "state",
101+
"triggers": "generated",
102+
"type": "_case:MockFunctionCaller"
103+
},
104+
"write": {
105+
"stateVariables": "default",
106+
"triggers": "provided",
107+
"type": "_case:MockFunctionExecution"
108+
}
109+
},
110+
"functionName": "NoArgFunction",
111+
"request": {
112+
"_case:matcher:type": "_case:Lookup",
113+
"_case:matcher:uniqueName": "a function invoked with no arguments",
114+
"_case:matcher:child": {
115+
"_case:matcher:type": "_case:FunctionArgumentsMatcher",
116+
"arguments": []
117+
}
118+
},
119+
"response": {
120+
"_case:matcher:type": "_case:Lookup",
121+
"_case:matcher:uniqueName": "null",
122+
"_case:matcher:child": {
123+
"_case:context:matchBy": "type",
124+
"_case:matcher:example": null,
125+
"_case:matcher:resolvesTo": "null",
126+
"_case:matcher:type": "_case:MatchNull"
127+
}
128+
}
129+
},
130+
"result": "VERIFIED"
131+
},
132+
{
133+
"states": [
134+
{
135+
"_case:state:type": "_case:NamedState",
136+
"stateName": "The map is not null"
137+
},
138+
{
139+
"_case:state:type": "_case:NamedState",
140+
"stateName": "The key 'foo' is set to 'bar'"
141+
}
142+
],
143+
"mock": {
144+
"_case:mock:type": "_case:MockFunctionExecution",
145+
"_case:run:context:setup": {
146+
"read": {
147+
"stateVariables": "state",
148+
"triggers": "generated",
149+
"type": "_case:MockFunctionCaller"
150+
},
151+
"write": {
152+
"stateVariables": "default",
153+
"triggers": "provided",
154+
"type": "_case:MockFunctionExecution"
155+
}
156+
},
157+
"functionName": "keyValueStore",
158+
"request": {
159+
"_case:matcher:type": "_case:Lookup",
160+
"_case:matcher:uniqueName": "a function invoked with 1 argument ( \"foo\" )",
161+
"_case:matcher:child": {
162+
"_case:matcher:type": "_case:FunctionArgumentsMatcher",
163+
"arguments": [
164+
"foo"
165+
]
166+
}
167+
},
168+
"response": {
169+
"_case:matcher:type": "_case:Lookup",
170+
"_case:matcher:uniqueName": "\"bar\"",
171+
"_case:matcher:child": "bar"
172+
}
173+
},
174+
"result": "VERIFIED"
175+
}
176+
]
177+
}

packages/dsl-java/case-contracts/java-function-implementer-example/java-function-caller-example-main.case.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
},
77
"metadata": {
88
"_case": {
9-
"version": "0.22.0"
9+
"version": "0.24.0"
1010
}
1111
},
1212
"matcherLookup": {
@@ -42,7 +42,12 @@
4242
},
4343
"examples": [
4444
{
45-
"states": [],
45+
"states": [
46+
{
47+
"_case:state:type": "_case:NamedState",
48+
"stateName": "The map is null"
49+
}
50+
],
4651
"mock": {
4752
"_case:mock:type": "_case:MockFunctionExecution",
4853
"_case:run:context:setup": {
@@ -82,7 +87,12 @@
8287
"result": "VERIFIED"
8388
},
8489
{
85-
"states": [],
90+
"states": [
91+
{
92+
"_case:state:type": "_case:NamedState",
93+
"stateName": "The map is null"
94+
}
95+
],
8696
"mock": {
8797
"_case:mock:type": "_case:MockFunctionExecution",
8898
"_case:run:context:setup": {
@@ -121,6 +131,10 @@
121131
},
122132
{
123133
"states": [
134+
{
135+
"_case:state:type": "_case:NamedState",
136+
"stateName": "The map is not null"
137+
},
124138
{
125139
"_case:state:type": "_case:NamedState",
126140
"stateName": "The key 'foo' is set to 'bar'"

packages/dsl-java/src/main/java/io/contract_testing/contractcase/internal/client/rpc/ContractResponseStreamObserver.java

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@
1515
import io.contract_testing.contractcase.grpc.ContractCaseStream.ResultResponse;
1616
import io.contract_testing.contractcase.grpc.ContractCaseStream.StateHandlerHandle.Stage;
1717
import io.contract_testing.contractcase.internal.client.MaintainerLog;
18+
import io.contract_testing.contractcase.internal.client.server.ContractCaseProcess;
1819
import io.contract_testing.contractcase.internal.edge.ConnectorResult;
1920
import io.contract_testing.contractcase.internal.edge.ConnectorStateHandler;
2021
import io.contract_testing.contractcase.internal.edge.ConnectorSuccess;
2122
import io.contract_testing.contractcase.internal.edge.RunTestCallback;
2223
import io.contract_testing.contractcase.logs.LogPrinter;
23-
import io.grpc.Status;
24-
import io.grpc.Status.Code;
2524
import io.grpc.stub.StreamObserver;
2625
import java.io.PrintWriter;
2726
import java.io.StringWriter;
@@ -221,37 +220,53 @@ public void onNext(final ContractResponse coreResponse) {
221220
@Override
222221
public void onError(final Throwable t) {
223222
try {
224-
System.err.println("""
225-
ContractCase was unable to contact its internal server.
226-
This is either a conflict while starting the server,
227-
a problem with the test runner (eg, no localhost
228-
network access), a crash while the server was running,
229-
or a bug in ContractCase.
230-
223+
if (ContractCaseProcess.getInstance().processShutdownTriggered()) {
224+
System.err.println("""
225+
ContractCase wasn't shutdown cleanly during a JVM exit.
226+
227+
\n
228+
This usually happens when a call to .close() was missed.
229+
230+
\n
231+
To ensure that the right outputs from tests are recorded,
232+
make sure that you always call .close() on your instance(s)
233+
of ContractDefiner or ContractVerifier, even if tests
234+
throw an error.
235+
""");
236+
} else {
237+
System.err.println("""
238+
ContractCase was unable to contact its internal server.
239+
This is either a conflict while starting the server,
240+
a problem with the test runner (eg, no localhost
241+
network access), a crash while the server was running,
242+
or a bug in ContractCase.
243+
244+
\n
245+
There may be additional context in the rest of
246+
the log output.
247+
248+
--- Error message is ---
249+
"""
250+
+ " " + t.getMessage() + """
251+
\n
252+
------------------------
253+
With stack trace:
254+
\n
255+
"""
256+
+ " " + getStackTrace(t) + """
257+
\n
258+
If you are unable to resolve this locally, or if
259+
you suspect a bug, please open an issue here:
231260
\n
232-
There may be additional context in the rest of
233-
the log output.
234-
235-
--- Error message is ---
236-
"""
237-
+ " " + t.getMessage() + """
238-
\n
239-
------------------------
240-
With stack trace:
241-
242-
"""
243-
+ " " + getStackTrace(t) + """
244-
\n
245-
If you are unable to resolve this locally, or if
246-
you suspect a bug, please open an issue here:
247-
\n
248-
https://github.com/case-contract-testing/contract-case/issues/new
249-
""");
261+
https://github.com/case-contract-testing/contract-case/issues/new
262+
263+
""");
264+
265+
}
250266
rpcConnector.cancelAll(new ContractCaseCoreError(
251267
"ContractCase failed while contacting its internal server: ",
252268
t
253269
));
254-
255270
executor.close();
256271
} finally {
257272
rpcConnector.finishLatch.countDown();

0 commit comments

Comments
 (0)