11"""Unit tests for firebase_client module."""
22
3+ import copy
34import os
45import sys
56import tempfile
@@ -77,10 +78,14 @@ def setUp(self):
7778 self .addCleanup (patcher .stop )
7879
7980 # Set up the mocks for the database refs.
81+ self ._mock_presence_ref = MagicMock ()
82+ self ._mock_presence_ref .get .return_value = None
83+ self ._mock_active_ref = MagicMock ()
8084 self ._mock_register_ref = MagicMock ()
8185 self ._fake_subscribe_ref = FakeReference ()
8286 self ._mock_db_ref .side_effect = [
83- self ._mock_register_ref , self ._fake_subscribe_ref
87+ self ._mock_presence_ref , self ._mock_register_ref ,
88+ self ._fake_subscribe_ref
8489 ]
8590
8691 def tearDown (self ):
@@ -139,18 +144,46 @@ def testStart(self):
139144 self ._mock_initialize_app .assert_called_with (
140145 None , {'databaseURL' : f'https://{ TEST_PROJECT_ID } -cdbg.firebaseio.com' })
141146 self .assertEqual ([
147+ call (f'cdbg/debuggees/{ debuggee_id } /registrationTimeUnixMsec' ),
142148 call (f'cdbg/debuggees/{ debuggee_id } ' ),
143149 call (f'cdbg/breakpoints/{ debuggee_id } /active' )
144150 ], self ._mock_db_ref .call_args_list )
145151
146152 # Verify that the register call has been made.
147- self ._mock_register_ref .set .assert_called_once_with (
148- self ._client ._GetDebuggee ())
153+ expected_data = copy .deepcopy (self ._client ._GetDebuggee ())
154+ expected_data ['registrationTimeUnixMsec' ] = {'.sv' : 'timestamp' }
155+ expected_data ['lastUpdateTimeUnixMsec' ] = {'.sv' : 'timestamp' }
156+ self ._mock_register_ref .set .assert_called_once_with (expected_data )
157+
158+ def testStartAlreadyPresent (self ):
159+ # Create a mock for just this test that claims the debuggee is registered.
160+ mock_presence_ref = MagicMock ()
161+ mock_presence_ref .get .return_value = 'present!'
162+
163+ self ._mock_db_ref .side_effect = [
164+ mock_presence_ref , self ._mock_active_ref , self ._fake_subscribe_ref
165+ ]
166+
167+ self ._client .SetupAuth (project_id = TEST_PROJECT_ID )
168+ self ._client .Start ()
169+ self ._client .subscription_complete .wait ()
170+
171+ debuggee_id = self ._client ._debuggee_id
172+
173+ self .assertEqual ([
174+ call (f'cdbg/debuggees/{ debuggee_id } /registrationTimeUnixMsec' ),
175+ call (f'cdbg/debuggees/{ debuggee_id } /lastUpdateTimeUnixMsec' ),
176+ call (f'cdbg/breakpoints/{ debuggee_id } /active' )
177+ ], self ._mock_db_ref .call_args_list )
178+
179+ # Verify that the register call has been made.
180+ self ._mock_active_ref .set .assert_called_once_with ({'.sv' : 'timestamp' })
149181
150182 def testStartRegisterRetry (self ):
151- # A new db ref is fetched on each retry.
183+ # A new set of db refs are fetched on each retry.
152184 self ._mock_db_ref .side_effect = [
153- self ._mock_register_ref , self ._mock_register_ref ,
185+ self ._mock_presence_ref , self ._mock_register_ref ,
186+ self ._mock_presence_ref , self ._mock_register_ref ,
154187 self ._fake_subscribe_ref
155188 ]
156189
@@ -169,6 +202,7 @@ def testStartSubscribeRetry(self):
169202
170203 # A new db ref is fetched on each retry.
171204 self ._mock_db_ref .side_effect = [
205+ self ._mock_presence_ref ,
172206 self ._mock_register_ref ,
173207 mock_subscribe_ref , # Fail the first time
174208 self ._fake_subscribe_ref # Succeed the second time
@@ -178,7 +212,28 @@ def testStartSubscribeRetry(self):
178212 self ._client .Start ()
179213 self ._client .subscription_complete .wait ()
180214
181- self .assertEqual (3 , self ._mock_db_ref .call_count )
215+ self .assertEqual (4 , self ._mock_db_ref .call_count )
216+
217+ def testMarkActiveTimer (self ):
218+ # Make sure that there are enough refs queued up.
219+ refs = list (self ._mock_db_ref .side_effect )
220+ refs .extend ([self ._mock_active_ref ] * 10 )
221+ self ._mock_db_ref .side_effect = refs
222+
223+ # Speed things WAY up rather than waiting for hours.
224+ self ._client ._mark_active_interval_sec = 0.1
225+
226+ self ._client .SetupAuth (project_id = TEST_PROJECT_ID )
227+ self ._client .Start ()
228+ self ._client .subscription_complete .wait ()
229+
230+ # wait long enough for the timer to trigger a few times.
231+ time .sleep (0.5 )
232+
233+ print (f'Timer triggered { self ._mock_active_ref .set .call_count } times' )
234+ self .assertTrue (self ._mock_active_ref .set .call_count > 3 )
235+ self ._mock_active_ref .set .assert_called_with ({'.sv' : 'timestamp' })
236+
182237
183238 def testBreakpointSubscription (self ):
184239 # This class will keep track of the breakpoint updates and will check
@@ -219,12 +274,10 @@ def callback(self, new_breakpoints):
219274 },
220275 ]
221276
222- expected_results = [[breakpoints [0 ]],
223- [breakpoints [0 ], breakpoints [1 ]],
277+ expected_results = [[breakpoints [0 ]], [breakpoints [0 ], breakpoints [1 ]],
224278 [breakpoints [0 ], breakpoints [1 ], breakpoints [2 ]],
225279 [breakpoints [1 ], breakpoints [2 ]],
226- [breakpoints [1 ], breakpoints [2 ]]
227- ]
280+ [breakpoints [1 ], breakpoints [2 ]]]
228281 result_checker = ResultChecker (expected_results , self )
229282
230283 self ._client .on_active_breakpoints_changed = result_checker .callback
@@ -257,8 +310,9 @@ def testEnqueueBreakpointUpdate(self):
257310 final_ref_mock = MagicMock ()
258311
259312 self ._mock_db_ref .side_effect = [
260- self ._mock_register_ref , self ._fake_subscribe_ref , active_ref_mock ,
261- snapshot_ref_mock , final_ref_mock
313+ self ._mock_presence_ref , self ._mock_register_ref ,
314+ self ._fake_subscribe_ref , active_ref_mock , snapshot_ref_mock ,
315+ final_ref_mock
262316 ]
263317
264318 self ._client .SetupAuth (project_id = TEST_PROJECT_ID )
@@ -316,13 +370,13 @@ def testEnqueueBreakpointUpdate(self):
316370 db_ref_calls = self ._mock_db_ref .call_args_list
317371 self .assertEqual (
318372 call (f'cdbg/breakpoints/{ debuggee_id } /active/{ breakpoint_id } ' ),
319- db_ref_calls [2 ])
373+ db_ref_calls [3 ])
320374 self .assertEqual (
321375 call (f'cdbg/breakpoints/{ debuggee_id } /snapshot/{ breakpoint_id } ' ),
322- db_ref_calls [3 ])
376+ db_ref_calls [4 ])
323377 self .assertEqual (
324378 call (f'cdbg/breakpoints/{ debuggee_id } /final/{ breakpoint_id } ' ),
325- db_ref_calls [4 ])
379+ db_ref_calls [5 ])
326380
327381 active_ref_mock .delete .assert_called_once ()
328382 snapshot_ref_mock .set .assert_called_once_with (full_breakpoint )
@@ -333,8 +387,8 @@ def testEnqueueBreakpointUpdateWithLogpoint(self):
333387 final_ref_mock = MagicMock ()
334388
335389 self ._mock_db_ref .side_effect = [
336- self ._mock_register_ref , self ._fake_subscribe_ref , active_ref_mock ,
337- final_ref_mock
390+ self ._mock_presence_ref , self ._mock_register_ref ,
391+ self . _fake_subscribe_ref , active_ref_mock , final_ref_mock
338392 ]
339393
340394 self ._client .SetupAuth (project_id = TEST_PROJECT_ID )
@@ -383,10 +437,10 @@ def testEnqueueBreakpointUpdateWithLogpoint(self):
383437 db_ref_calls = self ._mock_db_ref .call_args_list
384438 self .assertEqual (
385439 call (f'cdbg/breakpoints/{ debuggee_id } /active/{ breakpoint_id } ' ),
386- db_ref_calls [2 ])
440+ db_ref_calls [3 ])
387441 self .assertEqual (
388442 call (f'cdbg/breakpoints/{ debuggee_id } /final/{ breakpoint_id } ' ),
389- db_ref_calls [3 ])
443+ db_ref_calls [4 ])
390444
391445 active_ref_mock .delete .assert_called_once ()
392446 final_ref_mock .set .assert_called_once_with (output_breakpoint )
@@ -414,6 +468,7 @@ def testEnqueueBreakpointUpdateRetry(self):
414468 ]
415469
416470 self ._mock_db_ref .side_effect = [
471+ self ._mock_presence_ref ,
417472 self ._mock_register_ref ,
418473 self ._fake_subscribe_ref , # setup
419474 active_ref_mock , # attempt 1
0 commit comments