xref: /aosp_15_r20/external/toolchain-utils/cros_utils/bugs_test.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1#!/usr/bin/env python3
2# Copyright 2021 The ChromiumOS Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6# We're testing protected methods, so allow protected access.
7# pylint: disable=protected-access
8
9"""Tests bug filing bits."""
10
11import datetime
12import json
13import os
14from pathlib import Path
15import tempfile
16import unittest
17import unittest.mock
18
19from cros_utils import bugs
20
21
22_ARBITRARY_DATETIME = datetime.datetime(2020, 1, 1, 23, 0, 0, 0)
23
24
25class Tests(unittest.TestCase):
26    """Tests for the bugs module."""
27
28    def testWritingJSONFileSeemsToWork(self):
29        """Tests JSON file writing."""
30        old_x20_path = bugs.X20_PATH
31
32        def restore_x20_path():
33            bugs.X20_PATH = old_x20_path
34
35        self.addCleanup(restore_x20_path)
36
37        with tempfile.TemporaryDirectory() as tempdir:
38            bugs.X20_PATH = tempdir
39            file_path = bugs._WriteBugJSONFile(
40                "ObjectType",
41                {
42                    "foo": "bar",
43                    "baz": bugs.WellKnownComponents.CrOSToolchainPublic,
44                },
45                None,
46            )
47
48            self.assertTrue(
49                file_path.startswith(tempdir),
50                f"Expected {file_path} to start with {tempdir}",
51            )
52
53            with open(file_path, encoding="utf-8") as f:
54                self.assertEqual(
55                    json.load(f),
56                    {
57                        "type": "ObjectType",
58                        "value": {
59                            "foo": "bar",
60                            "baz": int(
61                                bugs.WellKnownComponents.CrOSToolchainPublic
62                            ),
63                        },
64                    },
65                )
66
67    @unittest.mock.patch.object(bugs, "_WriteBugJSONFile")
68    def testAppendingToBugsSeemsToWork(self, mock_write_json_file):
69        """Tests AppendToExistingBug."""
70        bugs.AppendToExistingBug(1234, "hello, world!")
71        mock_write_json_file.assert_called_once_with(
72            "AppendToExistingBugRequest",
73            {
74                "body": "hello, world!",
75                "bug_id": 1234,
76            },
77            None,
78        )
79
80    @unittest.mock.patch.object(bugs, "_WriteBugJSONFile")
81    def testBugCreationSeemsToWork(self, mock_write_json_file):
82        """Tests CreateNewBug."""
83        test_case_additions = (
84            {},
85            {
86                "component_id": bugs.WellKnownComponents.CrOSToolchainPublic,
87            },
88            {
89                "assignee": "[email protected]",
90                "cc": ["[email protected]"],
91            },
92            {
93                "parent_bug": 123,
94            },
95        )
96
97        for additions in test_case_additions:
98            test_case = {
99                "component_id": 123,
100                "title": "foo",
101                "body": "bar",
102                **additions,
103            }
104
105            bugs.CreateNewBug(**test_case)
106
107            expected_output = {
108                "component_id": test_case["component_id"],
109                "subject": test_case["title"],
110                "body": test_case["body"],
111            }
112
113            if assignee := test_case.get("assignee"):
114                expected_output["assignee"] = assignee
115
116            if cc := test_case.get("cc"):
117                expected_output["cc"] = cc
118
119            if parent_bug := test_case.get("parent_bug"):
120                expected_output["parent_bug"] = parent_bug
121
122            mock_write_json_file.assert_called_once_with(
123                "FileNewBugRequest",
124                expected_output,
125                None,
126            )
127            mock_write_json_file.reset_mock()
128
129    @unittest.mock.patch.object(bugs, "_WriteBugJSONFile")
130    def testCronjobLogSendingSeemsToWork(self, mock_write_json_file):
131        """Tests SendCronjobLog."""
132        bugs.SendCronjobLog("my_name", False, "hello, world!")
133        mock_write_json_file.assert_called_once_with(
134            "CronjobUpdate",
135            {
136                "name": "my_name",
137                "message": "hello, world!",
138                "failed": False,
139            },
140            None,
141        )
142
143    @unittest.mock.patch.object(bugs, "_WriteBugJSONFile")
144    def testCronjobLogSendingSeemsToWorkWithTurndown(
145        self, mock_write_json_file
146    ):
147        """Tests SendCronjobLog."""
148        bugs.SendCronjobLog(
149            "my_name", False, "hello, world!", turndown_time_hours=42
150        )
151        mock_write_json_file.assert_called_once_with(
152            "CronjobUpdate",
153            {
154                "name": "my_name",
155                "message": "hello, world!",
156                "failed": False,
157                "cronjob_turndown_time_hours": 42,
158            },
159            None,
160        )
161
162    @unittest.mock.patch.object(bugs, "_WriteBugJSONFile")
163    def testCronjobLogSendingSeemsToWorkWithParentBug(
164        self, mock_write_json_file
165    ):
166        """Tests SendCronjobLog."""
167        bugs.SendCronjobLog("my_name", False, "hello, world!", parent_bug=42)
168        mock_write_json_file.assert_called_once_with(
169            "CronjobUpdate",
170            {
171                "name": "my_name",
172                "message": "hello, world!",
173                "failed": False,
174                "parent_bug": 42,
175            },
176            None,
177        )
178
179    def testFileNameGenerationProducesFileNamesInSortedOrder(self):
180        """Tests that _FileNameGenerator gives us sorted file names."""
181        gen = bugs._FileNameGenerator()
182        first = gen.generate_json_file_name(_ARBITRARY_DATETIME)
183        second = gen.generate_json_file_name(_ARBITRARY_DATETIME)
184        self.assertLess(first, second)
185
186    def testFileNameGenerationProtectsAgainstRipplingAdds(self):
187        """Tests that _FileNameGenerator gives us sorted file names."""
188        gen = bugs._FileNameGenerator()
189        gen._entropy = 9
190        first = gen.generate_json_file_name(_ARBITRARY_DATETIME)
191        second = gen.generate_json_file_name(_ARBITRARY_DATETIME)
192        self.assertLess(first, second)
193
194        gen = bugs._FileNameGenerator()
195        all_9s = "9" * (gen._ENTROPY_STR_SIZE - 1)
196        gen._entropy = int(all_9s)
197        third = gen.generate_json_file_name(_ARBITRARY_DATETIME)
198        self.assertLess(second, third)
199
200        fourth = gen.generate_json_file_name(_ARBITRARY_DATETIME)
201        self.assertLess(third, fourth)
202
203    @unittest.mock.patch.object(os, "getpid")
204    def testForkingProducesADifferentReport(self, mock_getpid):
205        """Tests that _FileNameGenerator gives us sorted file names."""
206        gen = bugs._FileNameGenerator()
207
208        mock_getpid.return_value = 1
209        gen._entropy = 0
210        parent_file = gen.generate_json_file_name(_ARBITRARY_DATETIME)
211
212        mock_getpid.return_value = 2
213        gen._entropy = 0
214        child_file = gen.generate_json_file_name(_ARBITRARY_DATETIME)
215        self.assertNotEqual(parent_file, child_file)
216
217    @unittest.mock.patch.object(bugs, "_WriteBugJSONFile")
218    def testCustomDirectoriesArePassedThrough(self, mock_write_json_file):
219        directory = "/path/to/somewhere/interesting"
220        bugs.AppendToExistingBug(1, "foo", directory=directory)
221        mock_write_json_file.assert_called_once_with(
222            unittest.mock.ANY, unittest.mock.ANY, directory
223        )
224        mock_write_json_file.reset_mock()
225
226        bugs.CreateNewBug(1, "title", "body", directory=directory)
227        mock_write_json_file.assert_called_once_with(
228            unittest.mock.ANY, unittest.mock.ANY, directory
229        )
230        mock_write_json_file.reset_mock()
231
232        bugs.SendCronjobLog("cronjob", False, "message", directory=directory)
233        mock_write_json_file.assert_called_once_with(
234            unittest.mock.ANY, unittest.mock.ANY, directory
235        )
236
237    def testWriteBugJSONFileWritesToGivenDirectory(self):
238        with tempfile.TemporaryDirectory() as tmpdir:
239            bugs.AppendToExistingBug(1, "body", directory=tmpdir)
240            json_files = list(Path(tmpdir).glob("*.json"))
241            self.assertEqual(len(json_files), 1, json_files)
242
243
244if __name__ == "__main__":
245    unittest.main()
246