def test_hotspot_overhead(self): # Set SCALE to 5000 or something big to see how hotspot overhead # diminishes the more work the target function does. # It's low in this test suite because people like fast tests. SCALE = 100 val = [dict((str(i), i) for i in xrange(100))] * SCALE start = time.time() to_columns(val) unpatched = time.time() - start probe = probes.attach_to("diagnose.test_fixtures.to_columns") try: probe.start() probe.instruments["instrument1"] = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="to_columns.slowest.time", value="hotspots.worst.time", custom={ "tags": '{"source": "%s:%s" % (hotspots.worst.lineno, hotspots.worst.source)}' }, ) start = time.time() to_columns(val) patched = time.time() - start finally: probe.stop() print("\nUNPATCHED: %s PATCHED: %s (%s%%)" % (unpatched, patched, int((patched / unpatched) * 100)))
def test_probe_bad_mock(self): p = probes.attach_to("diagnose.test_fixtures.Thing.notamethod") with self.assertRaises(AttributeError) as exc: p.start() assert ( exc.exception.args[0] == "diagnose.test_fixtures.Thing does not have the attribute 'notamethod'" )
def test_patch_class_decorated(self): probe = probes.attach_to("diagnose.test_fixtures.sum4") try: probe.start() instr = ProbeTestInstrument("deco", "arg4", event="call") probe.instruments["deco"] = instr assert sum4(1, 2, 3, 4) == 10 assert instr.results == [4] finally: probe.stop()
def test_patch_wrapped_function_end_event(self): probe = probes.attach_to("diagnose.test_fixtures.Thing.add5") try: probe.start() instr = ProbeTestInstrument("deco", "arg1", event="end") probe.instruments["deco"] = instr Thing().add5(13) assert instr.results == [113] finally: probe.stop()
def test_probe_bad_mock(self): p = probes.attach_to("diagnose.test_fixtures.Thing.notamethod") with self.assertRaises(AttributeError) as exc: p.start() if six.PY2: expected_message = "diagnose.test_fixtures.Thing does not have the attribute 'notamethod'" else: expected_message = "<class 'diagnose.test_fixtures.Thing'> does not have the attribute 'notamethod'" assert (exc.exception.args[0] == expected_message)
def test_end_event_success(self): probe = probes.attach_to("diagnose.test_fixtures.a_func") try: probe.start() probe.instruments["instrument1"] = i = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="a_func", value="output", event="end", custom=None, ) assert a_func(27) == 40 assert i.results == [40] finally: probe.stop()
def __call__(self, f): """Use self as a decorator, attaching a probe to the wrapped function.""" classname = sys._getframe(1).f_code.co_name if classname == "<module>": target = "%s.%s" % (f.__module__, getattr(f, "__name__", None) or getattr(f, "func_name")) else: target = "%s.%s.%s" % (f.__module__, classname, getattr(f, "__name__", None) or getattr(f, "func_name")) probe = probes.attach_to(target) # If we prefix the spec_id with self.mgr.short_id, then that # manager would immediately remove this instrument because # it's not in self.mgr.specs! # Use a hardcoded prefix instead so no manager drops it. probe.instruments["hardcode:%s" % (hash(target),)] = self return f
def test_return_event_locals_frame(self): probe = probes.attach_to("diagnose.test_fixtures.a_func") try: probe.start() probe.instruments["instrument1"] = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="a_func", value="frame.f_back.f_code.co_name", custom=None, ) a_func(923775) assert probe.instruments["instrument1"].results == [ "test_return_event_locals_frame" ] finally: probe.stop()
def test_end_event_exception_in_target(self): probe = probes.attach_to("diagnose.test_fixtures.a_func") try: probe.start() probe.instruments["instrument1"] = i = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="a_func", value="extra", event="end", custom=None, ) with self.assertRaises(TypeError): a_func(None) self.assertEqual(i.results, [13]) finally: probe.stop()
def test_slowest_line(self): probe = probes.attach_to("diagnose.test_fixtures.hard_work") try: probe.start() probe.instruments["instrument1"] = i = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="hard_work.slowest.time", value="hotspots.worst.time", custom={ "tags": '{"source": "%s:%s" % (hotspots.worst.lineno, hotspots.worst.source)}' }, ) assert hard_work(0, 10000) == 1000 assert [tags for tags, value in i.log] == [[ "source:34: summary = len([x for x in output if x % 10 == 0])\n" ]] assert [type(value) for tags, value in i.log] == [float] finally: probe.stop()
def test_return_event_exception_in_target(self): probe = probes.attach_to("diagnose.test_fixtures.a_func") try: probe.start() probe.instruments["instrument1"] = i = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="a_func", value="result", event="return", custom=None, ) with self.assertRaises(TypeError): a_func(None) self.assertEqual(len(i.results), 1) self.assertEqual(type(i.results[0]), TypeError) self.assertEqual( i.results[0].args, ("unsupported operand type(s) for +: 'NoneType' and 'int'", ), ) finally: probe.stop()
def test_end_event_exception_in_value(self): probe = probes.attach_to("diagnose.test_fixtures.a_func") try: errs = [] old_handle_error = diagnose.manager.handle_error diagnose.manager.handle_error = lambda probe, instr: errs.append( sys.exc_info()[1].args[0] if sys.exc_info()[1].args[0] else "") probe.start() probe.instruments["instrument1"] = i = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="a_func", value="unknown", # Should throw NameError event="end", custom=None, ) assert a_func(1000) == 1013 assert i.results == [] assert i.expires == i.error_expiration assert errs == ["name 'unknown' is not defined"] finally: diagnose.manager.handle_error = old_handle_error probe.stop()
def test_probe_nonfunc(self): # We REALLY should not be allowed to patch anything # that's not a function! p = probes.attach_to("diagnose.test_fixtures.Thing") with self.assertRaises(TypeError): p.start()
def test_target_copies(self): # When module M chooses "from x import y", then mock.patching x.y # does not affect M.y. Similarly, an existing object instance I # which has I.y = y is not patched by mock.patch. # Assert that FunctionProbe patches (and UNpatches) all such copies of y. old_probes_func_2 = func_2 old_local_func_2 = func_2 class Entity(object): pass t = Entity() t.add13 = func_2 self.assertTrue(t.add13 is old_local_func_2) t2 = Entity() t2.add13 = func_2 self.assertTrue(t2.add13 is old_local_func_2) registry["in_a_dict"] = func_2 self.assertTrue(registry["in_a_dict"] is old_local_func_2) probe = probes.attach_to("diagnose.test_fixtures.func_2") try: probe.start() probe.instruments["instrument1"] = i = ProbeTestInstrument( expires=datetime.datetime.utcnow() + datetime.timedelta(minutes=10), name="func_2", value="arg", custom=None, ) # Invoking x.y is typical and works naturally... self.assertTrue(func_2 is not old_probes_func_2) func_2(44) self.assertEqual(i.results, [44]) # ...but invoking M.y (we imported func_2 into test_probes' namespace) # is harder: self.assertTrue(func_2 is not old_local_func_2) func_2(99999) self.assertEqual(i.results, [44, 99999]) # ...and invoking Entity().y is just as hard: self.assertTrue(t.add13 is not old_local_func_2) self.assertTrue(t2.add13 is not old_local_func_2) t.add13(1001) self.assertEqual(i.results, [44, 99999, 1001]) # ...etc: self.assertTrue(registry["in_a_dict"] is not old_local_func_2) registry["in_a_dict"](777) self.assertEqual(i.results, [44, 99999, 1001, 777]) # The next problem is that, while our patch is live, # if t2 goes out of its original scope, we've still got # a reference to it in our mock patch. self.assertEqual( owner_types(func_2), { types.ModuleType: 2, Entity: 2, probes.WeakMethodPatch: 3, MockPatch: 1, probes.DictPatch: 1, }, ) del t2 self.assertEqual( owner_types(func_2), { types.ModuleType: 2, # The number of Entity references MUST decrease by 1. Entity: 1, # The number of WeakMethodPatch references MUST decrease by 1. probes.WeakMethodPatch: 2, MockPatch: 1, probes.DictPatch: 1, }, ) finally: probe.stop() # All patches MUST be stopped assert func_2 is old_probes_func_2 assert func_2 is old_local_func_2 assert t.add13 is old_local_func_2 func_2(123) func_2(456) t.add13(789) registry["in_a_dict"](101112) assert i.results == [44, 99999, 1001, 777]