| OLD | NEW |
| 1 # Copyright (C) 2012 Google Inc. All rights reserved. | 1 # Copyright (C) 2012 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| 11 # in the documentation and/or other materials provided with the | 11 # in the documentation and/or other materials provided with the |
| 12 # distribution. | 12 # distribution. |
| 13 # * Neither the name of Google Inc. nor the names of its | 13 # * Neither the name of Google Inc. nor the names of its |
| 14 # contributors may be used to endorse or promote products derived from | 14 # contributors may be used to endorse or promote products derived from |
| 15 # this software without specific prior written permission. | 15 # this software without specific prior written permission. |
| 16 # | 16 # |
| 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | 28 |
| 29 import json |
| 29 import logging | 30 import logging |
| 30 import optparse | 31 import optparse |
| 31 import signal | 32 import signal |
| 32 import traceback | 33 import traceback |
| 33 | 34 |
| 34 from webkitpy.common.host import Host | 35 from webkitpy.common.host import Host |
| 35 from webkitpy.layout_tests.models import test_expectations | 36 from webkitpy.layout_tests.models import test_expectations |
| 36 from webkitpy.layout_tests.port import platform_options | 37 from webkitpy.layout_tests.port import platform_options |
| 37 | 38 |
| 38 | 39 |
| 39 # This mirrors what the shell normally does. | 40 # This mirrors what the shell normally does. |
| 40 INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128 | 41 INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128 |
| 41 | 42 |
| 42 # This is a randomly chosen exit code that can be tested against to | 43 # This is a randomly chosen exit code that can be tested against to |
| 43 # indicate that an unexpected exception occurred. | 44 # indicate that an unexpected exception occurred. |
| 44 EXCEPTIONAL_EXIT_STATUS = 254 | 45 EXCEPTIONAL_EXIT_STATUS = 254 |
| 45 | 46 |
| 46 _log = logging.getLogger(__name__) | 47 _log = logging.getLogger(__name__) |
| 47 | 48 |
| 48 | 49 |
| 49 def lint(host, options): | 50 def lint(host, options): |
| 50 ports_to_lint = [host.port_factory.get(name) for name in host.port_factory.a
ll_port_names(options.platform)] | 51 ports_to_lint = [host.port_factory.get(name) for name in host.port_factory.a
ll_port_names(options.platform)] |
| 51 files_linted = set() | 52 files_linted = set() |
| 52 lint_failed = False | |
| 53 | 53 |
| 54 failures = [] |
| 54 for port_to_lint in ports_to_lint: | 55 for port_to_lint in ports_to_lint: |
| 55 expectations_dict = port_to_lint.expectations_dict() | 56 expectations_dict = port_to_lint.expectations_dict() |
| 56 | 57 |
| 57 for expectations_file in expectations_dict.keys(): | 58 for expectations_file in expectations_dict.keys(): |
| 58 if expectations_file in files_linted: | 59 if expectations_file in files_linted: |
| 59 continue | 60 continue |
| 60 | 61 |
| 61 try: | 62 try: |
| 62 test_expectations.TestExpectations(port_to_lint, | 63 test_expectations.TestExpectations(port_to_lint, |
| 63 expectations_dict={expectations_file: expectations_dict[expe
ctations_file]}, | 64 expectations_dict={expectations_file: expectations_dict[expe
ctations_file]}, |
| 64 is_lint_mode=True) | 65 is_lint_mode=True) |
| 65 except test_expectations.ParseError as e: | 66 except test_expectations.ParseError as e: |
| 66 lint_failed = True | |
| 67 _log.error('') | 67 _log.error('') |
| 68 for warning in e.warnings: | 68 for warning in e.warnings: |
| 69 _log.error(warning) | 69 _log.error(warning) |
| 70 failures.append('%s: %s' % (expectations_file, warning)) |
| 70 _log.error('') | 71 _log.error('') |
| 71 files_linted.add(expectations_file) | 72 files_linted.add(expectations_file) |
| 72 return lint_failed | 73 return failures |
| 73 | 74 |
| 74 | 75 |
| 75 def check_virtual_test_suites(host, options): | 76 def check_virtual_test_suites(host, options): |
| 76 port = host.port_factory.get(options=options) | 77 port = host.port_factory.get(options=options) |
| 77 fs = host.filesystem | 78 fs = host.filesystem |
| 78 layout_tests_dir = port.layout_tests_dir() | 79 layout_tests_dir = port.layout_tests_dir() |
| 79 virtual_suites = port.virtual_test_suites() | 80 virtual_suites = port.virtual_test_suites() |
| 80 | 81 |
| 81 check_failed = False | 82 failures = [] |
| 82 for suite in virtual_suites: | 83 for suite in virtual_suites: |
| 83 comps = [layout_tests_dir] + suite.name.split('/') + ['README.txt'] | 84 comps = [layout_tests_dir] + suite.name.split('/') + ['README.txt'] |
| 84 path_to_readme = fs.join(*comps) | 85 path_to_readme = fs.join(*comps) |
| 85 if not fs.exists(path_to_readme): | 86 if not fs.exists(path_to_readme): |
| 86 _log.error('LayoutTests/%s/README.txt is missing (each virtual suite
must have one).' % suite.name) | 87 failure = 'LayoutTests/%s/README.txt is missing (each virtual suite
must have one).' % suite.name |
| 87 check_failed = True | 88 _log.error(failure) |
| 88 if check_failed: | 89 failures.append(failure) |
| 90 if failures: |
| 89 _log.error('') | 91 _log.error('') |
| 90 return check_failed | 92 return failures |
| 91 | 93 |
| 92 | 94 |
| 93 def set_up_logging(logging_stream): | 95 def set_up_logging(logging_stream): |
| 94 logger = logging.getLogger() | 96 logger = logging.getLogger() |
| 95 logger.setLevel(logging.INFO) | 97 logger.setLevel(logging.INFO) |
| 96 handler = logging.StreamHandler(logging_stream) | 98 handler = logging.StreamHandler(logging_stream) |
| 97 logger.addHandler(handler) | 99 logger.addHandler(handler) |
| 98 return (logger, handler) | 100 return (logger, handler) |
| 99 | 101 |
| 100 | 102 |
| 101 def tear_down_logging(logger, handler): | 103 def tear_down_logging(logger, handler): |
| 102 logger.removeHandler(handler) | 104 logger.removeHandler(handler) |
| 103 | 105 |
| 104 | 106 |
| 105 def run_checks(host, options, logging_stream): | 107 def run_checks(host, options, logging_stream): |
| 106 logger, handler = set_up_logging(logging_stream) | 108 logger, handler = set_up_logging(logging_stream) |
| 107 try: | 109 try: |
| 108 lint_failed = lint(host, options) | 110 failures = [] |
| 109 check_failed = check_virtual_test_suites(host, options) | 111 failures.extend(lint(host, options)) |
| 110 if lint_failed or check_failed: | 112 failures.extend(check_virtual_test_suites(host, options)) |
| 113 |
| 114 if options.json: |
| 115 with open(options.json, 'w') as f: |
| 116 json.dump(failures, f) |
| 117 |
| 118 if failures: |
| 111 _log.error('Lint failed.') | 119 _log.error('Lint failed.') |
| 112 return 1 | 120 return 1 |
| 113 else: | 121 else: |
| 114 _log.info('Lint succeeded.') | 122 _log.info('Lint succeeded.') |
| 115 return 0 | 123 return 0 |
| 116 finally: | 124 finally: |
| 117 logger.removeHandler(handler) | 125 logger.removeHandler(handler) |
| 118 | 126 |
| 119 | 127 |
| 120 def main(argv, _, stderr): | 128 def main(argv, _, stderr): |
| 121 parser = optparse.OptionParser(option_list=platform_options(use_globs=True)) | 129 parser = optparse.OptionParser(option_list=platform_options(use_globs=True)) |
| 130 parser.add_option('--json', help='Path to JSON output file') |
| 122 options, _ = parser.parse_args(argv) | 131 options, _ = parser.parse_args(argv) |
| 123 | 132 |
| 124 if options.platform and 'test' in options.platform: | 133 if options.platform and 'test' in options.platform: |
| 125 # It's a bit lame to import mocks into real code, but this allows the us
er | 134 # It's a bit lame to import mocks into real code, but this allows the us
er |
| 126 # to run tests against the test platform interactively, which is useful
for | 135 # to run tests against the test platform interactively, which is useful
for |
| 127 # debugging test failures. | 136 # debugging test failures. |
| 128 from webkitpy.common.host_mock import MockHost | 137 from webkitpy.common.host_mock import MockHost |
| 129 host = MockHost() | 138 host = MockHost() |
| 130 else: | 139 else: |
| 131 host = Host() | 140 host = Host() |
| 132 | 141 |
| 133 try: | 142 try: |
| 134 exit_status = run_checks(host, options, stderr) | 143 exit_status = run_checks(host, options, stderr) |
| 135 except KeyboardInterrupt: | 144 except KeyboardInterrupt: |
| 136 exit_status = INTERRUPTED_EXIT_STATUS | 145 exit_status = INTERRUPTED_EXIT_STATUS |
| 137 except Exception as e: | 146 except Exception as e: |
| 138 print >> stderr, '\n%s raised: %s' % (e.__class__.__name__, str(e)) | 147 print >> stderr, '\n%s raised: %s' % (e.__class__.__name__, str(e)) |
| 139 traceback.print_exc(file=stderr) | 148 traceback.print_exc(file=stderr) |
| 140 exit_status = EXCEPTIONAL_EXIT_STATUS | 149 exit_status = EXCEPTIONAL_EXIT_STATUS |
| 141 | 150 |
| 142 return exit_status | 151 return exit_status |
| OLD | NEW |