From a14806018f8de8484b387c9d688dce981536edbd Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 16 Jul 2015 10:16:42 -0700 Subject: [PATCH] Top-level files --- .clang-format | 8 + .gitattributes | 7 + .gitignore | 97 ++++ .gn | 23 + BUILD.gn | 13 + DEPS | 350 ++++++++++++ LICENSE | 27 + OWNERS | 1 + PRESUBMIT.py | 1134 +++++++++++++++++++++++++++++++++++++++ PRESUBMIT_test.py | 357 ++++++++++++ PRESUBMIT_test_mocks.py | 109 ++++ WATCHLISTS | 45 ++ codereview.settings | 6 + third_party/BUILD.gn | 43 ++ 14 files changed, 2220 insertions(+) create mode 100644 .clang-format create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .gn create mode 100644 BUILD.gn create mode 100644 DEPS create mode 100644 LICENSE create mode 100644 OWNERS create mode 100644 PRESUBMIT.py create mode 100644 PRESUBMIT_test.py create mode 100644 PRESUBMIT_test_mocks.py create mode 100644 WATCHLISTS create mode 100644 codereview.settings create mode 100644 third_party/BUILD.gn diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..6fdf1dc88 --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +# Defines the Chromium style for automatic reformatting. +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: Chromium +# This defaults to 'Auto'. Explicitly set it for a while, so that +# 'vector >' in existing files gets formatted to +# 'vector>'. ('Auto' means that clang-format will only use +# 'int>>' if the file already contains at least one such instance.) +Standard: Cpp11 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..21a13a6c6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +## This page intentionally left blank. ## +# +# Workaround for VS2013 automatically creating .gitattributes files with +# default settings that we don't want. +# See also: +# http://connect.microsoft.com/VisualStudio/feedback/details/804948/inappropriately-creates-gitattributes-file +# http://crbug.com/342064 diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..f7bff5626 --- /dev/null +++ b/.gitignore @@ -0,0 +1,97 @@ +# commonly generated files +*.pyc +*~ +.*.sw? +.DS_Store +.classpath +.cproject +.gdb_history +.gdbinit +.landmines +.project +.pub +.pydevproject +.checkstyle +cscope.* +Session.vim +tags +Thumbs.db +v8.log +/build/util/LASTCHANGE* + +# directories pulled in via deps or hooks +/build/linux/bin/eu-strip +/buildtools/ +/dart/ +/dart-pub-cache/ +/native_client/ +/out/ +/out_*/ +/sdch/open-vcdiff/ +/testing/gmock/ +/testing/gtest/ +/third_party/android_tools/ +/third_party/angle/ +/third_party/appurify-python/ +/third_party/boringssl/src/ +/third_party/brotli/src/ +/third_party/colorama/src/ +/third_party/dart-sdk/ +/third_party/dart-pkg/archive +/third_party/dart-pkg/args +/third_party/dart-pkg/box2d +/third_party/dart-pkg/cassowary +/third_party/dart-pkg/collection +/third_party/dart-pkg/crypto +/third_party/dart-pkg/newton +/third_party/dart-pkg/path +/third_party/dart-pkg/quiver +/third_party/dart-pkg/source_span +/third_party/dart-pkg/string_scanner +/third_party/dart-pkg/vector_math +/third_party/dart-pkg/vector_math +/third_party/dart-pkg/yaml +/third_party/dejavu-fonts-ttf-2.34/ttf/*.ttf +/third_party/freetype-android/src +/third_party/go/tool/ +/third_party/icu/ +/third_party/jsr-305/src/ +/third_party/libc++/trunk/ +/third_party/libc++abi/trunk/ +/third_party/libjpeg_turbo/ +/third_party/llvm/ +/third_party/llvm-build/ +/third_party/junit/src/ +/third_party/mesa/src/ +/third_party/mockito/src/ +/third_party/pdfium/ +/third_party/pywebsocket/src/ +/third_party/requests/src/ +/third_party/robolectric/src/ +/third_party/skia/ +/third_party/smhasher/src/ +/third_party/yasm/binaries/ +/third_party/yasm/source/patched-yasm/ +/tools/grit/ +/v8/ + +# dart packages directories and related. +/mojo/dart/apptest/packages +/mojo/dart/mojo_services/packages +/mojo/dart/mojo_services/pubspec.lock +/mojo/dart/mojom/bin/packages +/mojo/dart/mojom/packages +/mojo/dart/mojom/test/packages +/sky/examples/hello_world/packages +/sky/examples/stocks/packages +/sky/sdk/packages/mojo/packages +/sky/sdk/packages/mojo/pubspec.lock +/sky/sdk/packages/sky/packages +/sky/sdk/packages/sky/pubspec.lock + +# sky tools +/sky/tools/skygo/linux64/sky_server +/sky/tools/skygo/mac/sky_server + +# downloaded keyboard_native resources. +/services/keyboard_native/res/*.png diff --git a/.gn b/.gn new file mode 100644 index 000000000..6dcc94ecc --- /dev/null +++ b/.gn @@ -0,0 +1,23 @@ +# This file is used by the experimental meta-buildsystem in src/tools/gn to +# find the root of the source tree and to set startup options. + +# The location of the build configuration file. +buildconfig = "//build/config/BUILDCONFIG.gn" + +# The secondary source root is a parallel directory tree where +# GN build files are placed when they can not be placed directly +# in the source tree, e.g. for third party source trees. +secondary_source = "//build/secondary/" + +# The set of targets known to pass 'gn check'. When all targets pass, remove +# this. +check_targets = [ + "//crypto/*", + "//dart/*", + "//mojo/*", + "//mojom/*", + "//skia/*", + "//sky/*", + "//ui/*", + "//url/*", +] diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 000000000..9190fb8c9 --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,13 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This target will be built if no target is specified when invoking ninja. +group("default") { + testonly = true + + deps = [ + "//sky", + "//services/sky", + ] +} diff --git a/DEPS b/DEPS new file mode 100644 index 000000000..68fd41e16 --- /dev/null +++ b/DEPS @@ -0,0 +1,350 @@ +# This file is automatically processed to create .DEPS.git which is the file +# that gclient uses under git. +# +# See http://code.google.com/p/chromium/wiki/UsingGit +# +# To test manually, run: +# python tools/deps2git/deps2git.py -o .DEPS.git -w +# where is the absolute path to the directory containing the +# .gclient file (the parent of 'src'). +# +# Then commit .DEPS.git locally (gclient doesn't like dirty trees) and run +# gclient sync +# Verify the thing happened you wanted. Then revert your .DEPS.git change +# DO NOT CHECK IN CHANGES TO .DEPS.git upstream. It will be automatically +# updated by a bot when you modify this one. +# +# When adding a new dependency, please update the top-level .gitignore file +# to list the dependency's destination directory. + +vars = { + 'chromium_git': 'https://chromium.googlesource.com', + 'dart_svn': 'https://dart.googlecode.com', + 'skia_revision': '2ced78866fcadd98895777c8dffe92e229775181', + 'angle_revision': 'bdd419f9f5b006e913606e7363125942c8ae06bc', + 'dart_revision': 'e5e3d161e70d862608e6597facdf5ac8ae9ab2c3', + 'dart_observatory_packages_revision': '45565', + 'boringssl_revision': '642f1498d056dbba3e50ed5a232ab2f482626dec', + + 'buildtools_revision': 'fa660d47fa1a6c649d5c29e001348447c55709e6', + + 'archive_dart_revision': '07ffd98c5403b7f9ae067b57dc9487611be420f5', + 'args_dart_revision': 'e0e8377412ee6cd6a5a4a8632848181c1db91f44', + 'box2d_dart_revision': 'c5e65d9546275e78ad2a1d51b459e7638f6e4323', + 'cassowary_dart_revision': '7e5afc5b3956a18636d5b37b1dcba1705865564b', + 'collection_dart_revision': '79ebc6fc2dae581cb23ad50a5c600c1b7dd132f8', + 'crypto_dart_revision': 'd4558dea1639e5ad2a41d045265b8ece270c2d90', + 'newton_dart_revision': '26da04f0c441d005a6ecbf62ae047cd02ec9abc5', + 'path_dart_revision': '2f3dcdec32011f1bc41194ae3640d6d9292a7096', + 'quiver_dart_revision': '6bab7dec34189eee579178eb16d3063c8ae69031', + 'source_span_dart_revision': '5c6c13f62fc111adaace3aeb4a38853d64481d06', + 'string_scanner_dart_revision': '9f00056b32f41efc376adecfb696a01bc7c593d7', + 'vector_math_dart_revision': '65915583f7aa606cb47ed265f853c18c60102b81', + 'yaml_dart_revision': 'd8c1ce75edf051ea1d5583b24474f8656abb4920', +} + +# Only these hosts are allowed for dependencies in this DEPS file. +# If you need to add a new host, contact chrome infrastructure team. +allowed_hosts = [ + 'chromium.googlesource.com', + 'dart.googlecode.com', +] + +deps = { + 'src/buildtools': + Var('chromium_git') + '/chromium/buildtools.git' + '@' + Var('buildtools_revision'), + + 'src/testing/gtest': + Var('chromium_git') + '/external/googletest.git' + '@' + 'be1868139ffe0ccd0e8e3b37292b84c821d9c8ad', # from svn revision 704 + + 'src/testing/gmock': + Var('chromium_git') + '/external/googlemock.git' + '@' + '29763965ab52f24565299976b936d1265cb6a271', # from svn revision 501 + + 'src/third_party/angle': + Var('chromium_git') + '/angle/angle.git' + '@' + Var('angle_revision'), + + 'src/third_party/icu': + Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'c3f79166089e5360c09e3053fce50e6e296c3204', + + 'src/dart': + Var('chromium_git') + '/external/github.com/dart-lang/sdk.git' + '@' + Var('dart_revision'), + + 'src/dart/third_party/observatory_pub_packages': + Var('dart_svn') + '/svn/third_party/observatory_pub_packages' + '@' + + Var('dart_observatory_packages_revision'), + + 'src/third_party/skia': + Var('chromium_git') + '/skia.git' + '@' + Var('skia_revision'), + + 'src/third_party/yasm/source/patched-yasm': + Var('chromium_git') + '/chromium/deps/yasm/patched-yasm.git' + '@' + '4671120cd8558ce62ee8672ebf3eb6f5216f909b', + + 'src/third_party/libjpeg_turbo': + Var('chromium_git') + '/chromium/deps/libjpeg_turbo.git' + '@' + '034e9a9747e0983bc19808ea70e469bc8342081f', + + 'src/third_party/smhasher/src': + Var('chromium_git') + '/external/smhasher.git' + '@' + 'e87738e57558e0ec472b2fc3a643b838e5b6e88f', + + 'src/third_party/mesa/src': + Var('chromium_git') + '/chromium/deps/mesa.git' + '@' + '071d25db04c23821a12a8b260ab9d96a097402f0', + + 'src/third_party/boringssl/src': + 'https://boringssl.googlesource.com/boringssl.git' + '@' + Var('boringssl_revision'), + + 'src/third_party/requests/src': + Var('chromium_git') + '/external/github.com/kennethreitz/requests.git' + '@' + 'f172b30356d821d180fa4ecfa3e71c7274a32de4', + + 'src/third_party/dart-pkg/archive': + Var('chromium_git') + '/external/github.com/brendan-duncan/archive.git' + '@' + Var('archive_dart_revision'), + + 'src/third_party/dart-pkg/args': + Var('chromium_git') + '/external/github.com/dart-lang/args.git' + '@' + Var('args_dart_revision'), + + 'src/third_party/dart-pkg/box2d': + Var('chromium_git') + '/external/github.com/google/box2d.dart.git' + '@' + Var('box2d_dart_revision'), + + 'src/third_party/dart-pkg/cassowary': + Var('chromium_git') + '/external/github.com/domokit/cassowary.git' + '@' + Var('cassowary_dart_revision'), + + 'src/third_party/dart-pkg/collection': + Var('chromium_git') + '/external/github.com/dart-lang/collection.git' + '@' + Var('collection_dart_revision'), + + 'src/third_party/dart-pkg/crypto': + Var('chromium_git') + '/external/github.com/dart-lang/crypto.git' + '@' + Var('crypto_dart_revision'), + + 'src/third_party/dart-pkg/newton': + Var('chromium_git') + '/external/github.com/domokit/newton.git' + '@' + Var('newton_dart_revision'), + + 'src/third_party/dart-pkg/path': + Var('chromium_git') + '/external/github.com/dart-lang/path.git' + '@' + Var('path_dart_revision'), + + 'src/third_party/dart-pkg/quiver': + Var('chromium_git') + '/external/github.com/google/quiver-dart.git' + '@' + Var('quiver_dart_revision'), + + 'src/third_party/dart-pkg/source_span': + Var('chromium_git') + '/external/github.com/dart-lang/source_span.git' + '@' + Var('source_span_dart_revision'), + + 'src/third_party/dart-pkg/string_scanner': + Var('chromium_git') + '/external/github.com/dart-lang/string_scanner.git' + '@' + Var('string_scanner_dart_revision'), + + 'src/third_party/dart-pkg/vector_math': + Var('chromium_git') + '/external/github.com/google/vector_math.dart.git' + '@' + Var('vector_math_dart_revision'), + + 'src/third_party/dart-pkg/yaml': + Var('chromium_git') + '/external/github.com/dart-lang/yaml.git' + '@' + Var('yaml_dart_revision'), +} + +deps_os = { + 'android': { + 'src/third_party/colorama/src': + Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', + + 'src/third_party/jsr-305/src': + Var('chromium_git') + '/external/jsr-305.git' + '@' + '642c508235471f7220af6d5df2d3210e3bfc0919', + + 'src/third_party/junit/src': + Var('chromium_git') + '/external/junit.git' + '@' + '45a44647e7306262162e1346b750c3209019f2e1', + + 'src/third_party/mockito/src': + Var('chromium_git') + '/external/mockito/mockito.git' + '@' + 'ed99a52e94a84bd7c467f2443b475a22fcc6ba8e', + + 'src/third_party/robolectric/lib': + Var('chromium_git') + '/chromium/third_party/robolectric.git' + '@' + '6b63c99a8b6967acdb42cbed0adb067c80efc810', + + 'src/third_party/appurify-python/src': + Var('chromium_git') + '/external/github.com/appurify/appurify-python.git' + '@' + 'ee7abd5c5ae3106f72b2a0b9d2cb55094688e867', + + 'src/third_party/freetype-android/src': + Var('chromium_git') + '/chromium/src/third_party/freetype.git' + '@' + 'd1028db70bea988d1022e4d463de66581c696160', + + 'src/third_party/requests/src': + Var('chromium_git') + '/external/github.com/kennethreitz/requests.git' + '@' + 'f172b30356d821d180fa4ecfa3e71c7274a32de4', + }, +} + + +hooks = [ + { + # This clobbers when necessary (based on get_landmines.py). It must be the + # first hook so that other things that get/generate into the output + # directory will not subsequently be clobbered. + 'name': 'landmines', + 'pattern': '.', + 'action': [ + 'python', + 'src/build/landmines.py', + ], + }, + { + # Pull clang if needed or requested via GYP_DEFINES. + 'name': 'clang', + 'pattern': '.', + 'action': ['python', 'src/tools/clang/scripts/update.py', '--if-needed'], + }, + { + # Pull dart sdk if needed + 'name': 'dart', + 'pattern': '.', + 'action': ['python', 'src/tools/dart/update.py'], + }, + { + # This downloads android_tools according to tools/android/VERSION_*. + 'name': 'android_tools', + 'pattern': '.', + 'action': ['python', 'src/tools/android/download_android_tools.py'], + }, + { + # This downloads SDK extras and puts them in the + # third_party/android_tools/sdk/extras directory on the bots. Developers + # need to manually install these packages and accept the ToS. + 'name': 'sdkextras', + 'pattern': '.', + # When adding a new sdk extras package to download, add the package + # directory and zip file to .gitignore in third_party/android_tools. + 'action': ['python', 'src/build/download_sdk_extras.py'], + }, + { + # Update LASTCHANGE. This is also run by export_tarball.py in + # src/tools/export_tarball - please keep them in sync. + 'name': 'lastchange', + 'pattern': '.', + 'action': ['python', 'src/build/util/lastchange.py', + '-o', 'src/build/util/LASTCHANGE'], + }, + # Pull GN binaries. This needs to be before running GYP below. + { + 'name': 'gn_linux64', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'src/buildtools/linux64/gn.sha1', + ], + }, + { + 'name': 'gn_mac', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'src/buildtools/mac/gn.sha1', + ], + }, + { + 'name': 'gn_win', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=win*', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'src/buildtools/win/gn.exe.sha1', + ], + }, + # Pull clang-format binaries using checked-in hashes. + { + 'name': 'clang_format_linux', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', 'chromium-clang-format', + '-s', 'src/buildtools/linux64/clang-format.sha1', + ], + }, + { + 'name': 'clang_format_mac', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', 'chromium-clang-format', + '-s', 'src/buildtools/mac/clang-format.sha1', + ], + }, + # Pull sky_server binaries using checked-in hashes. + { + 'name': 'sky_server_linux', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', 'mojo', + '-s', 'src/sky/tools/skygo/linux64/sky_server.sha1', + ], + }, + { + 'name': 'sky_server_mac', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', 'mojo', + '-s', 'src/sky/tools/skygo/mac/sky_server.sha1', + ], + }, + { + 'name': 'material_design_icons', + 'pattern': '.', + 'action': [ + 'python', + 'src/sky/sdk/lib/download_material_design_icons', + ], + }, + # Pull binutils for linux, enabled debug fission for faster linking / + # debugging when used with clang on Ubuntu Precise. + # https://code.google.com/p/chromium/issues/detail?id=352046 + { + 'name': 'binutils', + 'pattern': 'src/third_party/binutils', + 'action': [ + 'python', + 'src/third_party/binutils/download.py', + ], + }, + # Pull eu-strip binaries using checked-in hashes. + { + 'name': 'eu-strip', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', 'chromium-eu-strip', + '-s', 'src/build/linux/bin/eu-strip.sha1', + ], + }, + # Run "pub get" on any directories with checked-in pubspec.yaml files + # (excluding sky/, whose pubspec.yaml files are not intended for supporting + # building in-place in the repo). + { + 'name': 'run_dart_pub_get', + 'pattern': '', + 'action': [ 'python', + 'src/mojo/public/tools/git/dart_pub_get.py', + '--repository-root', '../../../..', + '--dart-sdk-directory', + '../../../../third_party/dart-sdk/dart-sdk', + '--dirs-to-ignore', 'sky/', + ], + }, + { + # Ensure that we don't accidentally reference any .pyc files whose + # corresponding .py files have already been deleted. + 'name': 'remove_stale_pyc_files', + 'pattern': 'src/tools/.*\\.py', + 'action': [ + 'python', + 'src/tools/remove_stale_pyc_files.py', + 'src/tools', + ], + }, +] diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..972bb2edb --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/OWNERS b/OWNERS new file mode 100644 index 000000000..72e8ffc0d --- /dev/null +++ b/OWNERS @@ -0,0 +1 @@ +* diff --git a/PRESUBMIT.py b/PRESUBMIT.py new file mode 100644 index 000000000..7b3b26cf3 --- /dev/null +++ b/PRESUBMIT.py @@ -0,0 +1,1134 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Top-level presubmit script for Chromium. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details about the presubmit API built into gcl. +""" + + +_EXCLUDED_PATHS = ( + r"^native_client_sdk/src/build_tools/make_rules.py", + r"^native_client_sdk/src/build_tools/make_simple.py", + r"^native_client_sdk/src/tools/.*.mk", + r"^skia/.*", + r"^v8/.*", + r".*MakeFile$", + r".+_autogen\.h$", + r".+/pnacl_shim\.c$", + r"^gpu/config/.*_list_json\.cc$", + r"^tools/android_stack_parser/.*" +) + +_SKY_PATHS = ( + r"^sky/.*", +) + +# Fragment of a regular expression that matches C++ and Objective-C++ +# implementation files. +_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$' + +# Regular expression that matches code only used for test binaries +# (best effort). +_TEST_CODE_EXCLUDED_PATHS = ( + r'.*/(fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS, + r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS, + r'.+_(app|browser|perf|pixel|unit)?test(_[a-z]+)?%s' % + _IMPLEMENTATION_EXTENSIONS, + r'.*/(test|tool(s)?)/.*', + # Non-production example code. + r'mojo/examples/.*', + # Launcher for running iOS tests on the simulator. + r'testing/iossim/iossim\.mm$', +) + +_TEST_ONLY_WARNING = ( + 'You might be calling functions intended only for testing from\n' + 'production code. It is OK to ignore this warning if you know what\n' + 'you are doing, as the heuristics used to detect the situation are\n' + 'not perfect. The commit queue will not block on this warning.') + + +_INCLUDE_ORDER_WARNING = ( + 'Your #include order seems to be broken. Send mail to\n' + 'marja@chromium.org if this is not the case.') + + +_BANNED_CPP_FUNCTIONS = ( + # Make sure that gtest's FRIEND_TEST() macro is not used; the + # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be + # used instead since that allows for FLAKY_ and DISABLED_ prefixes. + ( + 'FRIEND_TEST(', + ( + 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include', + 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.', + ), + False, + (), + ), + ( + 'ScopedAllowIO', + ( + 'New code should not use ScopedAllowIO. Post a task to the blocking', + 'pool or the FILE thread instead.', + ), + True, + ( + r"^base/process/process_metrics_linux\.cc$", + r"^mojo/edk/embedder/simple_platform_shared_buffer_posix\.cc$", + ), + ), + ( + 'SkRefPtr', + ( + 'The use of SkRefPtr is prohibited. ', + 'Please use skia::RefPtr instead.' + ), + True, + (), + ), + ( + 'SkAutoRef', + ( + 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ', + 'Please use skia::RefPtr instead.' + ), + True, + (), + ), + ( + 'SkAutoTUnref', + ( + 'The use of SkAutoTUnref is dangerous because it implicitly ', + 'converts to a raw pointer. Please use skia::RefPtr instead.' + ), + True, + (), + ), + ( + 'SkAutoUnref', + ( + 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ', + 'because it implicitly converts to a raw pointer. ', + 'Please use skia::RefPtr instead.' + ), + True, + (), + ), + ( + r'/HANDLE_EINTR\(.*close', + ( + 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file', + 'descriptor will be closed, and it is incorrect to retry the close.', + 'Either call close directly and ignore its return value, or wrap close', + 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623' + ), + True, + (), + ), + ( + r'/IGNORE_EINTR\((?!.*close)', + ( + 'IGNORE_EINTR is only valid when wrapping close. To wrap other system', + 'calls, use HANDLE_EINTR. See http://crbug.com/269623', + ), + True, + ( + # Files that #define IGNORE_EINTR. + r'^base/posix/eintr_wrapper\.h$', + ), + ), + ( + r'/v8::Extension\(', + ( + 'Do not introduce new v8::Extensions into the code base, use', + 'gin::Wrappable instead. See http://crbug.com/334679', + ), + True, + (), + ), +) + + +_VALID_OS_MACROS = ( + # Please keep sorted. + 'OS_ANDROID', + 'OS_ANDROID_HOST', + 'OS_BSD', + 'OS_CAT', # For testing. + 'OS_CHROMEOS', + 'OS_FREEBSD', + 'OS_IOS', + 'OS_LINUX', + 'OS_MACOSX', + 'OS_NACL', + 'OS_OPENBSD', + 'OS_POSIX', + 'OS_QNX', + 'OS_SOLARIS', + 'OS_WIN', +) + + +def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api): + """Attempts to prevent use of functions intended only for testing in + non-testing code. For now this is just a best-effort implementation + that ignores header files and may have some false positives. A + better implementation would probably need a proper C++ parser. + """ + # We only scan .cc files and the like, as the declaration of + # for-testing functions in header files are hard to distinguish from + # calls to such functions without a proper C++ parser. + file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS + + base_function_pattern = r'[ :]test::[^\s]+|ForTest(ing)?|for_test(ing)?' + inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern) + comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern) + exclusion_pattern = input_api.re.compile( + r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % ( + base_function_pattern, base_function_pattern)) + + def FilterFile(affected_file): + black_list = (_EXCLUDED_PATHS + + _TEST_CODE_EXCLUDED_PATHS + + input_api.DEFAULT_BLACK_LIST) + return input_api.FilterSourceFile( + affected_file, + white_list=(file_inclusion_pattern, ), + black_list=black_list) + + problems = [] + for f in input_api.AffectedSourceFiles(FilterFile): + local_path = f.LocalPath() + for line_number, line in f.ChangedContents(): + if (inclusion_pattern.search(line) and + not comment_pattern.search(line) and + not exclusion_pattern.search(line)): + problems.append( + '%s:%d\n %s' % (local_path, line_number, line.strip())) + + if problems: + return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)] + else: + return [] + + +def _CheckNoIOStreamInHeaders(input_api, output_api): + """Checks to make sure no .h files include .""" + files = [] + pattern = input_api.re.compile(r'^#include\s*', + input_api.re.MULTILINE) + for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): + if not f.LocalPath().endswith('.h'): + continue + contents = input_api.ReadFile(f) + if pattern.search(contents): + files.append(f) + + if len(files): + return [ output_api.PresubmitError( + 'Do not #include in header files, since it inserts static ' + 'initialization into every file including the header. Instead, ' + '#include . See http://crbug.com/94794', + files) ] + return [] + + +def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api): + """Checks to make sure no source files use UNIT_TEST""" + problems = [] + for f in input_api.AffectedFiles(): + if (not f.LocalPath().endswith(('.cc', '.mm'))): + continue + + for line_num, line in f.ChangedContents(): + if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'): + problems.append(' %s:%d' % (f.LocalPath(), line_num)) + + if not problems: + return [] + return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' + + '\n'.join(problems))] + + +def _CheckNoNewWStrings(input_api, output_api): + """Checks to make sure we don't introduce use of wstrings.""" + problems = [] + for f in input_api.AffectedFiles(): + if (not f.LocalPath().endswith(('.cc', '.h')) or + f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h'))): + continue + + allowWString = False + for line_num, line in f.ChangedContents(): + if 'presubmit: allow wstring' in line: + allowWString = True + elif not allowWString and 'wstring' in line: + problems.append(' %s:%d' % (f.LocalPath(), line_num)) + allowWString = False + else: + allowWString = False + + if not problems: + return [] + return [output_api.PresubmitPromptWarning('New code should not use wstrings.' + ' If you are calling a cross-platform API that accepts a wstring, ' + 'fix the API.\n' + + '\n'.join(problems))] + + +def _CheckNoDEPSGIT(input_api, output_api): + """Make sure .DEPS.git is never modified manually.""" + if any(f.LocalPath().endswith('.DEPS.git') for f in + input_api.AffectedFiles()): + return [output_api.PresubmitError( + 'Never commit changes to .DEPS.git. This file is maintained by an\n' + 'automated system based on what\'s in DEPS and your changes will be\n' + 'overwritten.\n' + 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/get-the-code#Rolling_DEPS\n' + 'for more information')] + return [] + + +def _CheckValidHostsInDEPS(input_api, output_api): + """Checks that DEPS file deps are from allowed_hosts.""" + # Run only if DEPS file has been modified to annoy fewer bystanders. + if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()): + return [] + # Outsource work to gclient verify + try: + input_api.subprocess.check_output(['gclient', 'verify']) + return [] + except input_api.subprocess.CalledProcessError, error: + return [output_api.PresubmitError( + 'DEPS file must have only git dependencies.', + long_text=error.output)] + + +def _CheckNoBannedFunctions(input_api, output_api): + """Make sure that banned functions are not used.""" + warnings = [] + errors = [] + + file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h')) + for f in input_api.AffectedFiles(file_filter=file_filter): + for line_num, line in f.ChangedContents(): + for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS: + def IsBlacklisted(affected_file, blacklist): + local_path = affected_file.LocalPath() + for item in blacklist: + if input_api.re.match(item, local_path): + return True + return False + if IsBlacklisted(f, excluded_paths): + continue + matched = False + if func_name[0:1] == '/': + regex = func_name[1:] + if input_api.re.search(regex, line): + matched = True + elif func_name in line: + matched = True + if matched: + problems = warnings; + if error: + problems = errors; + problems.append(' %s:%d:' % (f.LocalPath(), line_num)) + for message_line in message: + problems.append(' %s' % message_line) + + result = [] + if (warnings): + result.append(output_api.PresubmitPromptWarning( + 'Banned functions were used.\n' + '\n'.join(warnings))) + if (errors): + result.append(output_api.PresubmitError( + 'Banned functions were used.\n' + '\n'.join(errors))) + return result + + +def _CheckNoPragmaOnce(input_api, output_api): + """Make sure that banned functions are not used.""" + files = [] + pattern = input_api.re.compile(r'^#pragma\s+once', + input_api.re.MULTILINE) + for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): + if not f.LocalPath().endswith('.h'): + continue + contents = input_api.ReadFile(f) + if pattern.search(contents): + files.append(f) + + if files: + return [output_api.PresubmitError( + 'Do not use #pragma once in header files.\n' + 'See http://www.chromium.org/developers/coding-style#TOC-File-headers', + files)] + return [] + + +def _CheckNoTrinaryTrueFalse(input_api, output_api): + """Checks to make sure we don't introduce use of foo ? true : false.""" + problems = [] + pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)') + for f in input_api.AffectedFiles(): + if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')): + continue + + for line_num, line in f.ChangedContents(): + if pattern.match(line): + problems.append(' %s:%d' % (f.LocalPath(), line_num)) + + if not problems: + return [] + return [output_api.PresubmitPromptWarning( + 'Please consider avoiding the "? true : false" pattern if possible.\n' + + '\n'.join(problems))] + + +def _CheckFilePermissions(input_api, output_api): + """Check that all files have their permissions properly set.""" + if input_api.platform == 'win32': + return [] + args = [input_api.python_executable, 'tools/checkperms/checkperms.py', + '--root', input_api.change.RepositoryRoot()] + for f in input_api.AffectedFiles(): + args += ['--file', f.LocalPath()] + checkperms = input_api.subprocess.Popen(args, + stdout=input_api.subprocess.PIPE) + errors = checkperms.communicate()[0].strip() + if errors: + return [output_api.PresubmitError('checkperms.py failed.', + errors.splitlines())] + return [] + + +def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums): + """Checks that the lines in scope occur in the right order. + + 1. C system files in alphabetical order + 2. C++ system files in alphabetical order + 3. Project's .h files + """ + + c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>') + cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>') + custom_include_pattern = input_api.re.compile(r'\s*#include ".*') + + C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3) + + state = C_SYSTEM_INCLUDES + + previous_line = '' + previous_line_num = 0 + problem_linenums = [] + for line_num, line in scope: + if c_system_include_pattern.match(line): + if state != C_SYSTEM_INCLUDES: + problem_linenums.append((line_num, previous_line_num)) + elif previous_line and previous_line > line: + problem_linenums.append((line_num, previous_line_num)) + elif cpp_system_include_pattern.match(line): + if state == C_SYSTEM_INCLUDES: + state = CPP_SYSTEM_INCLUDES + elif state == CUSTOM_INCLUDES: + problem_linenums.append((line_num, previous_line_num)) + elif previous_line and previous_line > line: + problem_linenums.append((line_num, previous_line_num)) + elif custom_include_pattern.match(line): + if state != CUSTOM_INCLUDES: + state = CUSTOM_INCLUDES + elif previous_line and previous_line > line: + problem_linenums.append((line_num, previous_line_num)) + else: + problem_linenums.append(line_num) + previous_line = line + previous_line_num = line_num + + warnings = [] + for (line_num, previous_line_num) in problem_linenums: + if line_num in changed_linenums or previous_line_num in changed_linenums: + warnings.append(' %s:%d' % (file_path, line_num)) + return warnings + + +def _CheckIncludeOrderInFile(input_api, f, changed_linenums): + """Checks the #include order for the given file f.""" + + system_include_pattern = input_api.re.compile(r'\s*#include \<.*') + # Exclude the following includes from the check: + # 1) #include <.../...>, e.g., includes often need to appear in a + # specific order. + # 2) , "build/build_config.h" + excluded_include_pattern = input_api.re.compile( + r'\s*#include (\<.*/.*|\|"build/build_config.h")') + custom_include_pattern = input_api.re.compile(r'\s*#include "(?P.*)"') + # Match the final or penultimate token if it is xxxtest so we can ignore it + # when considering the special first include. + test_file_tag_pattern = input_api.re.compile( + r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)') + if_pattern = input_api.re.compile( + r'\s*#\s*(if|elif|else|endif|define|undef).*') + # Some files need specialized order of includes; exclude such files from this + # check. + uncheckable_includes_pattern = input_api.re.compile( + r'\s*#include ' + '("ipc/.*macros\.h"||".*gl.*autogen.h")\s*') + + contents = f.NewContents() + warnings = [] + line_num = 0 + + # Handle the special first include. If the first include file is + # some/path/file.h, the corresponding including file can be some/path/file.cc, + # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h + # etc. It's also possible that no special first include exists. + # If the included file is some/path/file_platform.h the including file could + # also be some/path/file_xxxtest_platform.h. + including_file_base_name = test_file_tag_pattern.sub( + '', input_api.os_path.basename(f.LocalPath())) + + for line in contents: + line_num += 1 + if system_include_pattern.match(line): + # No special first include -> process the line again along with normal + # includes. + line_num -= 1 + break + match = custom_include_pattern.match(line) + if match: + match_dict = match.groupdict() + header_basename = test_file_tag_pattern.sub( + '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '') + + if header_basename not in including_file_base_name: + # No special first include -> process the line again along with normal + # includes. + line_num -= 1 + break + + # Split into scopes: Each region between #if and #endif is its own scope. + scopes = [] + current_scope = [] + for line in contents[line_num:]: + line_num += 1 + if uncheckable_includes_pattern.match(line): + continue + if if_pattern.match(line): + scopes.append(current_scope) + current_scope = [] + elif ((system_include_pattern.match(line) or + custom_include_pattern.match(line)) and + not excluded_include_pattern.match(line)): + current_scope.append((line_num, line)) + scopes.append(current_scope) + + for scope in scopes: + warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(), + changed_linenums)) + return warnings + + +def _CheckIncludeOrder(input_api, output_api): + """Checks that the #include order is correct. + + 1. The corresponding header for source files. + 2. C system files in alphabetical order + 3. C++ system files in alphabetical order + 4. Project's .h files in alphabetical order + + Each region separated by #if, #elif, #else, #endif, #define and #undef follows + these rules separately. + """ + def FileFilterIncludeOrder(affected_file): + black_list = (_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST) + return input_api.FilterSourceFile(affected_file, black_list=black_list) + + warnings = [] + for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder): + if f.LocalPath().endswith(('.cc', '.h')): + changed_linenums = set(line_num for line_num, _ in f.ChangedContents()) + warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums)) + + results = [] + if warnings: + results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING, + warnings)) + return results + + +def _CheckForVersionControlConflictsInFile(input_api, f): + pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$') + errors = [] + for line_num, line in f.ChangedContents(): + if pattern.match(line): + errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line)) + return errors + + +def _CheckForVersionControlConflicts(input_api, output_api): + """Usually this is not intentional and will cause a compile failure.""" + errors = [] + for f in input_api.AffectedFiles(): + errors.extend(_CheckForVersionControlConflictsInFile(input_api, f)) + + results = [] + if errors: + results.append(output_api.PresubmitError( + 'Version control conflict markers found, please resolve.', errors)) + return results + + +def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api): + def FilterFile(affected_file): + """Filter function for use with input_api.AffectedSourceFiles, + below. This filters out everything except non-test files from + top-level directories that generally speaking should not hard-code + service URLs (e.g. src/android_webview/, src/content/ and others). + """ + return input_api.FilterSourceFile( + affected_file, + white_list=(r'^base/.*', ), + black_list=(_EXCLUDED_PATHS + + _TEST_CODE_EXCLUDED_PATHS + + input_api.DEFAULT_BLACK_LIST)) + + base_pattern = '"[^"]*google\.com[^"]*"' + comment_pattern = input_api.re.compile('//.*%s' % base_pattern) + pattern = input_api.re.compile(base_pattern) + problems = [] # items are (filename, line_number, line) + for f in input_api.AffectedSourceFiles(FilterFile): + for line_num, line in f.ChangedContents(): + if not comment_pattern.search(line) and pattern.search(line): + problems.append((f.LocalPath(), line_num, line)) + + if problems: + return [output_api.PresubmitPromptOrNotify( + 'Most layers below src/chrome/ should not hardcode service URLs.\n' + 'Are you sure this is correct?', + [' %s:%d: %s' % ( + problem[0], problem[1], problem[2]) for problem in problems])] + else: + return [] + + +def _CheckNoAbbreviationInPngFileName(input_api, output_api): + """Makes sure there are no abbreviations in the name of PNG files. + """ + pattern = input_api.re.compile(r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$') + errors = [] + for f in input_api.AffectedFiles(include_deletes=False): + if pattern.match(f.LocalPath()): + errors.append(' %s' % f.LocalPath()) + + results = [] + if errors: + results.append(output_api.PresubmitError( + 'The name of PNG files should not have abbreviations. \n' + 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n' + 'Contact oshima@chromium.org if you have questions.', errors)) + return results + + +def _CheckSpamLogging(input_api, output_api): + file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS + black_list = (_EXCLUDED_PATHS + + _TEST_CODE_EXCLUDED_PATHS + + input_api.DEFAULT_BLACK_LIST + + (r"^base/logging\.h$", + r"^base/logging\.cc$", + r"^examples/wget/wget\.cc$", + r"^shell/application_manager/network_fetcher\.cc$", + r"^shell/tracer\.cc$", + r"^sandbox/linux/.*", + r"^tools/.*")) + source_file_filter = lambda x: input_api.FilterSourceFile( + x, white_list=(file_inclusion_pattern,), black_list=black_list) + + log_macro = input_api.re.compile(r"\bD?LOG\s*\(\s*INFO\s*\)") + log_if_macro = input_api.re.compile(r"\bD?LOG_IF\s*\(\s*INFO\s*,") + printf_macro = input_api.re.compile(r"\bprintf\(") + fprintf_macro = input_api.re.compile(r"\bfprintf\((stdout|stderr)") + + log_info = [] + printf = [] + + for f in input_api.AffectedSourceFiles(source_file_filter): + for linenum, line in f.ChangedContents(): + if log_macro.search(line) or log_if_macro.search(line): + log_info.append(f.LocalPath()) + if printf_macro.search(line) or fprintf_macro.search(line): + printf.append(f.LocalPath()) + + if log_info: + return [output_api.PresubmitError( + 'These files spam the console log with LOG(INFO):', + items=log_info)] + if printf: + return [output_api.PresubmitError( + 'These files spam the console log with printf/fprintf:', + items=printf)] + return [] + + +def _CheckForAnonymousVariables(input_api, output_api): + """These types are all expected to hold locks while in scope and + so should never be anonymous (which causes them to be immediately + destroyed).""" + they_who_must_be_named = [ + 'base::AutoLock', + 'base::AutoReset', + 'base::AutoUnlock', + 'SkAutoAlphaRestore', + 'SkAutoBitmapShaderInstall', + 'SkAutoBlitterChoose', + 'SkAutoBounderCommit', + 'SkAutoCallProc', + 'SkAutoCanvasRestore', + 'SkAutoCommentBlock', + 'SkAutoDescriptor', + 'SkAutoDisableDirectionCheck', + 'SkAutoDisableOvalCheck', + 'SkAutoFree', + 'SkAutoGlyphCache', + 'SkAutoHDC', + 'SkAutoLockColors', + 'SkAutoLockPixels', + 'SkAutoMalloc', + 'SkAutoMaskFreeImage', + 'SkAutoMutexAcquire', + 'SkAutoPathBoundsUpdate', + 'SkAutoPDFRelease', + 'SkAutoRasterClipValidate', + 'SkAutoRef', + 'SkAutoTime', + 'SkAutoTrace', + 'SkAutoUnref', + ] + anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named) + # bad: base::AutoLock(lock.get()); + # not bad: base::AutoLock lock(lock.get()); + bad_pattern = input_api.re.compile(anonymous) + # good: new base::AutoLock(lock.get()) + good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous) + errors = [] + + for f in input_api.AffectedFiles(): + if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')): + continue + for linenum, line in f.ChangedContents(): + if bad_pattern.search(line) and not good_pattern.search(line): + errors.append('%s:%d' % (f.LocalPath(), linenum)) + + if errors: + return [output_api.PresubmitError( + 'These lines create anonymous variables that need to be named:', + items=errors)] + return [] + + +def _GetJSONParseError(input_api, filename): + try: + contents = input_api.ReadFile(filename) + input_api.json.loads(contents) + except ValueError as e: + return e + return None + + +def _CheckParseErrors(input_api, output_api): + """Check that JSON files do not contain syntax errors.""" + actions = { + '.json': _GetJSONParseError, + } + # These paths contain test data and other known invalid JSON files. + excluded_patterns = [ + r'test/data/', + ] + # Most JSON files are preprocessed and support comments, but these do not. + json_no_comments_patterns = [ + r'^testing/', + ] + + def get_action(affected_file): + filename = affected_file.LocalPath() + return actions.get(input_api.os_path.splitext(filename)[1]) + + def MatchesFile(patterns, path): + for pattern in patterns: + if input_api.re.search(pattern, path): + return True + return False + + def FilterFile(affected_file): + action = get_action(affected_file) + if not action: + return False + path = affected_file.LocalPath() + + if MatchesFile(excluded_patterns, path): + return False + return True + + results = [] + for affected_file in input_api.AffectedFiles( + file_filter=FilterFile, include_deletes=False): + action = get_action(affected_file) + parse_error = action(input_api, affected_file.AbsoluteLocalPath()) + if parse_error: + results.append(output_api.PresubmitError('%s could not be parsed: %s' % + (affected_file.LocalPath(), parse_error))) + return results + + +def _CheckJavaStyle(input_api, output_api): + """Runs checkstyle on changed java files and returns errors if any exist.""" + import sys + original_sys_path = sys.path + try: + sys.path = sys.path + [input_api.os_path.join( + input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')] + import checkstyle + finally: + # Restore sys.path to what it was before. + sys.path = original_sys_path + + return checkstyle.RunCheckstyle( + input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml') + + +_DEPRECATED_CSS = [ + # Values + ( "-webkit-box", "flex" ), + ( "-webkit-inline-box", "inline-flex" ), + ( "-webkit-flex", "flex" ), + ( "-webkit-inline-flex", "inline-flex" ), + ( "-webkit-min-content", "min-content" ), + ( "-webkit-max-content", "max-content" ), + + # Properties + ( "-webkit-background-clip", "background-clip" ), + ( "-webkit-background-origin", "background-origin" ), + ( "-webkit-background-size", "background-size" ), + ( "-webkit-box-shadow", "box-shadow" ), + + # Functions + ( "-webkit-gradient", "gradient" ), + ( "-webkit-repeating-gradient", "repeating-gradient" ), + ( "-webkit-linear-gradient", "linear-gradient" ), + ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ), + ( "-webkit-radial-gradient", "radial-gradient" ), + ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ), +] + +def _CheckNoDeprecatedCSS(input_api, output_api): + """ Make sure that we don't use deprecated CSS + properties, functions or values. Our external + documentation is ignored by the hooks as it + needs to be consumed by WebKit. """ + results = [] + file_inclusion_pattern = (r".+\.css$") + black_list = (_EXCLUDED_PATHS + + _TEST_CODE_EXCLUDED_PATHS + + input_api.DEFAULT_BLACK_LIST + + (r"^chrome/common/extensions/docs", + r"^chrome/docs", + r"^native_client_sdk")) + file_filter = lambda f: input_api.FilterSourceFile( + f, white_list=file_inclusion_pattern, black_list=black_list) + for fpath in input_api.AffectedFiles(file_filter=file_filter): + for line_num, line in fpath.ChangedContents(): + for (deprecated_value, value) in _DEPRECATED_CSS: + if input_api.re.search(deprecated_value, line): + results.append(output_api.PresubmitError( + "%s:%d: Use of deprecated CSS %s, use %s instead" % + (fpath.LocalPath(), line_num, deprecated_value, value))) + return results + + +def _CheckForOverrideAndFinalRules(input_api, output_api): + """Checks for final and override used as per C++11""" + problems = [] + for f in input_api.AffectedFiles(): + if (f.LocalPath().endswith(('.cc', '.cpp', '.h', '.mm'))): + for line_num, line in f.ChangedContents(): + if (input_api.re.search(r'\b(FINAL|OVERRIDE)\b', line)): + problems.append(' %s:%d' % (f.LocalPath(), line_num)) + + if not problems: + return [] + return [output_api.PresubmitError('Use C++11\'s |final| and |override| ' + 'rather than FINAL and OVERRIDE.', + problems)] + + +def _CommonChecks(input_api, output_api): + """Checks common to both upload and commit.""" + results = [] + results.extend(input_api.canned_checks.PanProjectChecks( + input_api, output_api, excluded_paths=_EXCLUDED_PATHS + _SKY_PATHS)) + results.extend(_CheckAuthorizedAuthor(input_api, output_api)) + results.extend( + _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api)) + results.extend(_CheckNoIOStreamInHeaders(input_api, output_api)) + results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api)) + results.extend(_CheckNoNewWStrings(input_api, output_api)) + results.extend(_CheckNoDEPSGIT(input_api, output_api)) + results.extend(_CheckNoBannedFunctions(input_api, output_api)) + results.extend(_CheckNoPragmaOnce(input_api, output_api)) + results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api)) + results.extend(_CheckFilePermissions(input_api, output_api)) + results.extend(_CheckIncludeOrder(input_api, output_api)) + results.extend(_CheckForVersionControlConflicts(input_api, output_api)) + results.extend(_CheckPatchFiles(input_api, output_api)) + results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api)) + results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api)) + results.extend(_CheckForInvalidOSMacros(input_api, output_api)) + results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api)) + # TODO(danakj): Remove this when base/move.h is removed. + results.extend(_CheckForUsingSideEffectsOfPass(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckChangeHasNoTabs( + input_api, + output_api, + source_file_filter=lambda x: x.LocalPath().endswith('.grd'))) + results.extend(_CheckSpamLogging(input_api, output_api)) + results.extend(_CheckForAnonymousVariables(input_api, output_api)) + results.extend(_CheckNoDeprecatedCSS(input_api, output_api)) + results.extend(_CheckParseErrors(input_api, output_api)) + results.extend(_CheckForOverrideAndFinalRules(input_api, output_api)) + + if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()): + results.extend(input_api.canned_checks.RunUnitTestsInDirectory( + input_api, output_api, + input_api.PresubmitLocalPath(), + whitelist=[r'^PRESUBMIT_test\.py$'])) + return results + + +def _CheckAuthorizedAuthor(input_api, output_api): + """For non-googler/chromites committers, verify the author's email address is + in AUTHORS. + """ + # TODO(maruel): Add it to input_api? + import fnmatch + + author = input_api.change.author_email + if not author: + input_api.logging.info('No author, skipping AUTHOR check') + return [] + authors_path = input_api.os_path.join( + input_api.PresubmitLocalPath(), 'AUTHORS') + valid_authors = ( + input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line) + for line in open(authors_path)) + valid_authors = [item.group(1).lower() for item in valid_authors if item] + if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors): + input_api.logging.info('Valid authors are %s', ', '.join(valid_authors)) + return [output_api.PresubmitPromptWarning( + ('%s is not in AUTHORS file. If you are a new contributor, please visit' + '\n' + 'http://www.chromium.org/developers/contributing-code and read the ' + '"Legal" section\n' + 'If you are a chromite, verify the contributor signed the CLA.') % + author)] + return [] + + +def _CheckPatchFiles(input_api, output_api): + problems = [f.LocalPath() for f in input_api.AffectedFiles() + if f.LocalPath().endswith(('.orig', '.rej'))] + if problems: + return [output_api.PresubmitError( + "Don't commit .rej and .orig files.", problems)] + else: + return [] + + +def _DidYouMeanOSMacro(bad_macro): + try: + return {'A': 'OS_ANDROID', + 'B': 'OS_BSD', + 'C': 'OS_CHROMEOS', + 'F': 'OS_FREEBSD', + 'L': 'OS_LINUX', + 'M': 'OS_MACOSX', + 'N': 'OS_NACL', + 'O': 'OS_OPENBSD', + 'P': 'OS_POSIX', + 'S': 'OS_SOLARIS', + 'W': 'OS_WIN'}[bad_macro[3].upper()] + except KeyError: + return '' + + +def _CheckForInvalidOSMacrosInFile(input_api, f): + """Check for sensible looking, totally invalid OS macros.""" + preprocessor_statement = input_api.re.compile(r'^\s*#') + os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)') + results = [] + for lnum, line in f.ChangedContents(): + if preprocessor_statement.search(line): + for match in os_macro.finditer(line): + if not match.group(1) in _VALID_OS_MACROS: + good = _DidYouMeanOSMacro(match.group(1)) + did_you_mean = ' (did you mean %s?)' % good if good else '' + results.append(' %s:%d %s%s' % (f.LocalPath(), + lnum, + match.group(1), + did_you_mean)) + return results + + +def _CheckForInvalidOSMacros(input_api, output_api): + """Check all affected files for invalid OS macros.""" + bad_macros = [] + for f in input_api.AffectedFiles(): + if not f.LocalPath().endswith(('.py', '.js', '.html', '.css')): + bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f)) + + if not bad_macros: + return [] + + return [output_api.PresubmitError( + 'Possibly invalid OS macro[s] found. Please fix your code\n' + 'or add your macro to src/PRESUBMIT.py.', bad_macros)] + + +def _CheckForInvalidIfDefinedMacrosInFile(input_api, f): + """Check all affected files for invalid "if defined" macros.""" + ALWAYS_DEFINED_MACROS = ( + "TARGET_CPU_PPC", + "TARGET_CPU_PPC64", + "TARGET_CPU_68K", + "TARGET_CPU_X86", + "TARGET_CPU_ARM", + "TARGET_CPU_MIPS", + "TARGET_CPU_SPARC", + "TARGET_CPU_ALPHA", + "TARGET_IPHONE_SIMULATOR", + "TARGET_OS_EMBEDDED", + "TARGET_OS_IPHONE", + "TARGET_OS_MAC", + "TARGET_OS_UNIX", + "TARGET_OS_WIN32", + ) + ifdef_macro = input_api.re.compile(r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)') + results = [] + for lnum, line in f.ChangedContents(): + for match in ifdef_macro.finditer(line): + if match.group(1) in ALWAYS_DEFINED_MACROS: + always_defined = ' %s is always defined. ' % match.group(1) + did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1) + results.append(' %s:%d %s\n\t%s' % (f.LocalPath(), + lnum, + always_defined, + did_you_mean)) + return results + + +def _CheckForInvalidIfDefinedMacros(input_api, output_api): + """Check all affected files for invalid "if defined" macros.""" + bad_macros = [] + for f in input_api.AffectedFiles(): + if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')): + bad_macros.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api, f)) + + if not bad_macros: + return [] + + return [output_api.PresubmitError( + 'Found ifdef check on always-defined macro[s]. Please fix your code\n' + 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.', + bad_macros)] + + +def _CheckForUsingSideEffectsOfPass(input_api, output_api): + """Check all affected files for using side effects of Pass.""" + errors = [] + for f in input_api.AffectedFiles(): + if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')): + for lnum, line in f.ChangedContents(): + # Disallow Foo(*my_scoped_thing.Pass()); See crbug.com/418297. + if input_api.re.search(r'\*[a-zA-Z0-9_]+\.Pass\(\)', line): + errors.append(output_api.PresubmitError( + ('%s:%d uses *foo.Pass() to delete the contents of scoped_ptr. ' + + 'See crbug.com/418297.') % (f.LocalPath(), lnum))) + return errors + + +def CheckChangeOnUpload(input_api, output_api): + results = [] + results.extend(_CommonChecks(input_api, output_api)) + results.extend(_CheckValidHostsInDEPS(input_api, output_api)) + results.extend(_CheckJavaStyle(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckGNFormatted(input_api, output_api)) + return results + + +def GetDefaultTryConfigs(bots=None): + """Returns a list of ('bot', set(['tests']), optionally filtered by [bots]. + + If 'bots' is specified, will only return configurations for bots in that list. + """ + + builders_and_tests = { + 'Mojo Android Builder (dbg) Try': ['defaulttests'], + 'Mojo Android Builder Try': ['defaulttests'], + 'Mojo Android Builder Tests (dbg) Try': ['defaulttests'], + 'Mojo Linux (dbg) Try': ['defaulttests'], + 'Mojo Linux ASan Try': ['defaulttests'], + 'Mojo Linux Try': ['defaulttests'], + } + + if bots: + filtered_builders_and_tests = dict((bot, set(builders_and_tests[bot])) + for bot in bots) + else: + filtered_builders_and_tests = dict( + (bot, set(tests)) + for bot, tests in builders_and_tests.iteritems()) + + # Build up the mapping from tryserver master to bot/test. + out = dict() + for bot, tests in filtered_builders_and_tests.iteritems(): + out.setdefault("tryserver.client.mojo", {})[bot] = tests + return out + + +def CheckChangeOnCommit(input_api, output_api): + results = [] + results.extend(_CommonChecks(input_api, output_api)) + results.extend(input_api.canned_checks.CheckChangeHasBugField( + input_api, output_api)) + results.extend(input_api.canned_checks.CheckChangeHasDescription( + input_api, output_api)) + return results + + +def GetPreferredTryMasters(project, change): + import re + files = change.LocalPaths() + + if not files: + return {} + + builders = [ + 'Mojo Android Builder (dbg) Try', + 'Mojo Android Builder Try', + 'Mojo Android Builder Tests (dbg) Try', + 'Mojo Linux (dbg) Try', + 'Mojo Linux ASan Try', + 'Mojo Linux Try', + ] + + return GetDefaultTryConfigs(builders) + +def PostUploadHook(cl, change, output_api): + import subprocess + subprocess.check_call(["git", "cl", "try"]) + return [] diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py new file mode 100644 index 000000000..9db52e79b --- /dev/null +++ b/PRESUBMIT_test.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import re +import unittest + +import PRESUBMIT +from PRESUBMIT_test_mocks import MockChange, MockFile +from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi + + +_TEST_DATA_DIR = 'base/test/data/presubmit' + + +class IncludeOrderTest(unittest.TestCase): + def testSystemHeaderOrder(self): + scope = [(1, '#include '), + (2, '#include '), + (3, '#include "acustom.h"')] + all_linenums = [linenum for (linenum, _) in scope] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', all_linenums) + self.assertEqual(0, len(warnings)) + + def testSystemHeaderOrderMismatch1(self): + scope = [(10, '#include '), + (20, '#include '), + (30, '#include "acustom.h"')] + all_linenums = [linenum for (linenum, _) in scope] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', all_linenums) + self.assertEqual(1, len(warnings)) + self.assertTrue('20' in warnings[0]) + + def testSystemHeaderOrderMismatch2(self): + scope = [(10, '#include '), + (20, '#include "acustom.h"'), + (30, '#include ')] + all_linenums = [linenum for (linenum, _) in scope] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', all_linenums) + self.assertEqual(1, len(warnings)) + self.assertTrue('30' in warnings[0]) + + def testSystemHeaderOrderMismatch3(self): + scope = [(10, '#include "acustom.h"'), + (20, '#include '), + (30, '#include ')] + all_linenums = [linenum for (linenum, _) in scope] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', all_linenums) + self.assertEqual(2, len(warnings)) + self.assertTrue('20' in warnings[0]) + self.assertTrue('30' in warnings[1]) + + def testAlphabeticalOrderMismatch(self): + scope = [(10, '#include '), + (15, '#include '), + (20, '#include '), + (25, '#include '), + (30, '#include "bcustom.h"'), + (35, '#include "acustom.h"')] + all_linenums = [linenum for (linenum, _) in scope] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', all_linenums) + self.assertEqual(3, len(warnings)) + self.assertTrue('15' in warnings[0]) + self.assertTrue('25' in warnings[1]) + self.assertTrue('35' in warnings[2]) + + def testSpecialFirstInclude1(self): + mock_input_api = MockInputApi() + contents = ['#include "some/path/foo.h"', + '#include "a/header.h"'] + mock_file = MockFile('some/path/foo.cc', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + def testSpecialFirstInclude2(self): + mock_input_api = MockInputApi() + contents = ['#include "some/other/path/foo.h"', + '#include "a/header.h"'] + mock_file = MockFile('some/path/foo.cc', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + def testSpecialFirstInclude3(self): + mock_input_api = MockInputApi() + contents = ['#include "some/path/foo.h"', + '#include "a/header.h"'] + mock_file = MockFile('some/path/foo_platform.cc', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + def testSpecialFirstInclude4(self): + mock_input_api = MockInputApi() + contents = ['#include "some/path/bar.h"', + '#include "a/header.h"'] + mock_file = MockFile('some/path/foo_platform.cc', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(1, len(warnings)) + self.assertTrue('2' in warnings[0]) + + def testSpecialFirstInclude5(self): + mock_input_api = MockInputApi() + contents = ['#include "some/other/path/foo.h"', + '#include "a/header.h"'] + mock_file = MockFile('some/path/foo-suffix.h', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + def testSpecialFirstInclude6(self): + mock_input_api = MockInputApi() + contents = ['#include "some/other/path/foo_win.h"', + '#include ', + '#include "a/header.h"'] + mock_file = MockFile('some/path/foo_unittest_win.h', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + def testOrderAlreadyWrong(self): + scope = [(1, '#include "b.h"'), + (2, '#include "a.h"'), + (3, '#include "c.h"')] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', [3]) + self.assertEqual(0, len(warnings)) + + def testConflictAdded1(self): + scope = [(1, '#include "a.h"'), + (2, '#include "c.h"'), + (3, '#include "b.h"')] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', [2]) + self.assertEqual(1, len(warnings)) + self.assertTrue('3' in warnings[0]) + + def testConflictAdded2(self): + scope = [(1, '#include "c.h"'), + (2, '#include "b.h"'), + (3, '#include "d.h"')] + mock_input_api = MockInputApi() + warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api, + '', [2]) + self.assertEqual(1, len(warnings)) + self.assertTrue('2' in warnings[0]) + + def testIfElifElseEndif(self): + mock_input_api = MockInputApi() + contents = ['#include "e.h"', + '#define foo', + '#include "f.h"', + '#undef foo', + '#include "e.h"', + '#if foo', + '#include "d.h"', + '#elif bar', + '#include "c.h"', + '#else', + '#include "b.h"', + '#endif', + '#include "a.h"'] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + def testExcludedIncludes(self): + # #include 's can appear in any order. + mock_input_api = MockInputApi() + contents = ['#include ', + '#include '] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + contents = ['#include ', + '#include '] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + contents = ['#include "build/build_config.h"', + '#include "aaa.h"'] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(0, len(warnings)) + + def testCheckOnlyCFiles(self): + mock_input_api = MockInputApi() + mock_output_api = MockOutputApi() + contents = ['#include ', + '#include '] + mock_file_cc = MockFile('something.cc', contents) + mock_file_h = MockFile('something.h', contents) + mock_file_other = MockFile('something.py', contents) + mock_input_api.files = [mock_file_cc, mock_file_h, mock_file_other] + warnings = PRESUBMIT._CheckIncludeOrder(mock_input_api, mock_output_api) + self.assertEqual(1, len(warnings)) + self.assertEqual(2, len(warnings[0].items)) + self.assertEqual('promptOrNotify', warnings[0].type) + + def testUncheckableIncludes(self): + mock_input_api = MockInputApi() + contents = ['#include ', + '#include "b.h"', + '#include "a.h"'] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(1, len(warnings)) + + contents = ['#include "gpu/command_buffer/gles_autogen.h"', + '#include "b.h"', + '#include "a.h"'] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(1, len(warnings)) + + contents = ['#include "gl_mock_autogen.h"', + '#include "b.h"', + '#include "a.h"'] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(1, len(warnings)) + + contents = ['#include "ipc/some_macros.h"', + '#include "b.h"', + '#include "a.h"'] + mock_file = MockFile('', contents) + warnings = PRESUBMIT._CheckIncludeOrderInFile( + mock_input_api, mock_file, range(1, len(contents) + 1)) + self.assertEqual(1, len(warnings)) + + +class VersionControlConflictsTest(unittest.TestCase): + def testTypicalConflict(self): + lines = ['<<<<<<< HEAD', + ' base::ScopedTempDir temp_dir_;', + '=======', + ' ScopedTempDir temp_dir_;', + '>>>>>>> master'] + errors = PRESUBMIT._CheckForVersionControlConflictsInFile( + MockInputApi(), MockFile('some/path/foo_platform.cc', lines)) + self.assertEqual(3, len(errors)) + self.assertTrue('1' in errors[0]) + self.assertTrue('3' in errors[1]) + self.assertTrue('5' in errors[2]) + + +class BadExtensionsTest(unittest.TestCase): + def testBadRejFile(self): + mock_input_api = MockInputApi() + mock_input_api.files = [ + MockFile('some/path/foo.cc', ''), + MockFile('some/path/foo.cc.rej', ''), + MockFile('some/path2/bar.h.rej', ''), + ] + + results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi()) + self.assertEqual(1, len(results)) + self.assertEqual(2, len(results[0].items)) + self.assertTrue('foo.cc.rej' in results[0].items[0]) + self.assertTrue('bar.h.rej' in results[0].items[1]) + + def testBadOrigFile(self): + mock_input_api = MockInputApi() + mock_input_api.files = [ + MockFile('other/path/qux.h.orig', ''), + MockFile('other/path/qux.h', ''), + MockFile('other/path/qux.cc', ''), + ] + + results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi()) + self.assertEqual(1, len(results)) + self.assertEqual(1, len(results[0].items)) + self.assertTrue('qux.h.orig' in results[0].items[0]) + + def testGoodFiles(self): + mock_input_api = MockInputApi() + mock_input_api.files = [ + MockFile('other/path/qux.h', ''), + MockFile('other/path/qux.cc', ''), + ] + results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi()) + self.assertEqual(0, len(results)) + + def testNoFiles(self): + mock_change = MockChange([]) + results = PRESUBMIT.GetPreferredTryMasters(None, mock_change) + self.assertEqual({}, results) + + +class InvalidOSMacroNamesTest(unittest.TestCase): + def testInvalidOSMacroNames(self): + lines = ['#if defined(OS_WINDOWS)', + ' #elif defined(OS_WINDOW)', + ' # if defined(OS_MACOSX) || defined(OS_CHROME)', + '# else // defined(OS_MAC)', + '#endif // defined(OS_MACOS)'] + errors = PRESUBMIT._CheckForInvalidOSMacrosInFile( + MockInputApi(), MockFile('some/path/foo_platform.cc', lines)) + self.assertEqual(len(lines), len(errors)) + self.assertTrue(':1 OS_WINDOWS' in errors[0]) + self.assertTrue('(did you mean OS_WIN?)' in errors[0]) + + def testValidOSMacroNames(self): + lines = ['#if defined(%s)' % m for m in PRESUBMIT._VALID_OS_MACROS] + errors = PRESUBMIT._CheckForInvalidOSMacrosInFile( + MockInputApi(), MockFile('some/path/foo_platform.cc', lines)) + self.assertEqual(0, len(errors)) + + +class InvalidIfDefinedMacroNamesTest(unittest.TestCase): + def testInvalidIfDefinedMacroNames(self): + lines = ['#if defined(TARGET_IPHONE_SIMULATOR)', + '#if !defined(TARGET_IPHONE_SIMULATOR)', + '#elif defined(TARGET_IPHONE_SIMULATOR)', + '#ifdef TARGET_IPHONE_SIMULATOR', + ' # ifdef TARGET_IPHONE_SIMULATOR', + '# if defined(VALID) || defined(TARGET_IPHONE_SIMULATOR)', + '# else // defined(TARGET_IPHONE_SIMULATOR)', + '#endif // defined(TARGET_IPHONE_SIMULATOR)',] + errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile( + MockInputApi(), MockFile('some/path/source.mm', lines)) + self.assertEqual(len(lines), len(errors)) + + def testValidIfDefinedMacroNames(self): + lines = ['#if defined(FOO)', + '#ifdef BAR',] + errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile( + MockInputApi(), MockFile('some/path/source.cc', lines)) + self.assertEqual(0, len(errors)) + + +if __name__ == '__main__': + unittest.main() diff --git a/PRESUBMIT_test_mocks.py b/PRESUBMIT_test_mocks.py new file mode 100644 index 000000000..f99671daf --- /dev/null +++ b/PRESUBMIT_test_mocks.py @@ -0,0 +1,109 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import json +import os +import re +import subprocess +import sys + + +class MockInputApi(object): + """Mock class for the InputApi class. + + This class can be used for unittests for presubmit by initializing the files + attribute as the list of changed files. + """ + + def __init__(self): + self.json = json + self.re = re + self.os_path = os.path + self.python_executable = sys.executable + self.subprocess = subprocess + self.files = [] + self.is_committing = False + + def AffectedFiles(self, file_filter=None): + return self.files + + def PresubmitLocalPath(self): + return os.path.dirname(__file__) + + def ReadFile(self, filename, mode='rU'): + for file_ in self.files: + if file_.LocalPath() == filename: + return '\n'.join(file_.NewContents()) + # Otherwise, file is not in our mock API. + raise IOError, "No such file or directory: '%s'" % filename + + +class MockOutputApi(object): + """Mock class for the OutputApi class. + + An instance of this class can be passed to presubmit unittests for outputing + various types of results. + """ + + class PresubmitResult(object): + def __init__(self, message, items=None, long_text=''): + self.message = message + self.items = items + self.long_text = long_text + + class PresubmitError(PresubmitResult): + def __init__(self, message, items, long_text=''): + MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) + self.type = 'error' + + class PresubmitPromptWarning(PresubmitResult): + def __init__(self, message, items, long_text=''): + MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) + self.type = 'warning' + + class PresubmitNotifyResult(PresubmitResult): + def __init__(self, message, items, long_text=''): + MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) + self.type = 'notify' + + class PresubmitPromptOrNotify(PresubmitResult): + def __init__(self, message, items, long_text=''): + MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) + self.type = 'promptOrNotify' + + +class MockFile(object): + """Mock class for the File class. + + This class can be used to form the mock list of changed files in + MockInputApi for presubmit unittests. + """ + + def __init__(self, local_path, new_contents): + self._local_path = local_path + self._new_contents = new_contents + self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)] + + def ChangedContents(self): + return self._changed_contents + + def NewContents(self): + return self._new_contents + + def LocalPath(self): + return self._local_path + + +class MockChange(object): + """Mock class for Change class. + + This class can be used in presubmit unittests to mock the query of the + current change. + """ + + def __init__(self, changed_files): + self._changed_files = changed_files + + def LocalPaths(self): + return self._changed_files diff --git a/WATCHLISTS b/WATCHLISTS new file mode 100644 index 000000000..7230b062c --- /dev/null +++ b/WATCHLISTS @@ -0,0 +1,45 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Watchlist Rules +# Refer: http://dev.chromium.org/developers/contributing-code/watchlists + +# IMPORTANT: The regular expression filepath is tested against each path using +# re.search, so it is not usually necessary to add .*. + +{ + 'WATCHLIST_DEFINITIONS': { + 'mojo': { + 'filepath': 'mojo', + }, + 'services': { + 'filepath': 'services', + }, + 'shell': { + 'filepath': 'shell', + }, + 'sky': { + 'filepath': 'sky', + }, + }, + + 'WATCHLISTS': { + 'mojo': ['aa@chromium.org', + 'abarth@chromium.org', + 'ben+mojo@chromium.org', + 'darin@chromium.org', + 'qsr+mojo@chromium.org', + 'viettrungluu+watch@chromium.org', + 'gregsimon@chromium.org', + 'yzshen+watch@chromium.org'], + 'services': ['qsr+mojo@chromium.org', + 'yzshen+watch@chromium.org'], + 'shell': ['qsr+mojo@chromium.org', + 'yzshen+watch@chromium.org'], + 'sky': ['abarth@chromium.org', + 'gregsimon@chromium.org', + 'jackson@chromium.org', + 'qsr+mojo@chromium.org'], + }, +} diff --git a/codereview.settings b/codereview.settings new file mode 100644 index 000000000..1dc1b2a04 --- /dev/null +++ b/codereview.settings @@ -0,0 +1,6 @@ +# This file is used by gcl to get repository specific information. +CODE_REVIEW_SERVER: codereview.chromium.org +CC_LIST: mojo-reviews@chromium.org +VIEW_VC: https://chromium.googlesource.com/external/mojo/+/ +PROJECT: mojo +RUN_POST_UPLOAD_HOOK: True diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn new file mode 100644 index 000000000..261cb1fec --- /dev/null +++ b/third_party/BUILD.gn @@ -0,0 +1,43 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if (is_android) { + import("//build/config/android/config.gni") +} + +declare_args() { + # Uses system libjpeg. If true, overrides use_libjpeg_turbo. + use_system_libjpeg = false + + # Uses libjpeg_turbo as the jpeg implementation. Has no effect if + # use_system_libjpeg is set. + use_libjpeg_turbo = true +} + +config("system_libjpeg_config") { + defines = [ "USE_SYSTEM_LIBJPEG" ] +} + +config("libjpeg_turbo_config") { + defines = [ "USE_LIBJPEG_TURBO" ] +} + +# This is a meta target that forwards to the system's libjpeg, +# third_party/libjpeg, or third_party/libjpeg_turbo depending on the build args +# declared in this file. +group("jpeg") { + if (use_system_libjpeg) { + libs = [ "jpeg" ] + public_configs = [ ":system_libjpeg_config" ] + } else if (use_libjpeg_turbo) { + deps = [ + "//third_party/libjpeg_turbo:libjpeg", + ] + public_configs = [ ":libjpeg_turbo_config" ] + } else { + deps = [ + "//third_party/libjpeg:libjpeg", + ] + } +} -- GitLab