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