|
4 | 4 | import subprocess |
5 | 5 | import sys |
6 | 6 | import unittest |
| 7 | +from types import SimpleNamespace |
7 | 8 | from unittest import mock |
8 | 9 |
|
9 | 10 | try: |
|
15 | 16 |
|
16 | 17 | from test.support import is_emscripten, requires_remote_subprocess_debugging |
17 | 18 |
|
18 | | -from profiling.sampling.cli import main |
| 19 | +from profiling.sampling.cli import _handle_output, _handle_replay, main |
19 | 20 | from profiling.sampling.errors import SamplingScriptNotFoundError, SamplingModuleNotFoundError, SamplingUnknownProcessError |
20 | 21 |
|
21 | 22 | class TestSampleProfilerCLI(unittest.TestCase): |
@@ -700,6 +701,106 @@ def test_async_aware_incompatible_with_all_threads(self): |
700 | 701 | self.assertIn("--all-threads", error_msg) |
701 | 702 | self.assertIn("incompatible with --async-aware", error_msg) |
702 | 703 |
|
| 704 | + def test_handle_output_browser_not_opened_when_export_fails(self): |
| 705 | + collector = mock.MagicMock() |
| 706 | + collector.export.return_value = False |
| 707 | + args = SimpleNamespace( |
| 708 | + format="flamegraph", |
| 709 | + outfile="profile.html", |
| 710 | + browser=True, |
| 711 | + ) |
| 712 | + |
| 713 | + with ( |
| 714 | + mock.patch("profiling.sampling.cli.os.path.isdir", return_value=False), |
| 715 | + mock.patch("profiling.sampling.cli._open_in_browser") as mock_open, |
| 716 | + ): |
| 717 | + _handle_output(collector, args, pid=12345, mode=0) |
| 718 | + |
| 719 | + collector.export.assert_called_once_with("profile.html") |
| 720 | + mock_open.assert_not_called() |
| 721 | + |
| 722 | + def test_handle_output_browser_opened_when_export_succeeds(self): |
| 723 | + collector = mock.MagicMock() |
| 724 | + collector.export.return_value = True |
| 725 | + args = SimpleNamespace( |
| 726 | + format="flamegraph", |
| 727 | + outfile="profile.html", |
| 728 | + browser=True, |
| 729 | + ) |
| 730 | + |
| 731 | + with ( |
| 732 | + mock.patch("profiling.sampling.cli.os.path.isdir", return_value=False), |
| 733 | + mock.patch("profiling.sampling.cli._open_in_browser") as mock_open, |
| 734 | + ): |
| 735 | + _handle_output(collector, args, pid=12345, mode=0) |
| 736 | + |
| 737 | + collector.export.assert_called_once_with("profile.html") |
| 738 | + mock_open.assert_called_once_with("profile.html") |
| 739 | + |
| 740 | + def test_replay_browser_not_opened_when_export_fails(self): |
| 741 | + args = SimpleNamespace( |
| 742 | + input_file="profile.bin", |
| 743 | + format="flamegraph", |
| 744 | + outfile="profile.html", |
| 745 | + browser=True, |
| 746 | + sort=None, |
| 747 | + limit=None, |
| 748 | + no_summary=False, |
| 749 | + ) |
| 750 | + collector = mock.MagicMock() |
| 751 | + collector.export.return_value = False |
| 752 | + |
| 753 | + with ( |
| 754 | + mock.patch("profiling.sampling.cli.os.path.exists", return_value=True), |
| 755 | + mock.patch("profiling.sampling.cli.BinaryReader") as mock_reader_cls, |
| 756 | + mock.patch("profiling.sampling.cli._create_collector", return_value=collector), |
| 757 | + mock.patch("profiling.sampling.cli._open_in_browser") as mock_open, |
| 758 | + ): |
| 759 | + reader = mock_reader_cls.return_value.__enter__.return_value |
| 760 | + reader.get_info.return_value = { |
| 761 | + "sample_count": 1, |
| 762 | + "sample_interval_us": 1000, |
| 763 | + "compression_type": 0, |
| 764 | + } |
| 765 | + reader.replay_samples.return_value = 1 |
| 766 | + |
| 767 | + _handle_replay(args) |
| 768 | + |
| 769 | + collector.export.assert_called_once_with("profile.html") |
| 770 | + mock_open.assert_not_called() |
| 771 | + |
| 772 | + def test_replay_browser_opened_when_export_succeeds(self): |
| 773 | + args = SimpleNamespace( |
| 774 | + input_file="profile.bin", |
| 775 | + format="flamegraph", |
| 776 | + outfile="profile.html", |
| 777 | + browser=True, |
| 778 | + sort=None, |
| 779 | + limit=None, |
| 780 | + no_summary=False, |
| 781 | + ) |
| 782 | + collector = mock.MagicMock() |
| 783 | + collector.export.return_value = True |
| 784 | + |
| 785 | + with ( |
| 786 | + mock.patch("profiling.sampling.cli.os.path.exists", return_value=True), |
| 787 | + mock.patch("profiling.sampling.cli.BinaryReader") as mock_reader_cls, |
| 788 | + mock.patch("profiling.sampling.cli._create_collector", return_value=collector), |
| 789 | + mock.patch("profiling.sampling.cli._open_in_browser") as mock_open, |
| 790 | + ): |
| 791 | + reader = mock_reader_cls.return_value.__enter__.return_value |
| 792 | + reader.get_info.return_value = { |
| 793 | + "sample_count": 1, |
| 794 | + "sample_interval_us": 1000, |
| 795 | + "compression_type": 0, |
| 796 | + } |
| 797 | + reader.replay_samples.return_value = 1 |
| 798 | + |
| 799 | + _handle_replay(args) |
| 800 | + |
| 801 | + collector.export.assert_called_once_with("profile.html") |
| 802 | + mock_open.assert_called_once_with("profile.html") |
| 803 | + |
703 | 804 | @unittest.skipIf(is_emscripten, "subprocess not available") |
704 | 805 | def test_run_nonexistent_script_exits_cleanly(self): |
705 | 806 | """Test that running a non-existent script exits with a clean error.""" |
|
0 commit comments