1#!/usr/bin/env python3 2# Copyright 2022 The Pigweed Authors 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may not 5# use this file except in compliance with the License. You may obtain a copy of 6# the License at 7# 8# https://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations under 14# the License. 15"""Tests for todo_check.""" 16 17from pathlib import Path 18import re 19import unittest 20from unittest.mock import MagicMock, mock_open, patch 21 22from pw_presubmit import todo_check 23 24# pylint: disable=attribute-defined-outside-init 25# todo-check: disable 26 27 28# pylint: disable-next=too-many-public-methods 29class TestTodoCheck(unittest.TestCase): 30 """Test TODO checker.""" 31 32 def _run(self, regex: re.Pattern, contents: str) -> None: 33 self.ctx = MagicMock() 34 self.ctx.fail = MagicMock() 35 path = MagicMock(spec=Path('foo/bar')) 36 37 def mocked_open_read(*args, **kwargs): 38 return mock_open(read_data=contents)(*args, **kwargs) 39 40 with patch.object(path, 'open', mocked_open_read): 41 # pylint: disable=protected-access 42 todo_check._process_file(self.ctx, regex, path) 43 44 # pylint: enable=protected-access 45 46 def _run_bugs_users(self, contents: str) -> None: 47 self._run(todo_check.BUGS_OR_USERNAMES, contents) 48 49 def _run_bugs(self, contents: str) -> None: 50 self._run(todo_check.BUGS_ONLY, contents) 51 52 def test_one_bug_legacy(self) -> None: 53 contents = 'TODO(b/123): foo\n' 54 self._run_bugs_users(contents) 55 self.ctx.fail.assert_not_called() 56 self._run_bugs(contents) 57 self.ctx.fail.assert_not_called() 58 59 def test_one_bug_new(self) -> None: 60 contents = 'TODO: b/123 - foo\n' 61 self._run_bugs_users(contents) 62 self.ctx.fail.assert_not_called() 63 self._run_bugs(contents) 64 self.ctx.fail.assert_not_called() 65 66 def test_one_bug_short_url(self) -> None: 67 contents = 'TODO: https://pwbug.dev/123 - foo\n' 68 self._run_bugs_users(contents) 69 self.ctx.fail.assert_not_called() 70 self._run_bugs(contents) 71 self.ctx.fail.assert_not_called() 72 73 def test_one_bug_shorter_url(self) -> None: 74 contents = 'TODO: pwbug.dev/123 - foo\n' 75 self._run_bugs_users(contents) 76 self.ctx.fail.assert_not_called() 77 self._run_bugs(contents) 78 79 def test_one_bug_shorter_markdown_url(self) -> None: 80 contents = 'TODO: <pwbug.dev/123> - foo\n' 81 self._run_bugs_users(contents) 82 self.ctx.fail.assert_not_called() 83 self._run_bugs(contents) 84 self.ctx.fail.assert_not_called() 85 86 def test_one_bug_full_url(self) -> None: 87 contents = 'TODO: https://issues.pigweed.dev/issues/123 - foo\n' 88 self._run_bugs_users(contents) 89 self.ctx.fail.assert_not_called() 90 self._run_bugs(contents) 91 self.ctx.fail.assert_not_called() 92 93 def test_two_bugs_legacy(self) -> None: 94 contents = 'TODO(b/123, b/456): foo\n' 95 self._run_bugs_users(contents) 96 self.ctx.fail.assert_not_called() 97 self._run_bugs(contents) 98 self.ctx.fail.assert_not_called() 99 100 def test_two_bugs_new(self) -> None: 101 contents = 'TODO: b/123, b/456 - foo\n' 102 self._run_bugs_users(contents) 103 self.ctx.fail.assert_not_called() 104 self._run_bugs(contents) 105 self.ctx.fail.assert_not_called() 106 107 def test_three_bugs_legacy(self) -> None: 108 contents = 'TODO(b/123,b/456,b/789): foo\n' 109 self._run_bugs_users(contents) 110 self.ctx.fail.assert_not_called() 111 self._run_bugs(contents) 112 self.ctx.fail.assert_not_called() 113 114 def test_three_bugs_new(self) -> None: 115 contents = 'TODO: b/123,b/456,b/789 - foo\n' 116 self._run_bugs_users(contents) 117 self.ctx.fail.assert_not_called() 118 self._run_bugs(contents) 119 self.ctx.fail.assert_not_called() 120 121 def test_one_username_legacy(self) -> None: 122 self._run_bugs_users('TODO(usera): foo\n') 123 self.ctx.fail.assert_not_called() 124 125 def test_one_username_new(self) -> None: 126 self._run_bugs_users('TODO: usera@ - foo\n') 127 self.ctx.fail.assert_not_called() 128 129 def test_one_username_new_noat(self) -> None: 130 self._run_bugs_users('TODO: usera - foo\n') 131 self.ctx.fail.assert_called() 132 133 def test_one_username_new_short_domain(self) -> None: 134 self._run_bugs_users('TODO: usera@com - foo\n') 135 self.ctx.fail.assert_called() 136 137 def test_one_username_new_medium_domain(self) -> None: 138 self._run_bugs_users('TODO: usera@example.com - foo\n') 139 self.ctx.fail.assert_not_called() 140 141 def test_one_username_new_long_domain(self) -> None: 142 self._run_bugs_users('TODO: usera@a.b.c.d.example.com - foo\n') 143 self.ctx.fail.assert_not_called() 144 145 def test_two_usernames_legacy(self) -> None: 146 self._run_bugs_users('TODO(usera, userb): foo\n') 147 self.ctx.fail.assert_not_called() 148 149 def test_two_usernames_new(self) -> None: 150 self._run_bugs_users('TODO: usera@, userb@ - foo\n') 151 self.ctx.fail.assert_not_called() 152 153 def test_three_usernames_legacy(self) -> None: 154 self._run_bugs_users('TODO(usera,userb,userc): foo\n') 155 self.ctx.fail.assert_not_called() 156 157 def test_three_usernames_new(self) -> None: 158 self._run_bugs_users('TODO: usera@,userb@example.com,userc@ - foo\n') 159 self.ctx.fail.assert_not_called() 160 161 def test_username_not_allowed_legacy(self) -> None: 162 self._run_bugs('TODO(usera): foo\n') 163 self.ctx.fail.assert_called() 164 165 def test_username_not_allowed_new(self) -> None: 166 self._run_bugs('TODO: usera@ - foo\n') 167 self.ctx.fail.assert_called() 168 169 def test_space_after_todo_bugsonly_legacy(self) -> None: 170 self._run_bugs('TODO (b/123): foo\n') 171 self.ctx.fail.assert_called() 172 173 def test_space_after_todo_bugsonly_new(self) -> None: 174 self._run_bugs('TODO : b/123 - foo\n') 175 self.ctx.fail.assert_called() 176 177 def test_space_after_todo_bugsusers_legacy(self) -> None: 178 self._run_bugs_users('TODO (b/123): foo\n') 179 self.ctx.fail.assert_called() 180 181 def test_space_after_todo_bugsusers_new(self) -> None: 182 self._run_bugs_users('TODO : b/123 - foo\n') 183 self.ctx.fail.assert_called() 184 185 def test_space_before_bug_bugsonly_legacy(self) -> None: 186 self._run_bugs('TODO( b/123): foo\n') 187 self.ctx.fail.assert_called() 188 189 def test_no_space_before_bug_bugsonly_new(self) -> None: 190 self._run_bugs('TODO:b/123 - foo\n') 191 self.ctx.fail.assert_called() 192 193 def test_space_before_bug_bugsusers_legacy(self) -> None: 194 self._run_bugs_users('TODO( b/123): foo\n') 195 self.ctx.fail.assert_called() 196 197 def test_no_space_before_bug_bugsusers_new(self) -> None: 198 self._run_bugs_users('TODO:b/123 - foo\n') 199 self.ctx.fail.assert_called() 200 201 def test_space_after_bug_bugsonly_legacy(self) -> None: 202 self._run_bugs('TODO(b/123 ): foo\n') 203 self.ctx.fail.assert_called() 204 205 def test_no_space_after_bug_bugsonly_new(self) -> None: 206 self._run_bugs('TODO: b/123- foo\n') 207 self.ctx.fail.assert_called() 208 209 def test_no_space_before_explanation_bugsonly_new(self) -> None: 210 self._run_bugs('TODO: b/123 -foo\n') 211 self.ctx.fail.assert_called() 212 213 def test_space_after_bug_bugsusers_legacy(self) -> None: 214 self._run_bugs_users('TODO(b/123 ): foo\n') 215 self.ctx.fail.assert_called() 216 217 def test_no_space_before_explanation_bugsusers_new(self) -> None: 218 self._run_bugs_users('TODO: b/123 -foo\n') 219 self.ctx.fail.assert_called() 220 221 def test_missing_explanation_bugsonly_legacy(self) -> None: 222 self._run_bugs('TODO: b/123 -\n') 223 self.ctx.fail.assert_called() 224 225 def test_missing_explanation_bugsonly_new(self) -> None: 226 self._run_bugs('TODO: b/123\n') 227 self.ctx.fail.assert_called() 228 229 def test_missing_explanation_bugsusers_legacy(self) -> None: 230 self._run_bugs_users('TODO: b/123 -\n') 231 self.ctx.fail.assert_called() 232 233 def test_missing_explanation_bugsusers_new(self) -> None: 234 self._run_bugs_users('TODO: b/123\n') 235 self.ctx.fail.assert_called() 236 237 def test_not_a_bug_bugsonly_legacy(self) -> None: 238 self._run_bugs('TODO(cl/123): foo\n') 239 self.ctx.fail.assert_called() 240 241 def test_not_a_bug_bugsonly_new(self) -> None: 242 self._run_bugs('TODO: cl/123 - foo\n') 243 self.ctx.fail.assert_called() 244 245 def test_not_a_bug_bugsusers_legacy(self) -> None: 246 self._run_bugs_users('TODO(cl/123): foo\n') 247 self.ctx.fail.assert_called() 248 249 def test_not_a_bug_bugsusers_new(self) -> None: 250 self._run_bugs_users('TODO: cl/123 - foo\n') 251 self.ctx.fail.assert_called() 252 253 def test_but_not_bug_bugsonly_legacy(self) -> None: 254 self._run_bugs('TODO(b/123, cl/123): foo\n') 255 self.ctx.fail.assert_called() 256 257 def test_but_not_bug_bugsonly_new(self) -> None: 258 self._run_bugs('TODO: b/123, cl/123 - foo\n') 259 self.ctx.fail.assert_called() 260 261 def test_bug_not_bug_bugsusers_legacy(self) -> None: 262 self._run_bugs_users('TODO(b/123, cl/123): foo\n') 263 self.ctx.fail.assert_called() 264 265 def test_bug_not_bug_bugsusers_new(self) -> None: 266 self._run_bugs_users('TODO: b/123, cl/123 - foo\n') 267 self.ctx.fail.assert_called() 268 269 def test_empty_bugsonly_legacy(self) -> None: 270 self._run_bugs('TODO(): foo\n') 271 self.ctx.fail.assert_called() 272 273 def test_empty_bugsonly_new(self) -> None: 274 self._run_bugs('TODO: - foo\n') 275 self.ctx.fail.assert_called() 276 277 def test_empty_bugsusers_legacy(self) -> None: 278 self._run_bugs_users('TODO(): foo\n') 279 self.ctx.fail.assert_called() 280 281 def test_empty_bugsusers_new(self) -> None: 282 self._run_bugs_users('TODO: - foo\n') 283 self.ctx.fail.assert_called() 284 285 def test_bare_bugsonly_legacy(self) -> None: 286 self._run_bugs('TODO: foo\n') 287 self.ctx.fail.assert_called() 288 289 def test_bare_bugsonly_new(self) -> None: 290 self._run_bugs('TODO: foo\n') 291 self.ctx.fail.assert_called() 292 293 def test_bare_bugsusers_legacy(self) -> None: 294 self._run_bugs_users('TODO: foo\n') 295 self.ctx.fail.assert_called() 296 297 def test_bare_bugsusers_new(self) -> None: 298 self._run_bugs_users('TODO: foo\n') 299 self.ctx.fail.assert_called() 300 301 def test_fuchsia(self) -> None: 302 self._run_bugs_users('TODO(fxbug.dev/123): foo\n') 303 self.ctx.fail.assert_not_called() 304 305 def test_fuchsia_two_bugs(self) -> None: 306 self._run_bugs_users('TODO(fxbug.dev/123,fxbug.dev/321): bar\n') 307 self.ctx.fail.assert_not_called() 308 309 def test_bazel_gh_issue(self) -> None: 310 contents = ( 311 'TODO: https://github.com/bazelbuild/bazel/issues/12345 - ' 312 'Bazel sometimes works\n' 313 ) 314 self._run_bugs_users(contents) 315 self.ctx.fail.assert_not_called() 316 self._run_bugs(contents) 317 self.ctx.fail.assert_not_called() 318 319 def test_bazel_gh_issue_underscore(self) -> None: 320 contents = ( 321 'TODO: https://github.com/bazelbuild/rules_cc/issues/678910 - ' 322 'Sometimes it does not work\n' 323 ) 324 self._run_bugs_users(contents) 325 self.ctx.fail.assert_not_called() 326 self._run_bugs(contents) 327 self.ctx.fail.assert_not_called() 328 329 330if __name__ == '__main__': 331 unittest.main() 332 333# todo-check: enable 334