Skip to content

Commit a44cf01

Browse files
author
Jon Wayne Parrott
committed
Trying to improve datastore flaky tests.
1 parent 59b4ba0 commit a44cf01

File tree

5 files changed

+70
-27
lines changed

5 files changed

+70
-27
lines changed

datastore/api/snippets_test.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,39 @@
1111
# See the License for the specific language governing permissions and
1212
# limitations under the License.
1313
#
14-
from flaky import flaky
15-
import gcloud
14+
from functools import wraps
15+
import time
16+
1617
from gcloud import datastore
17-
from nose.plugins.attrib import attr
18-
from tests import CloudBaseTest
18+
from tests import CloudBaseTest, mark_flaky
1919

2020
from . import snippets
2121

2222

23-
def flaky_filter(e, *args):
24-
return isinstance(e, gcloud.exceptions.GCloudError)
23+
def eventually_consistent(f):
24+
@wraps(f)
25+
def inner(self, *args, **kwargs):
26+
# This is pretty hacky, but make datastore wait 1s after any
27+
# put operation to in order to account for eventual consistency.
28+
original_put_multi = self.client.put_multi
29+
30+
def put_multi(*args, **kwargs):
31+
result = original_put_multi(*args, **kwargs)
32+
time.sleep(1)
33+
return result
34+
35+
self.client.put_multi = put_multi
36+
37+
try:
38+
result = f(self, *args, **kwargs)
39+
finally:
40+
self.client.put_multi = original_put_multi
41+
42+
return result
43+
return inner
2544

2645

27-
@attr('slow')
28-
@flaky(rerun_filter=flaky_filter)
46+
@mark_flaky
2947
class DatastoreSnippetsTest(CloudBaseTest):
3048

3149
def setUp(self):
@@ -110,16 +128,19 @@ def test_batch_lookup(self):
110128
def test_batch_delete(self):
111129
snippets.batch_delete(self.client)
112130

131+
@eventually_consistent
113132
def test_unindexed_property_query(self):
114133
tasks = snippets.unindexed_property_query(self.client)
115134
self.to_delete_entities.extend(tasks)
116135
self.assertTrue(tasks)
117136

137+
@eventually_consistent
118138
def test_basic_query(self):
119139
tasks = snippets.basic_query(self.client)
120140
self.to_delete_entities.extend(tasks)
121141
self.assertTrue(tasks)
122142

143+
@eventually_consistent
123144
def test_projection_query(self):
124145
priorities, percents = snippets.projection_query(self.client)
125146
self.to_delete_entities.extend(self.client.query(kind='Task').fetch())
@@ -131,9 +152,11 @@ def test_ancestor_query(self):
131152
self.to_delete_entities.extend(tasks)
132153
self.assertTrue(tasks)
133154

155+
@eventually_consistent
134156
def test_run_query(self):
135157
snippets.run_query(self.client)
136158

