File: test_pstats_renderer.py

package info (click to toggle)
python-pyinstrument 5.1.1%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,624 kB
  • sloc: python: 6,713; ansic: 897; makefile: 46; sh: 26; javascript: 18
file content (130 lines) | stat: -rw-r--r-- 3,441 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import os
import time
from pathlib import Path
from pstats import Stats
from test.fake_time_util import FakeClock, fake_time
from typing import Any

import pytest

from pyinstrument import Profiler
from pyinstrument.renderers import PstatsRenderer


def a():
    b()
    c()


def b():
    d()


def c():
    d()


def d():
    e()


def e():
    time.sleep(1)


@pytest.fixture(scope="module")
def profiler_session():
    with fake_time():
        profiler = Profiler()
        profiler.start()

        a()

        profiler.stop()
        return profiler.last_session


def test_pstats_renderer(profiler_session, tmp_path):
    fname = tmp_path / "test.pstats"
    pstats_data = PstatsRenderer().render(profiler_session)
    with open(fname, "wb") as fid:
        fid.write(pstats_data.encode(encoding="utf-8", errors="surrogateescape"))
    stats: Any = Stats(str(fname))
    # Sanity check
    assert stats.total_tt > 0
    # The graph is
    # a() -> b() -> d() -> e() -> time.sleep()
    #    \-> c() /
    # so make sure d has callers of b, c, and that the times make sense

    # in stats,
    #   keys are tuples (file_path, line, func)
    #   values are tuples (calltime, numcalls, selftime, cumtime, callers)
    # in callers,
    #   keys are the same as in stats
    #   values are the same as stats but without callers

    # check the time of d
    d_key = [k for k in stats.stats.keys() if k[2] == "d"][0]
    d_val = stats.stats[d_key]
    d_cumtime = d_val[3]
    assert d_cumtime == pytest.approx(2)

    # check d's callers times are split
    b_key = [k for k in stats.stats.keys() if k[2] == "b"][0]
    c_key = [k for k in stats.stats.keys() if k[2] == "c"][0]
    d_callers = d_val[4]
    b_cumtime = d_callers[b_key][3]
    c_cumtime = d_callers[c_key][3]
    assert b_cumtime == pytest.approx(1)
    assert c_cumtime == pytest.approx(1)

    # check the time of e
    e_key = [k for k in stats.stats.keys() if k[2] == "e"][0]
    e_val = stats.stats[e_key]
    e_cumtime = e_val[3]
    assert e_cumtime == pytest.approx(2)


def test_round_trip_encoding_of_binary_data(tmp_path: Path):
    # as used by the pstats renderer
    data_blob = os.urandom(1024)
    file = tmp_path / "file.dat"

    data_blob_string = data_blob.decode(encoding="utf-8", errors="surrogateescape")

    # newline='' is required to prevent the default newline translation
    with open(file, mode="w", encoding="utf-8", errors="surrogateescape", newline="") as f:
        f.write(data_blob_string)

    assert data_blob == data_blob_string.encode(encoding="utf-8", errors="surrogateescape")
    assert data_blob == file.read_bytes()


def sleep_and_busy_wait(clock: FakeClock):
    time.sleep(1.0)
    # this looks like a busy wait to the profiler
    clock.time += 1.0


def test_sum_of_tottime(tmp_path):
    # Check that the sum of the tottime of all the functions is equal to the
    # total time of the profile

    with fake_time() as clock:
        profiler = Profiler()
        profiler.start()

        sleep_and_busy_wait(clock)

        profiler.stop()
        profiler_session = profiler.last_session

    assert profiler_session

    pstats_data = PstatsRenderer().render(profiler_session)
    fname = tmp_path / "test.pstats"
    with open(fname, "wb") as fid:
        fid.write(pstats_data.encode(encoding="utf-8", errors="surrogateescape"))
    stats: Any = Stats(str(fname))
    assert stats.total_tt == pytest.approx(2)