|
16 | 16 | CollapsedStackCollector, |
17 | 17 | FlamegraphCollector, |
18 | 18 | ) |
| 19 | + from profiling.sampling.jsonl_collector import JsonlCollector |
19 | 20 | from profiling.sampling.gecko_collector import GeckoCollector |
20 | 21 | from profiling.sampling.collector import extract_lineno, normalize_location |
21 | 22 | from profiling.sampling.opcode_utils import get_opcode_info, format_opcode |
@@ -1665,6 +1666,86 @@ def test_diff_flamegraph_load_baseline(self): |
1665 | 1666 | self.assertAlmostEqual(cold_node["diff"], -1.0) |
1666 | 1667 | self.assertAlmostEqual(cold_node["diff_pct"], -50.0) |
1667 | 1668 |
|
| 1669 | + def test_jsonl_collector_basic(self): |
| 1670 | + collapsed_out = tempfile.NamedTemporaryFile(delete=False) |
| 1671 | + self.addCleanup(close_and_unlink, collapsed_out) |
| 1672 | + |
| 1673 | + collector = JsonlCollector(1000) |
| 1674 | + run_id = collector.run_id |
| 1675 | + |
| 1676 | + self.assertIsNotNone(run_id) |
| 1677 | + |
| 1678 | + test_frames1 = [ |
| 1679 | + MockInterpreterInfo( |
| 1680 | + 0, |
| 1681 | + [ |
| 1682 | + MockThreadInfo( |
| 1683 | + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] |
| 1684 | + ) |
| 1685 | + ], |
| 1686 | + ) |
| 1687 | + ] |
| 1688 | + test_frames2 = [ |
| 1689 | + MockInterpreterInfo( |
| 1690 | + 0, |
| 1691 | + [ |
| 1692 | + MockThreadInfo( |
| 1693 | + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] |
| 1694 | + ) |
| 1695 | + ], |
| 1696 | + ) |
| 1697 | + ] # Same stack |
| 1698 | + test_frames3 = [ |
| 1699 | + MockInterpreterInfo( |
| 1700 | + 0, [MockThreadInfo(1, [MockFrameInfo("other.py", 5, "other_func")])] |
| 1701 | + ) |
| 1702 | + ] |
| 1703 | + |
| 1704 | + collector.collect(test_frames1) |
| 1705 | + collector.collect(test_frames2) |
| 1706 | + collector.collect(test_frames3) |
| 1707 | + |
| 1708 | + with captured_stdout(), captured_stderr(): |
| 1709 | + collector.export(collapsed_out.name) |
| 1710 | + |
| 1711 | + # Check file contents |
| 1712 | + with open(collapsed_out.name, "r") as f: |
| 1713 | + content = f.read() |
| 1714 | + |
| 1715 | + lines = content.strip().split("\n") |
| 1716 | + self.assertEqual(len(lines), 5) |
| 1717 | + |
| 1718 | + def jsonl(obj): |
| 1719 | + return json.dumps(obj, separators=(",", ":")) |
| 1720 | + |
| 1721 | + expected = [ |
| 1722 | + jsonl({"type": "meta", "v": 1, "run_id": run_id, |
| 1723 | + "sample_interval_usec": 1000}), |
| 1724 | + jsonl({"type": "str_def", "v": 1, "run_id": run_id, |
| 1725 | + "defs": [{"str_id": 1, "value": "func1"}, |
| 1726 | + {"str_id": 2, "value": "file.py"}, |
| 1727 | + {"str_id": 3, "value": "func2"}, |
| 1728 | + {"str_id": 4, "value": "other_func"}, |
| 1729 | + {"str_id": 5, "value": "other.py"}]}), |
| 1730 | + jsonl({"type": "frame_def", "v": 1, "run_id": run_id, |
| 1731 | + "defs": [{"frame_id": 1, "path_str_id": 2, "func_str_id": 1, |
| 1732 | + "line": 10, "end_line": 10}, |
| 1733 | + {"frame_id": 2, "path_str_id": 2, "func_str_id": 3, |
| 1734 | + "line": 20, "end_line": 20}, |
| 1735 | + {"frame_id": 3, "path_str_id": 5, "func_str_id": 4, |
| 1736 | + "line": 5, "end_line": 5}]}), |
| 1737 | + jsonl({"type": "agg", "v": 1, "run_id": run_id, |
| 1738 | + "kind": "frame", "scope": "final", "samples_total": 3, |
| 1739 | + "entries": [{"frame_id": 1, "self": 2, "cumulative": 2}, |
| 1740 | + {"frame_id": 2, "self": 0, "cumulative": 2}, |
| 1741 | + {"frame_id": 3, "self": 1, "cumulative": 1}]}), |
| 1742 | + jsonl({"type": "end", "v": 1, "run_id": run_id, |
| 1743 | + "samples_total": 3}), |
| 1744 | + ] |
| 1745 | + |
| 1746 | + for exp in expected: |
| 1747 | + self.assertIn(exp, lines) |
| 1748 | + |
1668 | 1749 |
|
1669 | 1750 | class TestRecursiveFunctionHandling(unittest.TestCase): |
1670 | 1751 | """Tests for correct handling of recursive functions in cumulative stats.""" |
|
0 commit comments