1import os 2import threading 3 4import pytest 5 6from ddtrace.internal import periodic 7from ddtrace.internal import service 8 9 10if os.getenv("DD_PROFILE_TEST_GEVENT", False): 11 import gevent 12 13 class Event(object): 14 """ 15 We can't use gevent Events here[0], nor can we use native threading 16 events (because gevent is not multi-threaded). 17 18 So for gevent, since it's not multi-threaded and will not run greenlets 19 in parallel (for our usage here, anyway) we can write a dummy Event 20 class which just does a simple busy wait on a shared variable. 21 22 [0] https://github.com/gevent/gevent/issues/891 23 """ 24 25 state = False 26 27 def wait(self): 28 while not self.state: 29 gevent.sleep(0.001) 30 31 def set(self): 32 self.state = True 33 34 35else: 36 Event = threading.Event 37 38 39def test_periodic(): 40 x = {"OK": False} 41 42 thread_started = Event() 43 thread_continue = Event() 44 45 def _run_periodic(): 46 thread_started.set() 47 x["OK"] = True 48 thread_continue.wait() 49 50 def _on_shutdown(): 51 x["DOWN"] = True 52 53 t = periodic.PeriodicRealThreadClass()(0.001, _run_periodic, on_shutdown=_on_shutdown) 54 t.start() 55 thread_started.wait() 56 thread_continue.set() 57 assert t.is_alive() 58 t.stop() 59 t.join() 60 assert not t.is_alive() 61 assert x["OK"] 62 assert x["DOWN"] 63 if hasattr(threading, "get_native_id"): 64 assert t.native_id is not None 65 66 67def test_periodic_double_start(): 68 def _run_periodic(): 69 pass 70 71 t = periodic.PeriodicRealThreadClass()(0.1, _run_periodic) 72 t.start() 73 with pytest.raises(RuntimeError): 74 t.start() 75 76 77def test_periodic_error(): 78 x = {"OK": False} 79 80 thread_started = Event() 81 thread_continue = Event() 82 83 def _run_periodic(): 84 thread_started.set() 85 thread_continue.wait() 86 raise ValueError 87 88 def _on_shutdown(): 89 x["DOWN"] = True 90 91 t = periodic.PeriodicRealThreadClass()(0.001, _run_periodic, on_shutdown=_on_shutdown) 92 t.start() 93 thread_started.wait() 94 thread_continue.set() 95 t.stop() 96 t.join() 97 assert "DOWN" not in x 98 99 100def test_gevent_class(): 101 if os.getenv("DD_PROFILE_TEST_GEVENT", False): 102 assert isinstance(periodic.PeriodicRealThreadClass()(1, sum), periodic._GeventPeriodicThread) 103 else: 104 assert isinstance(periodic.PeriodicRealThreadClass()(1, sum), periodic.PeriodicThread) 105 106 107def test_periodic_service_start_stop(): 108 t = periodic.PeriodicService(1) 109 t.start() 110 with pytest.raises(service.ServiceStatusError): 111 t.start() 112 t.stop() 113 t.join() 114 with pytest.raises(service.ServiceStatusError): 115 t.stop() 116 with pytest.raises(service.ServiceStatusError): 117 t.stop() 118 t.join() 119 t.join() 120 121 122def test_periodic_join_stop_no_start(): 123 t = periodic.PeriodicService(1) 124 t.join() 125 with pytest.raises(service.ServiceStatusError): 126 t.stop() 127 t.join() 128 t = periodic.PeriodicService(1) 129 with pytest.raises(service.ServiceStatusError): 130 t.stop() 131 t.join() 132 with pytest.raises(service.ServiceStatusError): 133 t.stop() 134 135 136def test_is_alive_before_start(): 137 def x(): 138 pass 139 140 t = periodic.PeriodicRealThreadClass()(1, x) 141 assert not t.is_alive() 142