14
14
15
15
import datetime
16
16
import os
17
+ import time
17
18
18
19
import unittest2
19
20
20
21
from gcloud ._helpers import UTC
21
22
from gcloud import datastore
22
- from gcloud .datastore import client
23
+ from gcloud .datastore import client as client_mod
23
24
from gcloud .environment_vars import GCD_DATASET
24
25
from gcloud .environment_vars import TESTS_DATASET
25
26
from gcloud .exceptions import Conflict
29
30
from system_tests import populate_datastore
30
31
31
32
33
+ # Isolated namespace so concurrent test runs don't collide.
34
+ TEST_NAMESPACE = 'ns%d' % (1000 * time .time (),)
32
35
EMULATOR_DATASET = os .getenv (GCD_DATASET )
33
36
34
37
@@ -41,18 +44,22 @@ class Config(object):
41
44
CLIENT = None
42
45
43
46
47
+ def clone_client (client ):
48
+ # Fool the Client constructor to avoid creating a new connection.
49
+ cloned_client = datastore .Client (project = client .project ,
50
+ namespace = client .namespace ,
51
+ http = object ())
52
+ cloned_client .connection = client .connection
53
+ return cloned_client
54
+
55
+
44
56
def setUpModule ():
45
57
if EMULATOR_DATASET is None :
46
- client .DATASET = TESTS_DATASET
47
- Config .CLIENT = datastore .Client ()
58
+ client_mod .DATASET = TESTS_DATASET
59
+ Config .CLIENT = datastore .Client (namespace = TEST_NAMESPACE )
48
60
else :
49
- Config .CLIENT = datastore .Client (project = EMULATOR_DATASET )
50
- populate_datastore .add_characters (client = Config .CLIENT )
51
-
52
-
53
- def tearDownModule ():
54
- if EMULATOR_DATASET is not None :
55
- clear_datastore .remove_all_entities (client = Config .CLIENT )
61
+ Config .CLIENT = datastore .Client (project = EMULATOR_DATASET ,
62
+ namespace = TEST_NAMESPACE )
56
63
57
64
58
65
class TestDatastore (unittest2 .TestCase ):
@@ -87,7 +94,6 @@ class TestDatastoreSave(TestDatastore):
87
94
88
95
@classmethod
89
96
def setUpClass (cls ):
90
- super (TestDatastoreSave , cls ).setUpClass ()
91
97
cls .PARENT = Config .CLIENT .key ('Blog' , 'PizzaMan' )
92
98
93
99
def _get_post (self , id_or_name = None , post_content = None ):
@@ -194,13 +200,32 @@ class TestDatastoreQuery(TestDatastore):
194
200
195
201
@classmethod
196
202
def setUpClass (cls ):
197
- super (TestDatastoreQuery , cls ).setUpClass ()
203
+ cls .CLIENT = clone_client (Config .CLIENT )
204
+ # Remove the namespace from the cloned client, since these
205
+ # query tests rely on the entities to be already stored and indexed,
206
+ # hence ``TEST_NAMESPACE`` set at runtime can't be used.
207
+ cls .CLIENT .namespace = None
208
+
209
+ # In the emulator, re-populating the datastore is cheap.
210
+ if EMULATOR_DATASET is not None :
211
+ # Populate the datastore with the cloned client.
212
+ populate_datastore .add_characters (client = cls .CLIENT )
213
+
198
214
cls .CHARACTERS = populate_datastore .CHARACTERS
199
- cls .ANCESTOR_KEY = Config .CLIENT .key (* populate_datastore .ANCESTOR )
215
+ # Use the client for this test instead of the global.
216
+ cls .ANCESTOR_KEY = cls .CLIENT .key (* populate_datastore .ANCESTOR )
217
+
218
+ @classmethod
219
+ def tearDownClass (cls ):
220
+ # In the emulator, destroy the query entities.
221
+ if EMULATOR_DATASET is not None :
222
+ # Use the client for this test instead of the global.
223
+ clear_datastore .remove_all_entities (client = cls .CLIENT )
200
224
201
225
def _base_query (self ):
202
- return Config .CLIENT .query (kind = 'Character' ,
203
- ancestor = self .ANCESTOR_KEY )
226
+ # Use the client for this test instead of the global.
227
+ return self .CLIENT .query (kind = 'Character' ,
228
+ ancestor = self .ANCESTOR_KEY )
204
229
205
230
def test_limit_queries (self ):
206
231
limit = 5
@@ -245,7 +270,8 @@ def test_ancestor_query(self):
245
270
self .assertEqual (len (entities ), expected_matches )
246
271
247
272
def test_query___key___filter (self ):
248
- rickard_key = Config .CLIENT .key (* populate_datastore .RICKARD )
273
+ # Use the client for this test instead of the global.
274
+ rickard_key = self .CLIENT .key (* populate_datastore .RICKARD )
249
275
250
276
query = self ._base_query ()
251
277
query .add_filter ('__key__' , '=' , rickard_key )
@@ -377,11 +403,8 @@ def test_transaction(self):
377
403
self .assertEqual (retrieved_entity , entity )
378
404
379
405
def test_failure_with_contention (self ):
380
- contention_key = 'baz'
381
- # Fool the Client constructor to avoid creating a new connection.
382
- local_client = datastore .Client (project = Config .CLIENT .project ,
383
- http = object ())
384
- local_client .connection = Config .CLIENT .connection
406
+ contention_prop_name = 'baz'
407
+ local_client = clone_client (Config .CLIENT )
385
408
386
409
# Insert an entity which will be retrieved in a transaction
387
410
# and updated outside it with a contentious value.
@@ -396,10 +419,10 @@ def test_failure_with_contention(self):
396
419
entity_in_txn = local_client .get (key )
397
420
398
421
# Update the original entity outside the transaction.
399
- orig_entity [contention_key ] = u'outside'
422
+ orig_entity [contention_prop_name ] = u'outside'
400
423
Config .CLIENT .put (orig_entity )
401
424
402
425
# Try to update the entity which we already updated outside the
403
426
# transaction.
404
- entity_in_txn [contention_key ] = u'inside'
427
+ entity_in_txn [contention_prop_name ] = u'inside'
405
428
txn .put (entity_in_txn )
0 commit comments