@@ -62,93 +62,91 @@ def tearDown(self):
62
62
self .listener .results .clear ()
63
63
64
64
65
- def create_test (scenario_def ):
65
+ def format_actual_results (results ):
66
+ started = results ['started' ]
67
+ succeeded = results ['succeeded' ]
68
+ failed = results ['failed' ]
69
+ msg = "\n Started: %r" % (started [0 ].command if len (started ) else None ,)
70
+ msg += "\n Succeeded: %r" % (succeeded [0 ].reply if len (succeeded ) else None ,)
71
+ msg += "\n Failed: %r" % (failed [0 ].failure if len (failed ) else None ,)
72
+ return msg
73
+
74
+
75
+ def create_test (scenario_def , test ):
66
76
def run_scenario (self ):
67
- self .assertTrue (scenario_def ['tests' ], "tests cannot be empty" )
68
77
dbname = scenario_def ['database_name' ]
69
78
collname = scenario_def ['collection_name' ]
70
79
71
- for test in scenario_def ['tests' ]:
72
- ver = client_context .version [:2 ]
73
- if "ignore_if_server_version_greater_than" in test :
74
- version = test ["ignore_if_server_version_greater_than" ]
75
- if ver > tuple (map (int , version .split ("." ))):
76
- continue
77
- if "ignore_if_server_version_less_than" in test :
78
- version = test ["ignore_if_server_version_less_than" ]
79
- if ver < tuple (map (int , version .split ("." ))):
80
- continue
81
- if "ignore_if_topology_type" in test :
82
- types = set (test ["ignore_if_topology_type" ])
83
- if "sharded" in types and client_context .is_mongos :
84
- continue
85
-
86
- coll = self .client [dbname ][collname ]
87
- coll .drop ()
88
- coll .insert_many (scenario_def ['data' ])
89
- self .listener .results .clear ()
90
- name = camel_to_snake (test ['operation' ]['name' ])
91
- # Don't send $readPreference to mongos before 2.4.
92
- if (client_context .version .at_least (2 , 4 , 0 )
93
- and 'read_preference' in test ['operation' ]):
94
- mode = read_pref_mode_from_name (
95
- test ['operation' ]['read_preference' ]['mode' ])
96
- coll = coll .with_options (
97
- read_preference = make_read_preference (mode , None ))
98
-
99
- test_args = test ['operation' ]['arguments' ]
100
- if 'writeConcern' in test_args :
101
- concern = test_args .pop ('writeConcern' )
102
- coll = coll .with_options (
103
- write_concern = WriteConcern (** concern ))
104
- args = {}
105
- for arg in test_args :
106
- args [camel_to_snake (arg )] = test_args [arg ]
107
-
108
- if name == 'bulk_write' :
109
- bulk_args = []
110
- for request in args ['requests' ]:
111
- opname = next (iter (request ))
112
- klass = opname [0 :1 ].upper () + opname [1 :]
113
- arg = getattr (pymongo , klass )(** request [opname ])
114
- bulk_args .append (arg )
115
- try :
116
- coll .bulk_write (bulk_args , args .get ('ordered' , True ))
117
- except OperationFailure :
118
- pass
119
- elif name == 'find' :
120
- if 'sort' in args :
121
- args ['sort' ] = list (args ['sort' ].items ())
122
- try :
123
- # Iterate the cursor.
124
- tuple (coll .find (** args ))
125
- except OperationFailure :
126
- pass
127
- # Wait for the killCursors thread to run if necessary.
128
- if 'limit' in args and client_context .version [:2 ] < (3 , 1 ):
129
- self .client ._kill_cursors_executor .wake ()
130
- started = self .listener .results ['started' ]
131
- wait_until (
132
- lambda : started [- 1 ].command_name == 'killCursors' ,
133
- "publish a start event for killCursors." )
134
- else :
135
- try :
136
- getattr (coll , name )(** args )
137
- except OperationFailure :
138
- pass
139
-
140
- for expectation in test ['expectations' ]:
141
- event_type = next (iter (expectation ))
142
- if event_type == "command_started_event" :
143
- event = self .listener .results ['started' ].pop (0 )
80
+ coll = self .client [dbname ][collname ]
81
+ coll .drop ()
82
+ coll .insert_many (scenario_def ['data' ])
83
+ self .listener .results .clear ()
84
+ name = camel_to_snake (test ['operation' ]['name' ])
85
+ # Don't send $readPreference to mongos before 2.4.
86
+ if (client_context .version .at_least (2 , 4 , 0 )
87
+ and 'read_preference' in test ['operation' ]):
88
+ mode = read_pref_mode_from_name (
89
+ test ['operation' ]['read_preference' ]['mode' ])
90
+ coll = coll .with_options (
91
+ read_preference = make_read_preference (mode , None ))
92
+
93
+ test_args = test ['operation' ]['arguments' ]
94
+ if 'writeConcern' in test_args :
95
+ concern = test_args .pop ('writeConcern' )
96
+ coll = coll .with_options (
97
+ write_concern = WriteConcern (** concern ))
98
+ args = {}
99
+ for arg in test_args :
100
+ args [camel_to_snake (arg )] = test_args [arg ]
101
+
102
+ if name == 'bulk_write' :
103
+ bulk_args = []
104
+ for request in args ['requests' ]:
105
+ opname = next (iter (request ))
106
+ klass = opname [0 :1 ].upper () + opname [1 :]
107
+ arg = getattr (pymongo , klass )(** request [opname ])
108
+ bulk_args .append (arg )
109
+ try :
110
+ coll .bulk_write (bulk_args , args .get ('ordered' , True ))
111
+ except OperationFailure :
112
+ pass
113
+ elif name == 'find' :
114
+ if 'sort' in args :
115
+ args ['sort' ] = list (args ['sort' ].items ())
116
+ try :
117
+ # Iterate the cursor.
118
+ tuple (coll .find (** args ))
119
+ except OperationFailure :
120
+ pass
121
+ # Wait for the killCursors thread to run if necessary.
122
+ if 'limit' in args and client_context .version [:2 ] < (3 , 1 ):
123
+ self .client ._kill_cursors_executor .wake ()
124
+ started = self .listener .results ['started' ]
125
+ wait_until (
126
+ lambda : started [- 1 ].command_name == 'killCursors' ,
127
+ "publish a start event for killCursors." )
128
+ else :
129
+ try :
130
+ getattr (coll , name )(** args )
131
+ except OperationFailure :
132
+ pass
133
+
134
+ res = self .listener .results
135
+ for expectation in test ['expectations' ]:
136
+ event_type = next (iter (expectation ))
137
+ if event_type == "command_started_event" :
138
+ event = res ['started' ][0 ] if len (res ['started' ]) else None
139
+ if event is not None :
144
140
# The tests substitute 42 for any number other than 0.
145
141
if (event .command_name == 'getMore'
146
142
and event .command ['getMore' ]):
147
143
event .command ['getMore' ] = 42
148
144
elif event .command_name == 'killCursors' :
149
145
event .command ['cursors' ] = [42 ]
150
- elif event_type == "command_succeeded_event" :
151
- event = self .listener .results ['succeeded' ].pop (0 )
146
+ elif event_type == "command_succeeded_event" :
147
+ event = (
148
+ res ['succeeded' ].pop (0 ) if len (res ['succeeded' ]) else None )
149
+ if event is not None :
152
150
reply = event .reply
153
151
# The tests substitute 42 for any number other than 0,
154
152
# and "" for any error message.
@@ -165,32 +163,69 @@ def run_scenario(self):
165
163
if 'cursorsKilled' in reply :
166
164
reply .pop ('cursorsKilled' )
167
165
reply ['cursorsUnknown' ] = [42 ]
168
- elif event_type == "command_failed_event" :
169
- event = self .listener .results ['failed' ].pop (0 )
166
+ # Found succeeded event. Pop related started event.
167
+ res ['started' ].pop (0 )
168
+ elif event_type == "command_failed_event" :
169
+ event = res ['failed' ].pop (0 ) if len (res ['failed' ]) else None
170
+ if event is not None :
171
+ # Found failed event. Pop related started event.
172
+ res ['started' ].pop (0 )
173
+ else :
174
+ self .fail ("Unknown event type" )
175
+
176
+ if event is None :
177
+ event_name = event_type .split ('_' )[1 ]
178
+ self .fail (
179
+ "Expected %s event for %s command. Actual "
180
+ "results:%s" % (
181
+ event_name ,
182
+ expectation [event_type ]['command_name' ],
183
+ format_actual_results (res )))
184
+
185
+ for attr , expected in expectation [event_type ].items ():
186
+ actual = getattr (event , attr )
187
+ if isinstance (expected , dict ):
188
+ for key , val in expected .items ():
189
+ self .assertEqual (val , actual [key ])
170
190
else :
171
- self .fail ("Unknown event type" )
172
- for attr , expected in expectation [event_type ].items ():
173
- actual = getattr (event , attr )
174
- if isinstance (expected , dict ):
175
- for key , val in expected .items ():
176
- self .assertEqual (val , actual [key ])
177
- else :
178
- self .assertEqual (actual , expected )
191
+ self .assertEqual (actual , expected )
179
192
180
193
return run_scenario
181
194
182
195
183
196
def create_tests ():
184
197
for dirpath , _ , filenames in os .walk (_TEST_PATH ):
198
+ dirname = os .path .split (dirpath )[- 1 ]
185
199
for filename in filenames :
186
200
with open (os .path .join (dirpath , filename )) as scenario_stream :
187
201
scenario_def = json .load (
188
202
scenario_stream , object_hook = object_hook )
203
+ assert bool (scenario_def .get ('tests' )), "tests cannot be empty"
189
204
# Construct test from scenario.
190
- new_test = create_test (scenario_def )
191
- test_name = 'test_%s' % (os .path .splitext (filename )[0 ],)
192
- new_test .__name__ = test_name
193
- setattr (TestAllScenarios , new_test .__name__ , new_test )
205
+ for test in scenario_def ['tests' ]:
206
+ new_test = create_test (scenario_def , test )
207
+ if "ignore_if_server_version_greater_than" in test :
208
+ version = test ["ignore_if_server_version_greater_than" ]
209
+ ver = tuple (int (elt ) for elt in version .split ('.' ))
210
+ new_test = client_context .require_version_max (* ver )(
211
+ new_test )
212
+ if "ignore_if_server_version_less_than" in test :
213
+ version = test ["ignore_if_server_version_less_than" ]
214
+ ver = tuple (int (elt ) for elt in version .split ('.' ))
215
+ new_test = client_context .require_version_min (* ver )(
216
+ new_test )
217
+ if "ignore_if_topology_type" in test :
218
+ types = set (test ["ignore_if_topology_type" ])
219
+ if "sharded" in types :
220
+ new_test = client_context .require_no_mongos (None )(
221
+ new_test )
222
+
223
+ test_name = 'test_%s.%s.%s' % (
224
+ dirname ,
225
+ os .path .splitext (filename )[0 ],
226
+ str (test ['description' ].replace (" " , "_" )))
227
+ new_test .__name__ = test_name
228
+ setattr (TestAllScenarios , new_test .__name__ , new_test )
194
229
195
230
196
231
create_tests ()
0 commit comments