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 (dataset_id = client .dataset_id ,
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 (dataset_id = 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 (dataset_id = 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 )
@@ -380,11 +406,8 @@ def test_transaction(self):
380
406
self .assertEqual (retrieved_entity , entity )
381
407
382
408
def test_failure_with_contention (self ):
383
- contention_key = 'baz'
384
- # Fool the Client constructor to avoid creating a new connection.
385
- local_client = datastore .Client (dataset_id = Config .CLIENT .dataset_id ,
386
- http = object ())
387
- local_client .connection = Config .CLIENT .connection
409
+ contention_prop_name = 'baz'
410
+ local_client = clone_client (Config .CLIENT )
388
411
389
412
# Insert an entity which will be retrieved in a transaction
390
413
# and updated outside it with a contentious value.
@@ -399,10 +422,10 @@ def test_failure_with_contention(self):
399
422
entity_in_txn = local_client .get (key )
400
423
401
424
# Update the original entity outside the transaction.
402
- orig_entity [contention_key ] = u'outside'
425
+ orig_entity [contention_prop_name ] = u'outside'
403
426
Config .CLIENT .put (orig_entity )
404
427
405
428
# Try to update the entity which we already updated outside the
406
429
# transaction.
407
- entity_in_txn [contention_key ] = u'inside'
430
+ entity_in_txn [contention_prop_name ] = u'inside'
408
431
txn .put (entity_in_txn )
0 commit comments