Skip to content

Commit 3bc8bb8

Browse files
authored
test(profiling): Add basic profiling tests (getsentry#1677)
This introduces some basic tests to the setup of the profiler.
1 parent 6e0b02b commit 3bc8bb8

3 files changed

Lines changed: 110 additions & 38 deletions

File tree

tests/conftest.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
eventlet = None
1616

1717
import sentry_sdk
18-
from sentry_sdk._compat import reraise, string_types, iteritems
19-
from sentry_sdk.transport import Transport
18+
from sentry_sdk._compat import iteritems, reraise, string_types
2019
from sentry_sdk.envelope import Envelope
21-
from sentry_sdk.utils import capture_internal_exceptions
2220
from sentry_sdk.integrations import _installed_integrations # noqa: F401
21+
from sentry_sdk.profiler import teardown_profiler
22+
from sentry_sdk.transport import Transport
23+
from sentry_sdk.utils import capture_internal_exceptions
2324

2425
from tests import _warning_recorder, _warning_recorder_mgr
2526

@@ -554,3 +555,9 @@ def __ne__(self, test_obj):
554555
return not self.__eq__(test_obj)
555556

556557
return ObjectDescribedBy
558+
559+
560+
@pytest.fixture
561+
def teardown_profiling():
562+
yield
563+
teardown_profiler()

tests/integrations/wsgi/test_wsgi.py

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
import sys
2+
13
from werkzeug.test import Client
24

35
import pytest
46

57
import sentry_sdk
68
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
7-
from sentry_sdk.profiler import teardown_profiler
89
from collections import Counter
9-
from sentry_sdk.utils import PY33
1010

1111
try:
1212
from unittest import mock # python 3.3 and above
@@ -284,38 +284,42 @@ def sample_app(environ, start_response):
284284
assert len(session_aggregates) == 1
285285

286286

287-
if PY33:
288-
289-
@pytest.fixture
290-
def profiling():
291-
yield
292-
teardown_profiler()
287+
@pytest.mark.skipif(
288+
sys.version_info < (3, 3), reason="Profiling is only supported in Python >= 3.3"
289+
)
290+
@pytest.mark.parametrize(
291+
"profiles_sample_rate,profile_count",
292+
[
293+
pytest.param(1.0, 1, id="profiler sampled at 1.0"),
294+
pytest.param(0.75, 1, id="profiler sampled at 0.75"),
295+
pytest.param(0.25, 0, id="profiler not sampled at 0.25"),
296+
pytest.param(None, 0, id="profiler not enabled"),
297+
],
298+
)
299+
def test_profile_sent(
300+
capture_envelopes,
301+
sentry_init,
302+
teardown_profiling,
303+
profiles_sample_rate,
304+
profile_count,
305+
):
306+
def test_app(environ, start_response):
307+
start_response("200 OK", [])
308+
return ["Go get the ball! Good dog!"]
293309

294-
@pytest.mark.parametrize(
295-
"profiles_sample_rate,should_send",
296-
[(1.0, True), (0.75, True), (0.25, False), (None, False)],
310+
sentry_init(
311+
traces_sample_rate=1.0,
312+
_experiments={"profiles_sample_rate": profiles_sample_rate},
297313
)
298-
def test_profile_sent_when_profiling_enabled(
299-
capture_envelopes, sentry_init, profiling, profiles_sample_rate, should_send
300-
):
301-
def test_app(environ, start_response):
302-
start_response("200 OK", [])
303-
return ["Go get the ball! Good dog!"]
304-
305-
sentry_init(
306-
traces_sample_rate=1.0,
307-
_experiments={"profiles_sample_rate": profiles_sample_rate},
308-
)
309-
app = SentryWsgiMiddleware(test_app)
310-
envelopes = capture_envelopes()
311-
312-
with mock.patch("sentry_sdk.profiler.random.random", return_value=0.5):
313-
client = Client(app)
314-
client.get("/")
315-
316-
profile_sent = False
317-
for item in envelopes[0].items:
318-
if item.headers["type"] == "profile":
319-
profile_sent = True
320-
break
321-
assert profile_sent == should_send
314+
app = SentryWsgiMiddleware(test_app)
315+
envelopes = capture_envelopes()
316+
317+
with mock.patch("sentry_sdk.profiler.random.random", return_value=0.5):
318+
client = Client(app)
319+
client.get("/")
320+
321+
count_item_types = Counter()
322+
for envelope in envelopes:
323+
for item in envelope.items:
324+
count_item_types[item.type] += 1
325+
assert count_item_types["profile"] == profile_count

tests/test_profiler.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import platform
2+
import sys
3+
import threading
4+
5+
import pytest
6+
7+
from sentry_sdk.profiler import setup_profiler
8+
9+
10+
minimum_python_33 = pytest.mark.skipif(
11+
sys.version_info < (3, 3), reason="Profiling is only supported in Python >= 3.3"
12+
)
13+
14+
unix_only = pytest.mark.skipif(
15+
platform.system().lower() not in {"linux", "darwin"}, reason="UNIX only"
16+
)
17+
18+
19+
@minimum_python_33
20+
def test_profiler_invalid_mode(teardown_profiling):
21+
with pytest.raises(ValueError):
22+
setup_profiler({"_experiments": {"profiler_mode": "magic"}})
23+
# make sure to clean up at the end of the test
24+
25+
26+
@unix_only
27+
@minimum_python_33
28+
@pytest.mark.parametrize("mode", ["sigprof", "sigalrm"])
29+
def test_profiler_signal_mode_none_main_thread(mode, teardown_profiling):
30+
"""
31+
signal based profiling must be initialized from the main thread because
32+
of how the signal library in python works
33+
"""
34+
35+
class ProfilerThread(threading.Thread):
36+
def run(self):
37+
self.exc = None
38+
try:
39+
setup_profiler({"_experiments": {"profiler_mode": mode}})
40+
except Exception as e:
41+
# store the exception so it can be raised in the caller
42+
self.exc = e
43+
44+
def join(self, timeout=None):
45+
ret = super(ProfilerThread, self).join(timeout=timeout)
46+
if self.exc:
47+
raise self.exc
48+
return ret
49+
50+
with pytest.raises(ValueError):
51+
thread = ProfilerThread()
52+
thread.start()
53+
thread.join()
54+
55+
# make sure to clean up at the end of the test
56+
57+
58+
@pytest.mark.parametrize("mode", ["sleep", "event", "sigprof", "sigalrm"])
59+
def test_profiler_valid_mode(mode, teardown_profiling):
60+
# should not raise any exceptions
61+
setup_profiler({"_experiments": {"profiler_mode": mode}})

0 commit comments

Comments
 (0)