1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ import concurrent .futures
1516import datetime
1617import itertools
1718import math
1819import operator
19- from time import sleep
2020from typing import Callable , Dict , List , Optional
2121
2222import google .auth
@@ -80,8 +80,8 @@ def cleanup():
8080 operations = []
8181 yield operations .append
8282
83- for operation in operations :
84- operation ( )
83+ with concurrent . futures . ThreadPoolExecutor () as executor :
84+ list ( executor . map ( lambda op : op (), operations ) )
8585
8686
8787@pytest .fixture
@@ -389,15 +389,18 @@ def test_create_document_w_vector(client, cleanup, database):
389389 for v in client .collection (collection_id ).order_by ("embedding" ).get ()
390390 ] == [data3 , data1 , data2 ]
391391
392+ vector_query_future = concurrent .futures .Future ()
393+
392394 def on_snapshot (docs , changes , read_time ):
393395 on_snapshot .results += docs
396+ if len (on_snapshot .results ) >= 3 and not vector_query_future .done ():
397+ vector_query_future .set_result (True )
394398
395399 on_snapshot .results = []
396400 client .collection (collection_id ).order_by ("embedding" ).on_snapshot (on_snapshot )
397401
398- # delay here so initial on_snapshot occurs and isn't combined with set
399- sleep (1 )
400- assert [v .to_dict () for v in on_snapshot .results ] == [data3 , data1 , data2 ]
402+ vector_query_future .result (timeout = 60.0 )
403+ assert [v .to_dict () for v in on_snapshot .results [:3 ]] == [data3 , data1 , data2 ]
401404
402405
403406@pytest .mark .skipif (FIRESTORE_EMULATOR , reason = "Require index and seed data" )
@@ -2336,15 +2339,13 @@ def test_watch_document(client, cleanup, database):
23362339 doc_ref .set ({"first" : "Jane" , "last" : "Doe" , "born" : 1900 })
23372340 cleanup (doc_ref .delete )
23382341
2339- # TODO(https://github.com/googleapis/google-cloud-python/issues/17428):
2340- # Investigate why these sleep/polling delays are needed for listener tests.
2341- # Having arbitrary delays is fragile and can lead to flakiness.
2342- # Explore event-driven synchronization.
2343- sleep (0.2 )
2344-
23452342 # Setup listener
2343+ ada_future = concurrent .futures .Future ()
2344+
23462345 def on_snapshot (docs , changes , read_time ):
23472346 on_snapshot .called_count += 1
2347+ if docs and docs [0 ].get ("first" ) == "Ada" and not ada_future .done ():
2348+ ada_future .set_result (True )
23482349
23492350 on_snapshot .called_count = 0
23502351
@@ -2353,12 +2354,8 @@ def on_snapshot(docs, changes, read_time):
23532354 # Alter document
23542355 doc_ref .set ({"first" : "Ada" , "last" : "Lovelace" , "born" : 1815 })
23552356
2356- sleep (0.2 )
2357-
2358- for _ in range (50 ):
2359- if on_snapshot .called_count > 0 :
2360- break
2361- sleep (0.2 )
2357+ # Wait for the event-driven callback to resolve the future
2358+ ada_future .result (timeout = 60.0 )
23622359
23632360 if on_snapshot .called_count not in (1 , 2 ):
23642361 raise AssertionError (
@@ -2378,34 +2375,18 @@ def test_watch_collection(client, cleanup, database):
23782375 cleanup (doc_ref .delete )
23792376
23802377 # Setup listener
2378+ born_1815_future = concurrent .futures .Future ()
2379+
23812380 def on_snapshot (docs , changes , read_time ):
2382- on_snapshot .called_count += 1
23832381 for doc in [doc for doc in docs if doc .id == doc_ref .id ]:
2384- on_snapshot .born = doc .get ("born" )
2385-
2386- on_snapshot .called_count = 0
2387- on_snapshot .born = 0
2382+ if doc .get ("born" ) == 1815 and not born_1815_future .done ():
2383+ born_1815_future .set_result (True )
23882384
23892385 collection_ref .on_snapshot (on_snapshot )
23902386
2391- # TODO(https://github.com/googleapis/google-cloud-python/issues/17428):
2392- # Investigate why these sleep/polling delays are needed for listener tests.
2393- # Having arbitrary delays is fragile and can lead to flakiness.
2394- # Explore event-driven synchronization.
2395- # delay here so initial on_snapshot occurs and isn't combined with set
2396- sleep (0.2 )
2397-
23982387 doc_ref .set ({"first" : "Ada" , "last" : "Lovelace" , "born" : 1815 })
23992388
2400- for _ in range (50 ):
2401- if on_snapshot .born == 1815 :
2402- break
2403- sleep (0.2 )
2404-
2405- if on_snapshot .born != 1815 :
2406- raise AssertionError (
2407- "Expected the last document update to update born: " + str (on_snapshot .born )
2408- )
2389+ born_1815_future .result (timeout = 60.0 )
24092390
24102391
24112392@pytest .mark .parametrize ("database" , TEST_DATABASES , indirect = True )
@@ -2419,20 +2400,21 @@ def test_watch_query(client, cleanup, database):
24192400 doc_ref .set ({"first" : "Jane" , "last" : "Doe" , "born" : 1900 })
24202401 cleanup (doc_ref .delete )
24212402
2422- # TODO(https://github.com/googleapis/google-cloud-python/issues/17428):
2423- # Investigate why these sleep/polling delays are needed for listener tests.
2424- # Having arbitrary delays is fragile and can lead to flakiness.
2425- # Explore event-driven synchronization.
2426- sleep (0.2 )
2427-
24282403 # Setup listener
2404+ one_doc_future = concurrent .futures .Future ()
2405+
24292406 def on_snapshot (docs , changes , read_time ):
24302407 on_snapshot .called_count += 1
24312408
2432- # A snapshot should return the same thing as if a query ran now.
2433- query_ran_query = collection_ref .where (filter = FieldFilter ("first" , "==" , "Ada" ))
2434- query_ran = query_ran_query .stream ()
2435- assert len (docs ) == len ([i for i in query_ran ])
2409+ if docs :
2410+ # A snapshot should return the same thing as if a query ran now.
2411+ query_ran_query = collection_ref .where (
2412+ filter = FieldFilter ("first" , "==" , "Ada" )
2413+ )
2414+ query_ran = query_ran_query .stream ()
2415+ assert len (docs ) == len ([i for i in query_ran ])
2416+ if not one_doc_future .done ():
2417+ one_doc_future .set_result (True )
24362418
24372419 on_snapshot .called_count = 0
24382420
@@ -2441,14 +2423,11 @@ def on_snapshot(docs, changes, read_time):
24412423 # Alter document
24422424 doc_ref .set ({"first" : "Ada" , "last" : "Lovelace" , "born" : 1815 })
24432425
2444- for _ in range (50 ):
2445- if on_snapshot .called_count == 1 :
2446- return
2447- sleep (0.2 )
2426+ one_doc_future .result (timeout = 60.0 )
24482427
2449- if on_snapshot .called_count != 1 :
2428+ if on_snapshot .called_count not in ( 1 , 2 ) :
24502429 raise AssertionError (
2451- "Failed to get exactly one document change: count: "
2430+ "Failed to get expected document change count: "
24522431 + str (on_snapshot .called_count )
24532432 )
24542433
@@ -2788,6 +2767,8 @@ def test_watch_query_order(client, cleanup, database):
27882767 )
27892768
27902769 # Setup listener
2770+ five_docs_future = concurrent .futures .Future ()
2771+
27912772 def on_snapshot (docs , changes , read_time ):
27922773 try :
27932774 docs = [i for i in docs if i .id .endswith (UNIQUE_RESOURCE_ID )]
@@ -2808,22 +2789,14 @@ def on_snapshot(docs, changes, read_time):
28082789 assert snapshot .get ("born" ) == query .get ("born" ), (
28092790 "expect the sort order to match, born"
28102791 )
2811- on_snapshot . called_count += 1
2812- on_snapshot . last_doc_count = len ( docs )
2792+ if not five_docs_future . done ():
2793+ five_docs_future . set_result ( True )
28132794 except Exception as e :
2814- on_snapshot .failed = e
2795+ if not five_docs_future .done ():
2796+ five_docs_future .set_exception (e )
28152797
2816- on_snapshot .called_count = 0
2817- on_snapshot .last_doc_count = 0
2818- on_snapshot .failed = None
28192798 query_ref .on_snapshot (on_snapshot )
28202799
2821- # TODO(https://github.com/googleapis/google-cloud-python/issues/17428):
2822- # Investigate why these sleep/polling delays are needed for listener tests.
2823- # Having arbitrary delays is fragile and can lead to flakiness.
2824- # Explore event-driven synchronization.
2825- sleep (0.2 )
2826-
28272800 doc_ref1 .set ({"first" : "Ada" , "last" : "Lovelace" , "born" : 1815 })
28282801 cleanup (doc_ref1 .delete )
28292802
@@ -2839,18 +2812,8 @@ def on_snapshot(docs, changes, read_time):
28392812 doc_ref5 .set ({"first" : "Ada" , "last" : "lovelace" , "born" : 1815 })
28402813 cleanup (doc_ref5 .delete )
28412814
2842- for _ in range (50 ):
2843- if on_snapshot .last_doc_count == 5 :
2844- break
2845- sleep (0.2 )
2846-
2847- if on_snapshot .failed :
2848- raise on_snapshot .failed
2849-
2850- if on_snapshot .last_doc_count != 5 :
2851- raise AssertionError (
2852- "5 docs expected in snapshot method " + str (on_snapshot .last_doc_count )
2853- )
2815+ # Wait for the future to be resolved
2816+ five_docs_future .result (timeout = 60.0 )
28542817
28552818
28562819@pytest .mark .parametrize ("database" , TEST_DATABASES_W_ENTERPRISE , indirect = True )
0 commit comments