1 #!/usr/bin/env python 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 import os 7 import re 8 import unittest 9 10 import PRESUBMIT 11 12 13 class MockInputApi(object): 14 def __init__(self): 15 self.re = re 16 self.os_path = os.path 17 self.files = [] 18 self.is_committing = False 19 20 def AffectedFiles(self): 21 return self.files 22 23 24 class MockOutputApi(object): 25 class PresubmitResult(object): 26 def __init__(self, message, items=None, long_text=''): 27 self.message = message 28 self.items = items 29 self.long_text = long_text 30 31 class PresubmitError(PresubmitResult): 32 def __init__(self, message, items, long_text=''): 33 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) 34 self.type = 'error' 35 36 class PresubmitPromptWarning(PresubmitResult): 37 def __init__(self, message, items, long_text=''): 38 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) 39 self.type = 'warning' 40 41 class PresubmitNotifyResult(PresubmitResult): 42 def __init__(self, message, items, long_text=''): 43 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) 44 self.type = 'notify' 45 46 class PresubmitPromptOrNotify(PresubmitResult): 47 def __init__(self, message, items, long_text=''): 48 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) 49 self.type = 'promptOrNotify' 50 51 52 class MockFile(object): 53 def __init__(self, local_path, new_contents): 54 self._local_path = local_path 55 self._new_contents = new_contents 56 self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)] 57 58 def ChangedContents(self): 59 return self._changed_contents 60 61 def NewContents(self): 62 return self._new_contents 63 64 def LocalPath(self): 65 return self._local_path 66 67 68 class MockChange(object): 69 def __init__(self, changed_files): 70 self._changed_files = changed_files 71 72 def LocalPaths(self): 73 return self._changed_files 74 75 76 class IncludeOrderTest(unittest.TestCase): 77 def testSystemHeaderOrder(self): 78 scope = [(1, '#include <csystem.h>'), 79 (2, '#include <cppsystem>'), 80 (3, '#include "acustom.h"')] 81 all_linenums = [linenum for (linenum, _) in scope] 82 mock_input_api = MockInputApi() 83 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 84 '', all_linenums) 85 self.assertEqual(0, len(warnings)) 86 87 def testSystemHeaderOrderMismatch1(self): 88 scope = [(10, '#include <cppsystem>'), 89 (20, '#include <csystem.h>'), 90 (30, '#include "acustom.h"')] 91 all_linenums = [linenum for (linenum, _) in scope] 92 mock_input_api = MockInputApi() 93 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 94 '', all_linenums) 95 self.assertEqual(1, len(warnings)) 96 self.assertTrue('20' in warnings[0]) 97 98 def testSystemHeaderOrderMismatch2(self): 99 scope = [(10, '#include <cppsystem>'), 100 (20, '#include "acustom.h"'), 101 (30, '#include <csystem.h>')] 102 all_linenums = [linenum for (linenum, _) in scope] 103 mock_input_api = MockInputApi() 104 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 105 '', all_linenums) 106 self.assertEqual(1, len(warnings)) 107 self.assertTrue('30' in warnings[0]) 108 109 def testSystemHeaderOrderMismatch3(self): 110 scope = [(10, '#include "acustom.h"'), 111 (20, '#include <csystem.h>'), 112 (30, '#include <cppsystem>')] 113 all_linenums = [linenum for (linenum, _) in scope] 114 mock_input_api = MockInputApi() 115 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 116 '', all_linenums) 117 self.assertEqual(2, len(warnings)) 118 self.assertTrue('20' in warnings[0]) 119 self.assertTrue('30' in warnings[1]) 120 121 def testAlphabeticalOrderMismatch(self): 122 scope = [(10, '#include <csystem.h>'), 123 (15, '#include <bsystem.h>'), 124 (20, '#include <cppsystem>'), 125 (25, '#include <bppsystem>'), 126 (30, '#include "bcustom.h"'), 127 (35, '#include "acustom.h"')] 128 all_linenums = [linenum for (linenum, _) in scope] 129 mock_input_api = MockInputApi() 130 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 131 '', all_linenums) 132 self.assertEqual(3, len(warnings)) 133 self.assertTrue('15' in warnings[0]) 134 self.assertTrue('25' in warnings[1]) 135 self.assertTrue('35' in warnings[2]) 136 137 def testSpecialFirstInclude1(self): 138 mock_input_api = MockInputApi() 139 contents = ['#include "some/path/foo.h"', 140 '#include "a/header.h"'] 141 mock_file = MockFile('some/path/foo.cc', contents) 142 warnings = PRESUBMIT._CheckIncludeOrderInFile( 143 mock_input_api, mock_file, range(1, len(contents) + 1)) 144 self.assertEqual(0, len(warnings)) 145 146 def testSpecialFirstInclude2(self): 147 mock_input_api = MockInputApi() 148 contents = ['#include "some/other/path/foo.h"', 149 '#include "a/header.h"'] 150 mock_file = MockFile('some/path/foo.cc', contents) 151 warnings = PRESUBMIT._CheckIncludeOrderInFile( 152 mock_input_api, mock_file, range(1, len(contents) + 1)) 153 self.assertEqual(0, len(warnings)) 154 155 def testSpecialFirstInclude3(self): 156 mock_input_api = MockInputApi() 157 contents = ['#include "some/path/foo.h"', 158 '#include "a/header.h"'] 159 mock_file = MockFile('some/path/foo_platform.cc', contents) 160 warnings = PRESUBMIT._CheckIncludeOrderInFile( 161 mock_input_api, mock_file, range(1, len(contents) + 1)) 162 self.assertEqual(0, len(warnings)) 163 164 def testSpecialFirstInclude4(self): 165 mock_input_api = MockInputApi() 166 contents = ['#include "some/path/bar.h"', 167 '#include "a/header.h"'] 168 mock_file = MockFile('some/path/foo_platform.cc', contents) 169 warnings = PRESUBMIT._CheckIncludeOrderInFile( 170 mock_input_api, mock_file, range(1, len(contents) + 1)) 171 self.assertEqual(1, len(warnings)) 172 self.assertTrue('2' in warnings[0]) 173 174 def testSpecialFirstInclude5(self): 175 mock_input_api = MockInputApi() 176 contents = ['#include "some/other/path/foo.h"', 177 '#include "a/header.h"'] 178 mock_file = MockFile('some/path/foo-suffix.h', contents) 179 warnings = PRESUBMIT._CheckIncludeOrderInFile( 180 mock_input_api, mock_file, range(1, len(contents) + 1)) 181 self.assertEqual(0, len(warnings)) 182 183 def testOrderAlreadyWrong(self): 184 scope = [(1, '#include "b.h"'), 185 (2, '#include "a.h"'), 186 (3, '#include "c.h"')] 187 mock_input_api = MockInputApi() 188 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 189 '', [3]) 190 self.assertEqual(0, len(warnings)) 191 192 def testConflictAdded1(self): 193 scope = [(1, '#include "a.h"'), 194 (2, '#include "c.h"'), 195 (3, '#include "b.h"')] 196 mock_input_api = MockInputApi() 197 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 198 '', [2]) 199 self.assertEqual(1, len(warnings)) 200 self.assertTrue('3' in warnings[0]) 201 202 def testConflictAdded2(self): 203 scope = [(1, '#include "c.h"'), 204 (2, '#include "b.h"'), 205 (3, '#include "d.h"')] 206 mock_input_api = MockInputApi() 207 warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, 208 '', [2]) 209 self.assertEqual(1, len(warnings)) 210 self.assertTrue('2' in warnings[0]) 211 212 def testIfElifElseEndif(self): 213 mock_input_api = MockInputApi() 214 contents = ['#include "e.h"', 215 '#define foo', 216 '#include "f.h"', 217 '#undef foo', 218 '#include "e.h"', 219 '#if foo', 220 '#include "d.h"', 221 '#elif bar', 222 '#include "c.h"', 223 '#else', 224 '#include "b.h"', 225 '#endif', 226 '#include "a.h"'] 227 mock_file = MockFile('', contents) 228 warnings = PRESUBMIT._CheckIncludeOrderInFile( 229 mock_input_api, mock_file, range(1, len(contents) + 1)) 230 self.assertEqual(0, len(warnings)) 231 232 def testSysIncludes(self): 233 # #include <sys/...>'s can appear in any order. 234 mock_input_api = MockInputApi() 235 contents = ['#include <sys/b.h>', 236 '#include <sys/a.h>'] 237 mock_file = MockFile('', contents) 238 warnings = PRESUBMIT._CheckIncludeOrderInFile( 239 mock_input_api, mock_file, range(1, len(contents) + 1)) 240 self.assertEqual(0, len(warnings)) 241 242 def testCheckOnlyCFiles(self): 243 mock_input_api = MockInputApi() 244 mock_output_api = MockOutputApi() 245 contents = ['#include <b.h>', 246 '#include <a.h>'] 247 mock_file_cc = MockFile('something.cc', contents) 248 mock_file_h = MockFile('something.h', contents) 249 mock_file_other = MockFile('something.py', contents) 250 mock_input_api.files = [mock_file_cc, mock_file_h, mock_file_other] 251 warnings = PRESUBMIT._CheckIncludeOrder(mock_input_api, mock_output_api) 252 self.assertEqual(1, len(warnings)) 253 self.assertEqual(2, len(warnings[0].items)) 254 self.assertEqual('promptOrNotify', warnings[0].type) 255 256 def testUncheckableIncludes(self): 257 mock_input_api = MockInputApi() 258 contents = ['#include <windows.h>', 259 '#include "b.h"' 260 '#include "a.h"'] 261 mock_file = MockFile('', contents) 262 warnings = PRESUBMIT._CheckIncludeOrderInFile( 263 mock_input_api, mock_file, range(1, len(contents) + 1)) 264 self.assertEqual(0, len(warnings)) 265 266 contents = ['#include "gpu/command_buffer/gles_autogen.h"', 267 '#include "b.h"' 268 '#include "a.h"'] 269 mock_file = MockFile('', contents) 270 warnings = PRESUBMIT._CheckIncludeOrderInFile( 271 mock_input_api, mock_file, range(1, len(contents) + 1)) 272 self.assertEqual(0, len(warnings)) 273 274 contents = ['#include "gl_mock_autogen.h"', 275 '#include "b.h"' 276 '#include "a.h"'] 277 mock_file = MockFile('', contents) 278 warnings = PRESUBMIT._CheckIncludeOrderInFile( 279 mock_input_api, mock_file, range(1, len(contents) + 1)) 280 self.assertEqual(0, len(warnings)) 281 282 contents = ['#include "ipc/some_macros.h"', 283 '#include "b.h"' 284 '#include "a.h"'] 285 mock_file = MockFile('', contents) 286 warnings = PRESUBMIT._CheckIncludeOrderInFile( 287 mock_input_api, mock_file, range(1, len(contents) + 1)) 288 self.assertEqual(0, len(warnings)) 289 290 291 class VersionControlConflictsTest(unittest.TestCase): 292 def testTypicalConflict(self): 293 lines = ['<<<<<<< HEAD', 294 ' base::ScopedTempDir temp_dir_;', 295 '=======', 296 ' ScopedTempDir temp_dir_;', 297 '>>>>>>> master'] 298 errors = PRESUBMIT._CheckForVersionControlConflictsInFile( 299 MockInputApi(), MockFile('some/path/foo_platform.cc', lines)) 300 self.assertEqual(3, len(errors)) 301 self.assertTrue('1' in errors[0]) 302 self.assertTrue('3' in errors[1]) 303 self.assertTrue('5' in errors[2]) 304 305 306 class BadExtensionsTest(unittest.TestCase): 307 def testBadRejFile(self): 308 mock_input_api = MockInputApi() 309 mock_input_api.files = [ 310 MockFile('some/path/foo.cc', ''), 311 MockFile('some/path/foo.cc.rej', ''), 312 MockFile('some/path2/bar.h.rej', ''), 313 ] 314 315 results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi()) 316 self.assertEqual(1, len(results)) 317 self.assertEqual(2, len(results[0].items)) 318 self.assertTrue('foo.cc.rej' in results[0].items[0]) 319 self.assertTrue('bar.h.rej' in results[0].items[1]) 320 321 def testBadOrigFile(self): 322 mock_input_api = MockInputApi() 323 mock_input_api.files = [ 324 MockFile('other/path/qux.h.orig', ''), 325 MockFile('other/path/qux.h', ''), 326 MockFile('other/path/qux.cc', ''), 327 ] 328 329 results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi()) 330 self.assertEqual(1, len(results)) 331 self.assertEqual(1, len(results[0].items)) 332 self.assertTrue('qux.h.orig' in results[0].items[0]) 333 334 def testGoodFiles(self): 335 mock_input_api = MockInputApi() 336 mock_input_api.files = [ 337 MockFile('other/path/qux.h', ''), 338 MockFile('other/path/qux.cc', ''), 339 ] 340 results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi()) 341 self.assertEqual(0, len(results)) 342 343 def testOnlyOwnersFiles(self): 344 mock_change = MockChange([ 345 'some/path/OWNERS', 346 'A\Windows\Path\OWNERS', 347 ]) 348 results = PRESUBMIT.GetPreferredTrySlaves(None, mock_change) 349 self.assertEqual(0, len(results)) 350 351 352 class InvalidOSMacroNamesTest(unittest.TestCase): 353 def testInvalidOSMacroNames(self): 354 lines = ['#if defined(OS_WINDOWS)', 355 ' #elif defined(OS_WINDOW)', 356 ' # if defined(OS_MACOSX) || defined(OS_CHROME)', 357 '# else // defined(OS_MAC)', 358 '#endif // defined(OS_MACOS)'] 359 errors = PRESUBMIT._CheckForInvalidOSMacrosInFile( 360 MockInputApi(), MockFile('some/path/foo_platform.cc', lines)) 361 self.assertEqual(len(lines), len(errors)) 362 self.assertTrue(':1 OS_WINDOWS' in errors[0]) 363 self.assertTrue('(did you mean OS_WIN?)' in errors[0]) 364 365 def testValidOSMacroNames(self): 366 lines = ['#if defined(%s)' % m for m in PRESUBMIT._VALID_OS_MACROS] 367 errors = PRESUBMIT._CheckForInvalidOSMacrosInFile( 368 MockInputApi(), MockFile('some/path/foo_platform.cc', lines)) 369 self.assertEqual(0, len(errors)) 370 371 372 class CheckAddedDepsHaveTetsApprovalsTest(unittest.TestCase): 373 def testDepsFilesToCheck(self): 374 changed_lines = [ 375 '"+breakpad",', 376 '"+chrome/installer",', 377 '"+chrome/plugin/chrome_content_plugin_client.h",', 378 '"+chrome/utility/chrome_content_utility_client.h",', 379 '"+chromeos/chromeos_paths.h",', 380 '"+components/breakpad",', 381 '"+components/nacl/common",', 382 '"+content/public/browser/render_process_host.h",', 383 '"+grit", # For generated headers', 384 '"+grit/generated_resources.h",', 385 '"+grit/",', 386 '"+policy", # For generated headers and source', 387 '"+sandbox",', 388 '"+tools/memory_watcher",', 389 '"+third_party/lss/linux_syscall_support.h",', 390 ] 391 files_to_check = PRESUBMIT._DepsFilesToCheck(re, changed_lines) 392 expected = set([ 393 'breakpad/DEPS', 394 'chrome/installer/DEPS', 395 'chrome/plugin/DEPS', 396 'chrome/utility/DEPS', 397 'chromeos/DEPS', 398 'components/breakpad/DEPS', 399 'components/nacl/common/DEPS', 400 'content/public/browser/DEPS', 401 'policy/DEPS', 402 'sandbox/DEPS', 403 'tools/memory_watcher/DEPS', 404 'third_party/lss/DEPS', 405 ]) 406 self.assertEqual(expected, files_to_check); 407 408 409 if __name__ == '__main__': 410 unittest.main() 411