|
18 | 18 | ) |
19 | 19 | from profiling.sampling.jsonl_collector import JsonlCollector |
20 | 20 | from profiling.sampling.gecko_collector import GeckoCollector |
| 21 | + from profiling.sampling.heatmap_collector import _TemplateLoader |
21 | 22 | from profiling.sampling.collector import extract_lineno, normalize_location |
22 | 23 | from profiling.sampling.opcode_utils import get_opcode_info, format_opcode |
23 | 24 | from profiling.sampling.constants import ( |
@@ -82,6 +83,18 @@ def test_mock_frame_info_with_empty_and_unicode_values(self): |
82 | 83 | self.assertEqual(frame.location.lineno, 999999) |
83 | 84 | self.assertEqual(frame.funcname, long_funcname) |
84 | 85 |
|
| 86 | + def test_heatmap_navigation_restarts_line_highlight(self): |
| 87 | + """Test heatmap navigation can replay target line highlights.""" |
| 88 | + loader = _TemplateLoader() |
| 89 | + |
| 90 | + self.assertIn(".code-line:target", loader.file_css) |
| 91 | + self.assertIn("function restartLineHighlight(target)", loader.file_js) |
| 92 | + self.assertIn("target.style.animation = 'none'", loader.file_js) |
| 93 | + self.assertIn("void target.offsetWidth", loader.file_js) |
| 94 | + self.assertIn("url.href === window.location.href", loader.file_js) |
| 95 | + self.assertIn("navigateToLine(JSON.parse(navData).link)", loader.file_js) |
| 96 | + self.assertIn("navigateToLine(linkData.link)", loader.file_js) |
| 97 | + |
85 | 98 | def test_pstats_collector_with_extreme_intervals_and_empty_data(self): |
86 | 99 | """Test PstatsCollector handles zero/large intervals, empty frames, None thread IDs, and duplicate frames.""" |
87 | 100 | # Test with zero interval |
@@ -1403,6 +1416,39 @@ def test_diff_flamegraph_elided_stacks(self): |
1403 | 1416 | self.assertGreater(child["baseline"], 0) |
1404 | 1417 | self.assertAlmostEqual(child["diff"], -child["baseline"]) |
1405 | 1418 |
|
| 1419 | + def test_diff_flamegraph_elided_top_level_root(self): |
| 1420 | + """Elided top-level roots do not crash metadata generation.""" |
| 1421 | + baseline_frames_1 = [ |
| 1422 | + MockInterpreterInfo(0, [ |
| 1423 | + MockThreadInfo(1, [ |
| 1424 | + MockFrameInfo("file.py", 10, "kept_leaf"), |
| 1425 | + MockFrameInfo("file.py", 20, "kept_root"), |
| 1426 | + ]) |
| 1427 | + ]) |
| 1428 | + ] |
| 1429 | + baseline_frames_2 = [ |
| 1430 | + MockInterpreterInfo(0, [ |
| 1431 | + MockThreadInfo(1, [ |
| 1432 | + MockFrameInfo("file.py", 30, "old_leaf"), |
| 1433 | + MockFrameInfo("file.py", 40, "old_root"), |
| 1434 | + ]) |
| 1435 | + ]) |
| 1436 | + ] |
| 1437 | + |
| 1438 | + diff = make_diff_collector_with_mock_baseline([ |
| 1439 | + baseline_frames_1, |
| 1440 | + baseline_frames_2, |
| 1441 | + ]) |
| 1442 | + diff.collect(baseline_frames_1) |
| 1443 | + |
| 1444 | + data = diff._convert_to_flamegraph_format() |
| 1445 | + elided = data["stats"]["elided_flamegraph"] |
| 1446 | + elided_strings = elided.get("strings", []) |
| 1447 | + children = elided.get("children", []) |
| 1448 | + |
| 1449 | + self.assertEqual(len(children), 1) |
| 1450 | + self.assertIn("old_root", resolve_name(children[0], elided_strings)) |
| 1451 | + |
1406 | 1452 | def test_diff_flamegraph_function_matched_despite_line_change(self): |
1407 | 1453 | """Functions match by (filename, funcname), ignoring lineno.""" |
1408 | 1454 | baseline_frames = [ |
|
0 commit comments