8
8
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
9
9
# obtain one at https://mozilla.org/MPL/2.0/.
10
10
11
+ import math
11
12
import sys
12
13
import time
13
14
from collections import Counter
14
15
15
- import pytest
16
-
17
16
from hypothesis import Phase , settings
18
17
from hypothesis .database import (
19
18
DirectoryBasedExampleDatabase ,
20
19
InMemoryExampleDatabase ,
21
20
MultiplexedDatabase ,
22
21
)
22
+ from hypothesis .internal .reflection import get_pretty_function_description
23
23
24
24
from tests .cover .test_database_backend import _database_conforms_to_listener_api
25
25
@@ -79,8 +79,17 @@ def listener(event):
79
79
}
80
80
81
81
82
- # TODO flaky failure on linux
83
- @pytest .mark .xfail (strict = False )
82
+ def wait_for (condition , * , timeout = 1 , interval = 0.01 ):
83
+ for _ in range (math .ceil (timeout / interval )):
84
+ if condition ():
85
+ return
86
+ time_sleep (interval )
87
+ raise Exception (
88
+ f"timing out after waiting { timeout } s for condition "
89
+ f"{ get_pretty_function_description (condition )} "
90
+ )
91
+
92
+
84
93
def test_database_listener_directory_explicit (tmp_path ):
85
94
db = DirectoryBasedExampleDatabase (tmp_path )
86
95
events = []
@@ -91,8 +100,7 @@ def listener(event):
91
100
db .add_listener (listener )
92
101
93
102
db .save (b"k1" , b"v1" )
94
- time_sleep (0.2 )
95
- assert events == [("save" , (b"k1" , b"v1" ))]
103
+ wait_for (lambda : events == [("save" , (b"k1" , b"v1" ))])
96
104
97
105
db .remove_listener (listener )
98
106
db .delete (b"k1" , b"v1" )
@@ -103,55 +111,76 @@ def listener(event):
103
111
db .add_listener (listener )
104
112
db .delete (b"k1" , b"v2" )
105
113
db .save (b"k1" , b"v3" )
106
- time_sleep (0.2 )
107
- assert events [1 :] == [
108
- ("delete" , (b"k1" , None )),
109
- ("save" , (b"k1" , b"v3" )),
110
- ]
114
+ wait_for (
115
+ lambda : events [1 :]
116
+ == [
117
+ ("delete" , (b"k1" , None )),
118
+ ("save" , (b"k1" , b"v3" )),
119
+ ]
120
+ )
111
121
112
122
# moving into a nonexistent key
113
123
db .move (b"k1" , b"k2" , b"v3" )
114
- time_sleep (0.2 )
124
+ time_sleep (0.5 )
115
125
# moving back into an existing key
116
126
db .move (b"k2" , b"k1" , b"v3" )
117
- time_sleep (0.2 )
127
+ time_sleep (0.5 )
118
128
119
129
if sys .platform .startswith ("darwin" ):
120
- expected = [
130
+ assert events [ 3 :] = = [
121
131
("delete" , (b"k1" , b"v3" )),
122
132
("save" , (b"k2" , b"v3" )),
123
133
("delete" , (b"k2" , b"v3" )),
124
134
("save" , (b"k1" , b"v3" )),
125
- ]
135
+ ], str ( events [ 3 :])
126
136
elif sys .platform .startswith ("win" ):
127
- # windows fires a save/delete event for our particular moves
128
- # at the os-level instead of a move (or watchdog just isn't picking
129
- # up on it correctly on windows). This means we don't get the exact
130
- # deleted values for us to broadcast.
131
- expected = [
137
+ # watchdog fires save/delete events instead of move events on windows.
138
+ # This means we don't broadcast the exact deleted value.
139
+ assert events [3 :] == [
132
140
("delete" , (b"k1" , None )),
133
141
("save" , (b"k2" , b"v3" )),
134
142
("delete" , (b"k2" , None )),
135
143
("save" , (b"k1" , b"v3" )),
136
- ]
144
+ ], str ( events [ 3 :])
137
145
elif sys .platform .startswith ("linux" ):
138
- expected = [
139
- # as far as I can tell, linux fires both a save and a move event
140
- # for the first move event. I don't know if this is our bug or an os
141
- # implementation detail. I am leaning towards the latter, since other
142
- # os' are fine.
143
- ("save" , (b"k2" , b"v3" )),
144
- # first move event is normal...
145
- ("delete" , (b"k1" , b"v3" )),
146
- ("save" , (b"k2" , b"v3" )),
147
- # ...but the second move event gets picked up by watchdog as an individual
148
- # save/delete, not a move. I'm not sure why. Therefore we don't have
149
- # the delete value present; and the ordering is also different from
150
- # normal.
151
- ("save" , (b"k1" , b"v3" )),
152
- ("delete" , (b"k2" , None )),
153
- ]
146
+ # move #1
147
+ assert ("save" , (b"k2" , b"v3" )) in events
148
+ # sometimes watchdog fires a move event (= save + delete with value),
149
+ # and other times it fires separate save and delete events (= delete with
150
+ # no value). I think this is due to particulars of what happens when
151
+ # a new directory gets created very close to the time when a file is
152
+ # saved to that directory.
153
+ assert any (("delete" , (b"k1" , val )) in events for val in [b"v3" , None ])
154
+
155
+ # move #2
156
+ assert ("save" , (b"k1" , b"v3" )) in events
157
+ assert any (("delete" , (b"k2" , val )) in events for val in [b"v3" , None ])
154
158
else :
155
159
raise NotImplementedError (f"unknown platform { sys .platform } " )
156
160
157
- assert events [3 :] == expected , str (events [3 :])
161
+
162
+ def test_database_listener_directory_move (tmp_path ):
163
+ db = DirectoryBasedExampleDatabase (tmp_path )
164
+ events = []
165
+
166
+ def listener (event ):
167
+ events .append (event )
168
+
169
+ # make sure both keys exist and that v1 exists in k1 and not k2
170
+ db .save (b"k1" , b"v1" )
171
+ db .save (b"k2" , b"v_unrelated" )
172
+
173
+ time_sleep (0.1 )
174
+ db .add_listener (listener )
175
+ time_sleep (0.1 )
176
+
177
+ db .move (b"k1" , b"k2" , b"v1" )
178
+ # events might arrive in either order
179
+ wait_for (
180
+ lambda : set (events )
181
+ == {
182
+ ("save" , (b"k2" , b"v1" )),
183
+ # windows doesn't fire move events, so value is None
184
+ ("delete" , (b"k1" , None if sys .platform .startswith ("win" ) else b"v1" )),
185
+ }
186
+ )
0 commit comments