21
21
import com .mongodb .MongoNamespace ;
22
22
import com .mongodb .ReadPreference ;
23
23
import com .mongodb .UnixServerAddress ;
24
- import com .mongodb .client .unified .UnifiedTestModifications .TestDef ;
25
- import com .mongodb .event .TestServerMonitorListener ;
26
- import com .mongodb .internal .logging .LogMessage ;
27
- import com .mongodb .logging .TestLoggingInterceptor ;
28
24
import com .mongodb .WriteConcern ;
29
25
import com .mongodb .client .ClientSession ;
30
26
import com .mongodb .client .MongoClient ;
31
27
import com .mongodb .client .MongoDatabase ;
32
28
import com .mongodb .client .gridfs .GridFSBucket ;
33
29
import com .mongodb .client .model .Filters ;
34
30
import com .mongodb .client .test .CollectionHelper ;
31
+ import com .mongodb .client .unified .UnifiedTestModifications .TestDef ;
35
32
import com .mongodb .client .vault .ClientEncryption ;
36
33
import com .mongodb .connection .ClusterDescription ;
37
34
import com .mongodb .connection .ClusterType ;
38
35
import com .mongodb .connection .ServerDescription ;
39
36
import com .mongodb .event .CommandEvent ;
40
37
import com .mongodb .event .CommandStartedEvent ;
38
+ import com .mongodb .event .TestServerMonitorListener ;
41
39
import com .mongodb .internal .connection .TestCommandListener ;
42
40
import com .mongodb .internal .connection .TestConnectionPoolListener ;
41
+ import com .mongodb .internal .logging .LogMessage ;
43
42
import com .mongodb .lang .NonNull ;
44
43
import com .mongodb .lang .Nullable ;
44
+ import com .mongodb .logging .TestLoggingInterceptor ;
45
45
import com .mongodb .test .AfterBeforeParameterResolver ;
46
46
import org .bson .BsonArray ;
47
47
import org .bson .BsonBoolean ;
57
57
import org .junit .jupiter .params .ParameterizedTest ;
58
58
import org .junit .jupiter .params .provider .Arguments ;
59
59
import org .junit .jupiter .params .provider .MethodSource ;
60
+ import org .opentest4j .AssertionFailedError ;
60
61
import org .opentest4j .TestAbortedException ;
61
62
62
63
import java .io .File ;
63
64
import java .io .IOException ;
64
65
import java .net .URISyntaxException ;
66
+ import java .text .MessageFormat ;
65
67
import java .util .ArrayList ;
66
68
import java .util .Collection ;
67
69
import java .util .Collections ;
70
+ import java .util .HashSet ;
68
71
import java .util .List ;
69
72
import java .util .Set ;
70
73
import java .util .concurrent .ExecutionException ;
81
84
import static com .mongodb .client .test .CollectionHelper .getCurrentClusterTime ;
82
85
import static com .mongodb .client .test .CollectionHelper .killAllSessions ;
83
86
import static com .mongodb .client .unified .RunOnRequirementsMatcher .runOnRequirementsMet ;
87
+ import static com .mongodb .client .unified .UnifiedTestModifications .doSkips ;
84
88
import static com .mongodb .client .unified .UnifiedTestModifications .testDef ;
85
89
import static java .util .Collections .singletonList ;
86
90
import static java .util .stream .Collectors .toList ;
91
95
import static org .junit .jupiter .api .Assertions .assertNull ;
92
96
import static org .junit .jupiter .api .Assertions .assertTrue ;
93
97
import static org .junit .jupiter .api .Assertions .fail ;
98
+ import static org .junit .jupiter .api .Assumptions .assumeFalse ;
94
99
import static org .junit .jupiter .api .Assumptions .assumeTrue ;
95
100
import static util .JsonPoweredTestHelper .getTestDocument ;
96
101
import static util .JsonPoweredTestHelper .getTestFiles ;
@@ -100,6 +105,9 @@ public abstract class UnifiedTest {
100
105
private static final Set <String > PRESTART_POOL_ASYNC_WORK_MANAGER_FILE_DESCRIPTIONS = Collections .singleton (
101
106
"wait queue timeout errors include details about checked out connections" );
102
107
108
+ public static final int ATTEMPTS = 3 ;
109
+ private static Set <String > completed = new HashSet <>();
110
+
103
111
@ Nullable
104
112
private String fileDescription ;
105
113
private String schemaVersion ;
@@ -154,32 +162,47 @@ public Entities getEntities() {
154
162
}
155
163
156
164
@ NonNull
157
- protected static Collection <Arguments > getTestData (final String directory ) throws URISyntaxException , IOException {
165
+ protected static Collection <Arguments > getTestData (final String directory , final boolean isReactive )
166
+ throws URISyntaxException , IOException {
158
167
List <Arguments > data = new ArrayList <>();
159
168
for (File file : getTestFiles ("/" + directory + "/" )) {
160
169
BsonDocument fileDocument = getTestDocument (file );
161
-
162
170
for (BsonValue cur : fileDocument .getArray ("tests" )) {
163
- data .add (UnifiedTest .createTestData (directory , fileDocument , cur .asDocument ()));
171
+
172
+ final BsonDocument testDocument = cur .asDocument ();
173
+ String testDescription = testDocument .getString ("description" ).getValue ();
174
+ String fileDescription = fileDocument .getString ("description" ).getValue ();
175
+ TestDef testDef = testDef (directory , fileDescription , testDescription , isReactive );
176
+ doSkips (testDef );
177
+
178
+ boolean retry = testDef .wasAssignedModifier (UnifiedTestModifications .Modifier .RETRY );
179
+ int attempts = retry ? ATTEMPTS : 1 ;
180
+
181
+
182
+ for (int attempt = 1 ; attempt <= attempts ; attempt ++) {
183
+ String testName = !retry
184
+ ? MessageFormat .format ("{0}: {1}" , fileDescription , testDescription )
185
+ : MessageFormat .format (
186
+ "{0}: {1} ({2} of {3})" ,
187
+ fileDescription , testDescription , attempt , attempts );
188
+ data .add (Arguments .of (
189
+ testName ,
190
+ fileDescription ,
191
+ testDescription ,
192
+ directory ,
193
+ attempt ,
194
+ attempts ,
195
+ fileDocument .getString ("schemaVersion" ).getValue (),
196
+ fileDocument .getArray ("runOnRequirements" , null ),
197
+ fileDocument .getArray ("createEntities" , new BsonArray ()),
198
+ fileDocument .getArray ("initialData" , new BsonArray ()),
199
+ testDocument ));
200
+ }
164
201
}
165
202
}
166
203
return data ;
167
204
}
168
205
169
- @ NonNull
170
- private static Arguments createTestData (
171
- final String directory , final BsonDocument fileDocument , final BsonDocument testDocument ) {
172
- return Arguments .of (
173
- fileDocument .getString ("description" ).getValue (),
174
- testDocument .getString ("description" ).getValue (),
175
- directory ,
176
- fileDocument .getString ("schemaVersion" ).getValue (),
177
- fileDocument .getArray ("runOnRequirements" , null ),
178
- fileDocument .getArray ("createEntities" , new BsonArray ()),
179
- fileDocument .getArray ("initialData" , new BsonArray ()),
180
- testDocument );
181
- }
182
-
183
206
protected BsonDocument getDefinition () {
184
207
return definition ;
185
208
}
@@ -192,9 +215,12 @@ protected BsonDocument getDefinition() {
192
215
193
216
@ BeforeEach
194
217
public void setUp (
218
+ final String testName ,
195
219
@ Nullable final String fileDescription ,
196
220
@ Nullable final String testDescription ,
197
221
@ Nullable final String directoryName ,
222
+ final int attemptNumber ,
223
+ final int totalAttempts ,
198
224
final String schemaVersion ,
199
225
@ Nullable final BsonArray runOnRequirements ,
200
226
final BsonArray entitiesArray ,
@@ -216,7 +242,11 @@ public void setUp(
216
242
ignoreExtraEvents = false ;
217
243
testDef = testDef (directoryName , fileDescription , testDescription , isReactive ());
218
244
UnifiedTestModifications .doSkips (testDef );
245
+
246
+ boolean skip = testDef .wasAssignedModifier (UnifiedTestModifications .Modifier .SKIP );
247
+ assumeFalse (skip , "Skipping test" );
219
248
skips (fileDescription , testDescription );
249
+
220
250
assertTrue (
221
251
schemaVersion .equals ("1.0" )
222
252
|| schemaVersion .equals ("1.1" )
@@ -283,8 +313,9 @@ protected void postCleanUp(final TestDef testDef) {
283
313
}
284
314
285
315
/**
286
- * This method is called once per {@link #setUp(String, String, String, String, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonDocument)},
287
- * unless {@link #setUp(String, String, String, String, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonDocument)} fails unexpectedly.
316
+ * This method is called once per
317
+ * {@link #setUp(String, String, String, String, int, int, String, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonDocument)}, unless
318
+ * {@link #setUp(String, String, String, String, int, int, String, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonArray, org.bson.BsonDocument)} fails unexpectedly.
288
319
*/
289
320
protected void skips (final String fileDescription , final String testDescription ) {
290
321
}
@@ -293,40 +324,54 @@ protected boolean isReactive() {
293
324
return false ;
294
325
}
295
326
296
- @ ParameterizedTest (name = "{0}: {1} " )
327
+ @ ParameterizedTest (name = "{0}" )
297
328
@ MethodSource ("data" )
298
329
public void shouldPassAllOutcomes (
330
+ final String testName ,
299
331
@ Nullable final String fileDescription ,
300
332
@ Nullable final String testDescription ,
301
333
@ Nullable final String directoryName ,
334
+ final int attemptNumber ,
335
+ final int totalAttempts ,
302
336
final String schemaVersion ,
303
337
@ Nullable final BsonArray runOnRequirements ,
304
338
final BsonArray entitiesArray ,
305
339
final BsonArray initialData ,
306
340
final BsonDocument definition ) {
307
- BsonArray operations = definition .getArray ("operations" );
308
- for (int i = 0 ; i < operations .size (); i ++) {
309
- BsonValue cur = operations .get (i );
310
- assertOperation (rootContext , cur .asDocument (), i );
311
- }
341
+ assumeFalse (completed .contains (testName ), "Skipping test already performed" );
342
+ completed .add (testName );
343
+ try {
344
+ BsonArray operations = definition .getArray ("operations" );
345
+ for (int i = 0 ; i < operations .size (); i ++) {
346
+ BsonValue cur = operations .get (i );
347
+ assertOperation (rootContext , cur .asDocument (), i );
348
+ }
312
349
313
- if (definition .containsKey ("outcome" )) {
314
- assertOutcome (rootContext );
315
- }
350
+ if (definition .containsKey ("outcome" )) {
351
+ assertOutcome (rootContext );
352
+ }
316
353
317
- if (definition .containsKey ("expectEvents" )) {
318
- compareEvents (rootContext , definition );
319
- }
354
+ if (definition .containsKey ("expectEvents" )) {
355
+ compareEvents (rootContext , definition );
356
+ }
320
357
321
- if (definition .containsKey ("expectLogMessages" )) {
322
- ArrayList <LogMatcher .Tweak > tweaks = new ArrayList <>(singletonList (
323
- // `LogMessage.Entry.Name.OPERATION` is not supported, therefore we skip matching its value
324
- LogMatcher .Tweak .skip (LogMessage .Entry .Name .OPERATION )));
325
- if (getMongoClientSettings ().getClusterSettings ()
326
- .getHosts ().stream ().anyMatch (serverAddress -> serverAddress instanceof UnixServerAddress )) {
327
- tweaks .add (LogMatcher .Tweak .skip (LogMessage .Entry .Name .SERVER_PORT ));
358
+ if (definition .containsKey ("expectLogMessages" )) {
359
+ ArrayList <LogMatcher .Tweak > tweaks = new ArrayList <>(singletonList (
360
+ // `LogMessage.Entry.Name.OPERATION` is not supported, therefore we skip matching its value
361
+ LogMatcher .Tweak .skip (LogMessage .Entry .Name .OPERATION )));
362
+ if (getMongoClientSettings ().getClusterSettings ()
363
+ .getHosts ().stream ().anyMatch (serverAddress -> serverAddress instanceof UnixServerAddress )) {
364
+ tweaks .add (LogMatcher .Tweak .skip (LogMessage .Entry .Name .SERVER_PORT ));
365
+ }
366
+ compareLogMessages (rootContext , definition , tweaks );
367
+ }
368
+ } catch (AssertionFailedError e ) {
369
+ completed .remove (testName );
370
+ if (attemptNumber == totalAttempts ) { // last attempt
371
+ throw e ;
372
+ } else {
373
+ assumeFalse (completed .contains (testName ), "Ignoring failure and retrying attempt " + attemptNumber );
328
374
}
329
- compareLogMessages (rootContext , definition , tweaks );
330
375
}
331
376
}
332
377
0 commit comments