def test_set_continue_no_breaks(self): """ Asserts that set_continue works with no breakpoints. """ cmd_manager = QueueCommandManager() db = Qdb(cmd_manager=cmd_manager) cmd_manager.enqueue(lambda t: t.set_continue()) cmd_manager.user_wait(0.2) line_1 = line_2 = line_3 = False with Timeout(0.1, False): db.set_trace() line_1 = True # EDIT IN BOTH PLACES line_2 = True line_3 = True # Assert that we hit all three lines event though interaction is # blocked. self.assertTrue(line_1 and line_2 and line_3) # As this was the last time we were supposed to stop, this should be # the curframe data. self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' line_1 = True # EDIT IN BOTH PLACES' )
def test_set_continue_no_breaks(self): """ Asserts that set_continue works with no breakpoints. """ db = Qdb(cmd_manager=QueueCommandManager) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) line_1 = line_2 = line_3 = False with Timeout(0.1, False): db.set_trace() line_1 = True # EDIT IN BOTH PLACES line_2 = True line_3 = True # Assert that we hit all three lines event though interaction is # blocked. self.assertTrue(line_1 and line_2 and line_3) # As this was the last time we were supposed to stop, this should be # the curframe data. self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' line_1 = True # EDIT IN BOTH PLACES' )
def test_set_step(self): """ Tests the functionality of set_step by asserting that it only executes the next line and no more. """ db = Qdb(cmd_manager=QueueCommandManager) # Queue up a step command. db.cmd_manager.enqueue(lambda t: t.set_step()) stepped = False with Timeout(0.1, False): db.set_trace() stepped = True self.assertTrue(stepped) db.disable() db = Qdb(cmd_manager=QueueCommandManager) db.cmd_manager.enqueue(lambda t: t.set_step()) db.cmd_manager.user_wait(0.2) stepped = over_stepped = False with Timeout(0.1, False): db.set_trace() stepped = True over_stepped = True self.assertTrue(stepped) self.assertFalse(over_stepped)
def test_set_trace_without_stop(self): """ Asserts that calling set_trace with stop=False will start tracing but not stop. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) line_offset = 8 # The difference in the set_break call and line_3. db.set_break( self.filename, sys._getframe().f_lineno + line_offset, ) db.cmd_manager.user_wait(0.2) line_1 = line_2 = line_3 = False with Timeout(0.1, False): db.set_trace(stop=False) # Should not stop us here. line_1 = True line_2 = True line_3 = True # Since we are stepping, we should not hit this line. self.assertTrue(line_1) self.assertTrue(line_2) # We should have still stopped at this breakpoint if we are tracing. self.assertFalse(line_3) db.disable() db = Qdb(cmd_manager=NopCommandManager) line_1 = False with Timeout(0.1, False): db.set_trace(stop=False) line_1 = True self.assertTrue(line_1)
def test_eval_state_update(self): """ Tests that eval may update the state of the program. """ # We will try to corrupt this variable with a stateful operation. test_var = 'pure' # NOQA db = Qdb( uuid='eval_test', cmd_manager=self.cmd_manager, host=self.tracer_host, port=self.tracer_port, redirect_output=False, green=True, ) gyield() self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('eval', "test_var = 'mutated'") ) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('continue') ) db.set_trace(stop=True) self.server.session_store.slaughter(db.uuid) self.assertEqual(test_var, 'mutated')
def test_eval_pprint(self, input_, exc, output): """ Tests that evaling code returns the proper results. """ prints = [] class cmd_manager(type(self.cmd_manager)): """ Captures print commands to make assertions on them. """ def send_print(self, input_, exc, output): prints.append({'input': input_, 'exc': exc, 'output': output}) db = Qdb( uuid='eval_test', cmd_manager=cmd_manager(), host=self.tracer_host, port=self.tracer_port, redirect_output=False, ) gyield() self.server.session_store.send_to_tracer(uuid=db.uuid, event=fmt_msg( 'pprint', input_)) self.server.session_store.send_to_tracer(uuid=db.uuid, event=fmt_msg('continue')) db.set_trace(stop=True) self.server.session_store.slaughter(db.uuid) self.assertTrue(prints) print_ = prints[0] self.assertEqual(print_['input'], input_) self.assertEqual(print_['exc'], exc) self.assertEqual(print_['output'], output)
def test_eval_state_update(self): """ Tests that eval may update the state of the program. """ # We will try to corrupt this variable with a stateful operation. test_var = 'pure' # NOQA db = Qdb( uuid='eval_test', cmd_manager=self.cmd_manager, host=self.tracer_host, port=self.tracer_port, redirect_output=False, ) sleep(0.01) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('eval', "test_var = 'mutated'") ) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('continue') ) db.set_trace(stop=True) self.server.session_store.slaughter(db.uuid) self.assertEqual(test_var, 'mutated')
def test_function_call_next_step(self): """ Tests the functionality of next and step when calling functions. This checks to make sure the function is stepped into and can be stepped over. """ cmd_manager = QueueCommandManager() db = Qdb(cmd_manager=cmd_manager) # Queue up a next command to next over the function call. cmd_manager.enqueue(lambda t: t.set_next(t.curframe)) # Queue up a sleep so that we block after calling next. # This would cause us to NOT execute the f_called[0] = True line of f # had we only called set_step. This is asserted afterwards. cmd_manager.user_wait(0.2) # A mutable structure to check if f is called. f_called = NonLocal(False) def f(): f_called.value = True with Timeout(0.1, False): db.set_trace() f() # We hit that line in f, so it should now be True. self.assertTrue(f_called.value) # Assert that we are currently executing the line we think we should # be executing. Since we are just stepping, this should be setting # curframe each time. self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' db.get_line(self.filename, db.curframe.f_lineno),' ) db.disable() cmd_manager = QueueCommandManager() db = Qdb(cmd_manager=cmd_manager) f_called = NonLocal(False) # This time we will be only stepping, so we should not execute the # entire call to f. cmd_manager.enqueue(lambda t: t.set_step()) cmd_manager.user_wait(1.2) with Timeout(0.1, False): db.set_trace() f() # We should not have hit this line in f. self.assertFalse(f_called.value) # Since we only stepped once, this is the last time we set the frame. self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' f_called.value = True' )
def test_is_singleton(self): """ Tests that two newly created Qdb objects are the same. """ self.assertIs( Qdb(cmd_manager=NopCommandManager()), Qdb(cmd_manager=NopCommandManager()) )
def test_stack_transpose_no_skip(self, direction): """ Tests moving up the stack. """ events = [] def capture_event(self, event, payload): events.append(fmt_msg(event, payload)) class cmd_manager(type(self.cmd_manager)): """ Wrap send_stack by just capturing the output to make assertions on it. """ def send_stack(self, tracer): with patch.object(cmd_manager, 'send_event', capture_event): super(cmd_manager, self).send_stack(tracer) db = Qdb( uuid='test_' + direction, cmd_manager=cmd_manager(), host=self.tracer_host, port=self.tracer_port, redirect_output=False, green=True, ) gyield() if direction == 'down': # We are already located in the bottom frame, let's go up one # so that we may try going down. self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('up') ) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg(direction) ) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('disable', 'soft') ) gyield() db.set_trace() start_ind = events[-2]['p']['index'] shift_ind = events[-1]['p']['index'] if direction == 'up': self.assertEqual(start_ind - shift_ind, 1) elif direction == 'down': self.assertEqual(shift_ind - start_ind, 1) else: self.fail("direction is not 'up' or 'down'") # wut did u do?
def test_eval_timeout(self): """ Tests that evaluating user repl commands will raise Timeouts. """ def g(): while True: pass prints = [] class cmd_manager(type(self.cmd_manager)): """ Captures print commands to make assertions on them. """ def send_print(self, input_, exc, output): prints.append({ 'input': input_, 'exc': exc, 'output': output }) to_eval = 'g()' db = Qdb( uuid='timeout_test', cmd_manager=cmd_manager(), host=self.tracer_host, port=self.tracer_port, redirect_output=False, execution_timeout=1, green=True, ) gyield() self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('eval', to_eval) ) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('continue') ) db.set_trace(stop=True) self.server.session_store.slaughter(db.uuid) self.assertTrue(prints) print_ = prints[0] self.assertEqual(print_['input'], to_eval) self.assertTrue(print_['exc']) self.assertEqual( print_['output'], db.exception_serializer(QdbExecutionTimeout(to_eval, 1)) )
def test_send_stack_results(self, use_skip_fn): """ Tests that the results from sending the stack are accurate. WARNING: This test uses lines of it's own source as string literals, be sure to edit the source and the string if you make any changes. """ def skip_fn(filename): return not fix_filename(__file__) in filename events = [] def capture_event(self, event, payload): events.append(fmt_msg(event, payload)) class cmd_manager(type(self.cmd_manager)): """ Wrap send_stack by just capturing the output to make assertions on it. """ def send_stack(self, tracer): with patch.object(cmd_manager, 'send_event', capture_event): super(cmd_manager, self).send_stack(tracer) db = Qdb( uuid='send_stack_test', cmd_manager=cmd_manager(), host=self.tracer_host, port=self.tracer_port, redirect_output=False, skip_fn=skip_fn if use_skip_fn else None, green=True, ) gyield() self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('continue') ) db.set_trace(stop=True) self.assertTrue(events) # EDIT IN BOTH PLACES event = events[0] if use_skip_fn: # Assert that we actually suppressed some frames. self.assertTrue(len(event['p']['stack']) < len(db.stack)) self.assertEqual( # I love dictionaries so much! event['p']['stack'][event['p']['index']]['code'], ' self.assertTrue(events) # EDIT IN BOTH PLACES', ) self.server.session_store.slaughter(db.uuid)
def test_set_trace_with_stop(self): """ Asserts that calling set_trace will put us into stepping mode. """ db = Qdb(cmd_manager=QueueCommandManager) db.cmd_manager.user_wait(0.2) line_1 = False with Timeout(0.1, False): db.set_trace() line_1 = True # EDIT IN BOTH PLACES # Since we are stepping, we should not hit this line. self.assertFalse(line_1)
def test_clear_break(self): """ Tests clearing a breakpoint. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) clear_break_offset = 14 set_break_offset = 8 db.cmd_manager.enqueue(lambda t: t.clear_break( self.filename, sys._getframe().f_lineno + clear_break_offset)) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) db.set_break(self.filename, sys._getframe().f_lineno + set_break_offset) db.set_trace(stop=False) continued = False with Timeout(0.1): db.set_trace(stop=False) for n in xrange(2): pass continued = True self.assertTrue(continued)
def test_conditional_breakpoint_timeout(self): """ Tests conditional breakpoints that cause timeouts. WARNING: This test relies on the relative line numbers inside the test. """ stopped = [False] def stop(): stopped[0] = True return True # Execute the assertion. line = None cond = 'g()' cmd_manager = QueueCommandManager() db = Qdb(cmd_manager=cmd_manager, execution_timeout=1) cmd_manager.enqueue(lambda t: stop() and self.assertEqual(line, 1)) line_offset = 10 # Set a condition that will time out. db.set_break( self.filename, sys._getframe().f_lineno + line_offset, cond='g()', ) db.set_trace(stop=False) def g(): while True: pass line = 1 line = 2 line = 3 db.disable() errors = [e['p'] for e in cmd_manager.sent if e['e'] == 'error'] self.assertEqual(len(errors), 1) error = errors[0] self.assertEqual(error['type'], 'condition') negative_line_offset = 14 exc = QdbExecutionTimeout(cond, db.execution_timeout) self.assertEqual( error['data'], { 'line': sys._getframe().f_lineno - negative_line_offset, 'cond': cond, 'exc': type(exc).__name__, 'output': db.exception_serializer(exc), } ) # Make sure we stopped when we raised the exception. self.assertTrue(stopped[0])
def test_as_ctx_mgr(self): """ Tests the debugger as a context manager. """ line_1 = False cmd_stop = None with patch.object(NopCommandManager, 'start') as cmd_start, \ patch.object(NopCommandManager, 'stop') as cmd_stop, \ Qdb(cmd_manager=NopCommandManager()) as db: db.set_trace() cmd_start.assert_called_once_with(db, '') line_1 = True self.assertTrue(line_1) self.assertIs(Qdb._instance, db) self.assertEqual( db.get_line( self.filename, db.curframe.f_lineno), ' db.curframe.f_lineno),' ) # Assert the __exit__ clears the singleton so a new one can be used. self.assertIs(Qdb._instance, None) # Assert that __exit__ stopped the command manager. cmd_stop.assert_called_once_with()
def test_pause(self): """ Asserts that sending a pause to the process will raise the pause signal in the tracer process. """ pause_called = [False] def pause_handler(signal, stackframe): """ Pause handler that marks that we made it into this function. """ pause_called[0] = True db = Qdb( cmd_manager=self.cmd_manager, host=self.tracer_host, port=self.tracer_port, green=True, ) signal.signal(db.pause_signal, pause_handler) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('pause') ) self.assertTrue(pause_called)
def test_conditional_breakpoint_raises(self): """ Tests conditional breakpoints that raise an exception. WARNING: This test relies on the relative line numbers inside the test. """ line = None exc = ValueError('lol wut r u doing?') cond = 'raiser()' stopped = [False] def stop(): stopped[0] = True return True # Execute the assertion. cmd_manager = QueueCommandManager() db = Qdb(cmd_manager=cmd_manager) cmd_manager.enqueue(lambda t: stop() and self.assertEqual(line, 1)) line_offset = 9 # Set a condition that will raise a ValueError. db.set_break( self.filename, sys._getframe().f_lineno + line_offset, cond=cond, ) db.set_trace(stop=False) def raiser(): raise exc line = 1 line = 2 # This line number is used in the data assertion. line = 3 db.disable() errors = [e['p'] for e in cmd_manager.sent if e['e'] == 'error'] self.assertEqual(len(errors), 1) error = errors[0] self.assertEqual(error['type'], 'condition') negative_line_offset = 13 self.assertEqual( error['data'], { 'line': sys._getframe().f_lineno - negative_line_offset, 'cond': cond, 'exc': type(exc).__name__, 'output': db.exception_serializer(exc), } ) # Make sure we stopped when we raised the exception. self.assertTrue(stopped[0])
def test_conditional_breakpoint(self): """ Tests valid conditional breakpoints. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) loop_counter = 0 db.cmd_manager.enqueue(lambda t: self.assertEqual(loop_counter, 5)) line_offset = 5 db.set_break( self.filename, sys._getframe().f_lineno + line_offset, cond='loop_counter == 5' ) db.set_trace(stop=False) while loop_counter < 10: loop_counter += 1
def test_redirect_stderr(self): """ Tests that stderr is stored on the tracer. """ sys.stderr = stderr = StringIO() db = Qdb(cmd_manager=NopCommandManager) data_to_write = 'stderr' db.set_trace(stop=False) print >> sys.stderr, data_to_write, # Write some data to stderr. db.disable() self.assertEqual(db.stderr.getvalue(), data_to_write) # Assert that the stream was restored. self.assertIs(sys.stderr, stderr)
def test_clear_break(self): """ Tests clearing a breakpoint. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) clear_break_offset = 14 set_break_offset = 8 db.cmd_manager.enqueue(lambda t: t.clear_break( self.filename, sys._getframe().f_lineno + clear_break_offset )) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) db.set_break( self.filename, sys._getframe().f_lineno + set_break_offset ) db.set_trace(stop=False) continued = False with Timeout(0.1): db.set_trace(stop=False) for n in xrange(2): pass continued = True self.assertTrue(continued)
def test_why_are_you_executing_all_these_commands(self): db = Qdb( uuid='send_stack_test', cmd_manager=self.cmd_manager, host=self.tracer_host, port=self.tracer_port, redirect_output=False, green=True, ) gyield() for n in range(sys.getrecursionlimit()): self.server.session_store.send_to_tracer(uuid=db.uuid, event=fmt_msg( 'eval', 'None')) self.server.session_store.send_to_tracer(uuid=db.uuid, event=fmt_msg('continue')) with gevent.Timeout(1): db.set_trace(stop=True)
def test_eval_results(self, input_, exc, output): """ Tests that evaling code returns the proper results. """ prints = [] class cmd_manager(type(self.cmd_manager)): """ Captures print commands to make assertions on them. """ def send_print(self, input_, exc, output): prints.append({ 'input': input_, 'exc': exc, 'output': output }) db = Qdb( uuid='eval_test', cmd_manager=cmd_manager(), host=self.tracer_host, port=self.tracer_port, redirect_output=False, green=True, ) gyield() self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('eval', input_) ) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('continue') ) db.set_trace(stop=True) self.server.session_store.slaughter(db.uuid) self.assertTrue(prints) print_ = prints[0] self.assertEqual(print_['input'], input_) self.assertEqual(print_['exc'], exc) self.assertEqual(print_['output'], output)
def test_file_cache_from_string(self): """ Asserts that manual caching from a string works. """ contents = dedent( """\ line 1 line 2 line 3 line 4 """ ) db = Qdb(cmd_manager=NopCommandManager()) db.cache_file('file', contents=contents) # Check the whole 'file'. self.assertEquals(db.get_file('file'), contents[:-1]) # drop '\n' for n in range(1, 5): # Check all the lines. self.assertEquals('line %d' % n, db.get_line('file', n))
def test_why_are_you_executing_all_these_commands(self): db = Qdb( uuid='send_stack_test', cmd_manager=self.cmd_manager, host=self.tracer_host, port=self.tracer_port, redirect_output=False, green=True, ) gyield() for n in range(sys.getrecursionlimit()): self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('eval', 'None') ) self.server.session_store.send_to_tracer( uuid=db.uuid, event=fmt_msg('continue') ) with gevent.Timeout(1): db.set_trace(stop=True)
def test_redirect_stdout(self): """ Tests that stdout is stored on the tracer. """ sys.stdout = stdout = StringIO() db = Qdb(cmd_manager=OutputCatchingNopCommandManager) data_to_write = 'stdout' db.set_trace(stop=False) print data_to_write, # Write some data to stdout. db.disable() msg = db.cmd_manager.msgs[0] self.assertEqual(msg.input_, '<stdout>') self.assertFalse(msg.exc) self.assertEqual(msg.output, data_to_write) self.assertEqual(stdout.getvalue(), data_to_write) # Assert that the stream was restored. self.assertIs(sys.stdout, stdout)
def test_inject_default_ns_no_trample(self): """ Tests adding the default namespace does not override a defined name. """ ns = {'a': 1, 'b': 2} with Qdb(cmd_manager=NopCommandManager, default_namespace=ns) as db, \ db.inject_default_namespace(sys._getframe()): a = 'a' b = 'b' self.assertEqual(a, 'a') self.assertEqual(b, 'b')
def test_file_cache_from_disk(self): """ Asserts that the disk caching works. """ # We will use this file, as it is the only file we know that exists. # The first time this is run after a change, __file__ will point to # the source code file; however, if we run this twice in a row, it # points to the byte-compiled file. filename = fix_filename(__file__) db = Qdb(cmd_manager=NopCommandManager()) db.cache_file(filename) with open(filename) as f: contents = f.read()[:-1] # Drop the last newline. # Assert that querying the entire file works. self.assertEquals(db.get_file(filename), contents) for n, line in zip(count(start=1), contents.splitlines()): # Iterate over all the lines of the file, asserting that we # have saved them correctly. This also asserts that the line # indexing is working as intended. self.assertEquals(db.get_line(filename, n), line)
def test_send_disabled(self): """ Tests that disabling sends a 'disabled' message back to the server. """ class cmd_manager(self.cmd_manager): disabled = False def send_disabled(self): self.disabled = True db = Qdb( uuid='send_disabled_test', cmd_manager=cmd_manager, host=self.tracer_host, port=self.tracer_port, redirect_output=False, ) sleep(0.01) db.set_trace(stop=False) db.disable() self.assertTrue(db.cmd_manager.disabled) self.server.session_store.slaughter(db.uuid)
def test_temporary_breakpoint(self): """ Tests conditional breakpoints. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) loop_counter = 0 line_offset = 6 db.set_break( self.filename, sys._getframe().f_lineno + line_offset, temporary=True, ) with Timeout(0.1, False): db.set_trace(stop=False) while loop_counter < 10: loop_counter += 1 # By hitting it the first time, we cleared the breakpoint and did not # stop there again. self.assertEqual(loop_counter, 10)
def test_redirect_stderr(self): """ Tests that stderr is stored on the tracer. """ sys.stderr = stderr = StringIO() cmd_manager = OutputCatchingNopCommandManager() db = Qdb(cmd_manager=cmd_manager) data_to_write = 'stderr' db.set_trace(stop=False) # Write some data to stderr. print(data_to_write, end='', file=sys.stderr) db.disable() msg = cmd_manager.msgs[0] self.assertEqual(msg.input_, '<stderr>') self.assertFalse(msg.exc) self.assertEqual(msg.output, data_to_write) self.assertEqual(stderr.getvalue(), data_to_write) # Assert that the stream was restored. self.assertIs(sys.stderr, stderr)
def test_inject_default_ns(self): """ Tests adding a default namespace to a frame. """ ns = {'a': 1, 'b': 2} # rip pyflakes with Qdb(cmd_manager=NopCommandManager, default_namespace=ns) as db, \ db.inject_default_namespace(sys._getframe()): self.assertEqual(a, 1) # NOQA self.assertEqual(b, 2) # NOQA # Assert that the namespace was cleaned. with self.assertRaises(NameError): a # NOQA with self.assertRaises(NameError): b # NOQA
def test_inject_default_ns_curframe(self): """ Tests adding a default namespace to the curframe. """ ns = {'a': 1, 'b': 2} # rip pyflakes with Qdb(cmd_manager=NopCommandManager, default_namespace=ns) as db: db.curframe = sys._getframe() with db.inject_default_namespace(): self.assertEqual(a, 1) # NOQA self.assertEqual(b, 2) # NOQA with self.assertRaises(NameError): a # NOQA with self.assertRaises(NameError): b # NOQA
def test_conditional_breakpoint(self): """ Tests valid conditional breakpoints. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) loop_counter = 0 db.cmd_manager.enqueue(lambda t: self.assertEqual(loop_counter, 5)) line_offset = 5 db.set_break(self.filename, sys._getframe().f_lineno + line_offset, cond='loop_counter == 5') db.set_trace(stop=False) while loop_counter < 10: loop_counter += 1
def test_clear_output_buffers(self): """ Tests that we can clear the output buffers to free up some memory. """ db = Qdb(cmd_manager=NopCommandManager) stdout_data, stderr_data = 'stdout', 'stderr' db.set_trace(stop=False) print stdout_data, print >> sys.stderr, stderr_data, db.disable() # Assert that the data actually got written. self.assertEqual(db.stdout.getvalue(), stdout_data) self.assertEqual(db.stderr.getvalue(), stderr_data) db.clear_output_buffers() # Assert that the data actually got cleared. self.assertEqual(db.stdout.getvalue(), '') self.assertEqual(db.stderr.getvalue(), '')
def test_file_cache_from_string(self): """ Asserts that manual caching from a string works. """ contents = dedent("""\ line 1 line 2 line 3 line 4 """) db = Qdb(cmd_manager=NopCommandManager()) db.cache_file('file', contents=contents) # Check the whole 'file'. self.assertEquals(db.get_file('file'), contents[:-1]) # drop '\n' for n in range(1, 5): # Check all the lines. self.assertEquals('line %d' % n, db.get_line('file', n))
def test_file_cache_from_disk(self): """ Asserts that the disk caching works. """ # We will use this file, as it is the only file we know that exists. # The first time this is run after a change, __file__ will point to # the source code file; however, if we run this twice in a row, it # points to the byte-compiled file. filename = fix_filename(__file__) db = Qdb(cmd_manager=NopCommandManager()) db.cache_file(filename) with open(filename) as f: contents = f.read()[:-1] # Drop the last newline. # Assert that querying the entire file works. self.assertEqual(db.get_file(filename), contents) for n, line in zip(count(start=1), contents.splitlines()): # Iterate over all the lines of the file, asserting that we # have saved them correctly. This also asserts that the line # indexing is working as intended. self.assertEqual(db.get_line(filename, n), line)
def test_redirect_stdout(self): """ Tests that stdout is stored on the tracer. """ sys.stdout = stdout = StringIO() cmd_manager = OutputCatchingNopCommandManager() db = Qdb(cmd_manager=cmd_manager) data_to_write = 'stdout' db.set_trace(stop=False) # Write some data to stdout. print(data_to_write, end='') db.disable() msg = cmd_manager.msgs[0] self.assertEqual(msg.input_, '<stdout>') self.assertFalse(msg.exc) self.assertEqual(msg.output, data_to_write) self.assertEqual(stdout.getvalue(), data_to_write) # Assert that the stream was restored. self.assertIs(sys.stdout, stdout)
def test_function_call_next_step(self): """ Tests the functionality of next and step when calling functions. This checks to make sure the function is stepped into and can be stepped over. """ db = Qdb(cmd_manager=QueueCommandManager) # Queue up a next command to next over the function call. db.cmd_manager.enqueue(lambda t: t.set_next(t.curframe)) # Queue up a sleep so that we block after calling next. # This would cause us to NOT execute the f_called[0] = True line of f # had we only called set_step. This is asserted afterwards. db.cmd_manager.user_wait(0.2) # A mutable structure to check if f is called. f_called = [False] def f(): f_called[0] = True with Timeout(0.1, False): db.set_trace() f() # We hit that line in f, so it should now be True. self.assertTrue(f_called[0]) # Assert that we are currently executing the line we think we should # be executing. Since we are just stepping, this should be setting # curframe each time. self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' db.get_line(self.filename, db.curframe.f_lineno),') db.disable() db = Qdb(cmd_manager=QueueCommandManager) f_called[0] = False # This time we will be only stepping, so we should not execute the # entire call to f. db.cmd_manager.enqueue(lambda t: t.set_step()) db.cmd_manager.user_wait(0.2) with Timeout(0.1, False): db.set_trace() f() # We should not have hit this line in f. self.assertFalse(f_called[0]) # Since we only stepped once, this is the last time we set the frame. self.assertEqual(db.get_line(self.filename, db.curframe.f_lineno), ' f_called[0] = True')
def test_set_continue_with_breaks(self): """ Tests the behavior of continue when there are breakpoints in the mix. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) line_offset = 8 # The difference in the set_break call and line_2. db.set_break( self.filename, sys._getframe().f_lineno + line_offset, ) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) line_1 = line_2 = line_3 = False with Timeout(0.1, False): db.set_trace() line_1 = True line_2 = True line_3 = True # Assert we only got to line_1 because of the breakpoint. # These are split up to give more helpful messages if the test fails. self.assertTrue(line_1) self.assertFalse(line_2) self.assertFalse(line_3) # We are still in stepping mode so we should be reporting the stack. self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' db.get_line(self.filename, db.curframe.f_lineno),') sys.settrace(None) db = Qdb(cmd_manager=QueueCommandManager) line_2_offset = 13 # The difference in the set_break call and line_2. line_3_offset = 10 # THe difference in the set_break call and line_3. db.set_break( self.filename, sys._getframe().f_lineno + line_2_offset, ) db.set_break( self.filename, sys._getframe().f_lineno + line_3_offset, ) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) line_1 = line_2 = line_3 = False with Timeout(0.1, False): db.set_trace() line_1 = True line_2 = True line_3 = True self.assertTrue(line_1) self.assertTrue(line_2) self.assertFalse(line_3) self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' db.get_line(self.filename, db.curframe.f_lineno),')
def test_watchlist(self): """ Tests the watchlist by evaluating a constant, local function, local variable, global function, and global variable. """ db = Qdb(cmd_manager=NopCommandManager, execution_timeout=1) too_long_msg = db.exception_serializer( QdbExecutionTimeout('too_long()', 1)) db.extend_watchlist( '2 + 2', 'local_var', 'local_fn()', 'global_var', 'global_fn()', 'too_long()', ) def new_curframe(): """ Test function for checking for NameErrors on the watchlist. This changes the curframe of the tracer to eval the watchlist with a new set of locals. """ self.assertEqual(db.watchlist['2 + 2'], (None, 4)) self.assertEqual( db.watchlist['local_var'], ('NameError', "NameError: name 'local_var' is not defined")) self.assertEqual( db.watchlist['local_fn()'], ('NameError', "NameError: name 'local_fn' is not defined")) self.assertEqual(db.watchlist['global_var'], (None, 'global_var')) self.assertEqual(db.watchlist['global_fn()'], (None, 'global_fn')) local_var = 'local_var' # NOQA local_fn = lambda: 'local_fn' # NOQA def too_long(): while True: pass # Set trace and check innitial assertions. db.set_trace() self.assertEqual(db.watchlist['2 + 2'], (None, 4)) self.assertEqual(db.watchlist['local_var'], (None, 'local_var')) self.assertEqual(db.watchlist['local_fn()'], (None, 'local_fn')) self.assertEqual(db.watchlist['global_var'], (None, 'global_var')) self.assertEqual(db.watchlist['global_fn()'], (None, 'global_fn')) # Testing this as a tuple causes strange behavior. self.assertEqual(db.watchlist['too_long()'][0], 'QdbExecutionTimeout') self.assertEqual(db.watchlist['too_long()'][1], too_long_msg) local_var = 'updated_local_var' # NOQA local_fn = lambda: 'updated_local_fn' # NOQA self.assertEqual(db.watchlist['2 + 2'], (None, 4)) self.assertEqual(db.watchlist['local_var'], (None, 'updated_local_var')) self.assertEqual(db.watchlist['local_fn()'], (None, 'updated_local_fn')) self.assertEqual(db.watchlist['global_var'], (None, 'global_var')) self.assertEqual(db.watchlist['global_fn()'], (None, 'global_fn')) new_curframe()
def test_set_continue_with_breaks(self): """ Tests the behavior of continue when there are breakpoints in the mix. WARNING: This test relies on the relative line numbers inside the test. """ db = Qdb(cmd_manager=QueueCommandManager) line_offset = 8 # The difference in the set_break call and line_2. db.set_break( self.filename, sys._getframe().f_lineno + line_offset, ) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) line_1 = line_2 = line_3 = False with Timeout(0.1, False): db.set_trace() line_1 = True line_2 = True line_3 = True # Assert we only got to line_1 because of the breakpoint. # These are split up to give more helpful messages if the test fails. self.assertTrue(line_1) self.assertFalse(line_2) self.assertFalse(line_3) # We are still in stepping mode so we should be reporting the stack. self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' db.get_line(self.filename, db.curframe.f_lineno),' ) sys.settrace(None) db = Qdb(cmd_manager=QueueCommandManager) line_2_offset = 13 # The difference in the set_break call and line_2. line_3_offset = 10 # THe difference in the set_break call and line_3. db.set_break( self.filename, sys._getframe().f_lineno + line_2_offset, ) db.set_break( self.filename, sys._getframe().f_lineno + line_3_offset, ) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.enqueue(lambda t: t.set_continue()) db.cmd_manager.user_wait(0.2) line_1 = line_2 = line_3 = False with Timeout(0.1, False): db.set_trace() line_1 = True line_2 = True line_3 = True self.assertTrue(line_1) self.assertTrue(line_2) self.assertFalse(line_3) self.assertEqual( db.get_line(self.filename, db.curframe.f_lineno), ' db.get_line(self.filename, db.curframe.f_lineno),' )
def test_watchlist(self): """ Tests the watchlist by evaluating a constant, local function, local variable, global function, and global variable. """ db = Qdb(cmd_manager=NopCommandManager, execution_timeout=1) too_long_msg = db.exception_serializer( QdbExecutionTimeout('too_long()', 1) ) db.extend_watchlist( '2 + 2', 'local_var', 'local_fn()', 'global_var', 'global_fn()', 'too_long()', ) def new_curframe(): """ Test function for checking for NameErrors on the watchlist. This changes the curframe of the tracer to eval the watchlist with a new set of locals. """ self.assertEqual(db.watchlist['2 + 2'], (None, 4)) self.assertEqual( db.watchlist['local_var'], ('NameError', "NameError: name 'local_var' is not defined") ) self.assertEqual( db.watchlist['local_fn()'], ('NameError', "NameError: name 'local_fn' is not defined") ) self.assertEqual(db.watchlist['global_var'], (None, 'global_var')) self.assertEqual(db.watchlist['global_fn()'], (None, 'global_fn')) local_var = 'local_var' # NOQA local_fn = lambda: 'local_fn' # NOQA def too_long(): while True: pass # Set trace and check innitial assertions. db.set_trace() self.assertEqual(db.watchlist['2 + 2'], (None, 4)) self.assertEqual(db.watchlist['local_var'], (None, 'local_var')) self.assertEqual(db.watchlist['local_fn()'], (None, 'local_fn')) self.assertEqual(db.watchlist['global_var'], (None, 'global_var')) self.assertEqual(db.watchlist['global_fn()'], (None, 'global_fn')) # Testing this as a tuple causes strange behavior. self.assertEqual(db.watchlist['too_long()'][0], 'QdbExecutionTimeout') self.assertEqual(db.watchlist['too_long()'][1], too_long_msg) local_var = 'updated_local_var' # NOQA local_fn = lambda: 'updated_local_fn' # NOQA self.assertEqual(db.watchlist['2 + 2'], (None, 4)) self.assertEqual(db.watchlist['local_var'], (None, 'updated_local_var')) self.assertEqual(db.watchlist['local_fn()'], (None, 'updated_local_fn')) self.assertEqual(db.watchlist['global_var'], (None, 'global_var')) self.assertEqual(db.watchlist['global_fn()'], (None, 'global_fn')) new_curframe()