159+
@eventually_consistent
137160
def test_cursor_paging(self):
138161
for n in range(6):
139162
self.to_delete_entities.append(
@@ -147,46 +170,55 @@ def test_cursor_paging(self):
147170
self.assertTrue(cursor_one)
148171
self.assertTrue(cursor_two)
149172

173+
@eventually_consistent
150174
def test_property_filter(self):
151175
tasks = snippets.property_filter(self.client)
152176
self.to_delete_entities.extend(tasks)
153177
self.assertTrue(tasks)
154178

179+
@eventually_consistent
155180
def test_composite_filter(self):
156181
tasks = snippets.composite_filter(self.client)
157182
self.to_delete_entities.extend(tasks)
158183
self.assertTrue(tasks)
159184

185+
@eventually_consistent
160186
def test_key_filter(self):
161187
tasks = snippets.key_filter(self.client)
162188
self.to_delete_entities.extend(tasks)
163189
self.assertTrue(tasks)
164190

191+
@eventually_consistent
165192
def test_ascending_sort(self):
166193
tasks = snippets.ascending_sort(self.client)
167194
self.to_delete_entities.extend(tasks)
168195
self.assertTrue(tasks)
169196

197+
@eventually_consistent
170198
def test_descending_sort(self):
171199
tasks = snippets.descending_sort(self.client)
172200
self.to_delete_entities.extend(tasks)
173201
self.assertTrue(tasks)
174202

203+
@eventually_consistent
175204
def test_multi_sort(self):
176205
tasks = snippets.multi_sort(self.client)
177206
self.to_delete_entities.extend(tasks)
178207
self.assertTrue(tasks)
179208

209+
@eventually_consistent
180210
def test_keys_only_query(self):
181211
keys = snippets.keys_only_query(self.client)
182212
self.to_delete_keys.extend(keys)
183213
self.assertTrue(keys)
184214

215+
@eventually_consistent
185216
def test_distinct_query(self):
186217
tasks = snippets.distinct_query(self.client)
187218
self.to_delete_entities.extend(tasks)
188219
self.assertTrue(tasks)
189220

221+
@eventually_consistent
190222
def test_distinct_on_query(self):
191223
tasks = snippets.distinct_on_query(self.client)
192224
self.to_delete_entities.extend(tasks)
@@ -241,25 +273,29 @@ def transactional_single_entity_group_read_only(self):
241273
self.assertTrue(task_list)
242274
self.assertTrue(tasks_in_list)
243275

276+
@eventually_consistent
244277
def test_namespace_run_query(self):
245278
all_namespaces, filtered_namespaces = snippets.namespace_run_query(
246279
self.client)
247280
self.assertTrue(all_namespaces)
248281
self.assertTrue(filtered_namespaces)
249282
self.assertTrue('google' in filtered_namespaces)
250283

284+
@eventually_consistent
251285
def test_kind_run_query(self):
252286
kinds = snippets.kind_run_query(self.client)
253287
self.to_delete_entities.extend(self.client.query(kind='Task').fetch())
254288
self.assertTrue(kinds)
255289
self.assertTrue('Task' in kinds)
256290

291+
@eventually_consistent
257292
def test_property_run_query(self):
258293
kinds = snippets.property_run_query(self.client)
259294
self.to_delete_entities.extend(self.client.query(kind='Task').fetch())
260295
self.assertTrue(kinds)
261296
self.assertTrue('Task' in kinds)
262297

298+
@eventually_consistent
263299
def test_property_by_kind_run_query(self):
264300
reprs = snippets.property_by_kind_run_query(self.client)
265301
self.to_delete_entities.extend(self.client.query(kind='Task').fetch())

datastore/api/tasks_test.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,14 @@
1010
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1111
# See the License for the specific language governing permissions and
1212
# limitations under the License.
13-
#
14-
from flaky import flaky
15-
import gcloud
13+
1614
from gcloud import datastore
17-
from nose.plugins.attrib import attr
18-
from tests import CloudBaseTest
15+
from tests import CloudBaseTest, mark_flaky
1916

2017
from . import tasks
2118

2219

23-
def flaky_filter(e, *args):
24-
return isinstance(e, gcloud.exceptions.GCloudError)
25-
26-
27-
@attr('slow')
28-
@flaky(rerun_filter=flaky_filter)
20+
@mark_flaky
2921
class DatastoreTasksTest(CloudBaseTest):
3022

3123
def setUp(self):

tests/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@
1616
AppEngineTestbedCase,
1717
capture_stdout,
1818
CloudBaseTest,
19+
flaky_filter,
1920
Http2Mock,
21+
mark_flaky,
2022
RESOURCE_PATH)
2123

2224

2325
__all__ = [
2426
'AppEngineTestbedCase',
2527
'capture_stdout',
2628
'CloudBaseTest',
29+
'flaky_filter',
2730
'Http2Mock',
31+
'mark_flaky',
2832
'RESOURCE_PATH'
2933
]

tests/utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
import tempfile
2222
import unittest
2323

24+
from flaky import flaky
25+
import gcloud
2426
import httplib2
27+
from nose.plugins.attrib import attr
2528
from nose.plugins.skip import SkipTest
2629
from six.moves import cStringIO
2730

@@ -40,6 +43,18 @@
4043
BUCKET_NAME_ENV_VAR = 'TEST_BUCKET_NAME'
4144

4245

46+
def flaky_filter(e, *args):
47+
exception_class, exception_instance, traceback = e
48+
return isinstance(
49+
exception_instance,
50+
(gcloud.exceptions.GCloudError,))
51+
52+
53+
def mark_flaky(f):
54+
return flaky(max_runs=3, rerun_filter=flaky_filter)(
55+
attr('flaky')(f))
56+
57+
4358
class CloudBaseTest(unittest.TestCase):
4459

4560
def setUp(self):

tox.ini

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,8 @@ deps =
5252
commands =
5353
nosetests \
5454
--exclude-dir=appengine \
55-
-a '!slow' \
5655
{[testenv]commonargs} \
57-
{posargs}
56+
{posargs:-a '!slow,!flaky'}
5857

5958
[testenv:py34]
6059
basepython = python3.4
@@ -63,28 +62,25 @@ deps =
6362
commands =
6463
nosetests \
6564
--exclude-dir=appengine \
66-
-a '!slow' \
6765
{[testenv]commonargs} \
68-
{posargs}
66+
{posargs:-a '!slow,!flaky'}
6967

70-
[testenv:py27-slow]
68+
[testenv:py27-all]
7169
deps =
7270
{[testenv]deps}
7371
commands =
7472
nosetests \
7573
--exclude-dir=appengine \
76-
-a 'slow' \
7774
{[testenv]commonargs} \
7875
{posargs}
7976

80-
[testenv:py34-slow]
77+
[testenv:py34-all]
8178
basepython = python3.4
8279
deps =
8380
{[testenv]deps}
8481
commands =
8582
nosetests \
8683
--exclude-dir=appengine \
87-
-a 'slow' \
8884
{[testenv]commonargs} \
8985
{posargs}
9086

0 commit comments

Comments
 (0)