Diff
Modified: trunk/Tools/ChangeLog (219656 => 219657)
--- trunk/Tools/ChangeLog 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/ChangeLog 2017-07-19 17:09:32 UTC (rev 219657)
@@ -1,3 +1,89 @@
+2017-07-19 Jonathan Bedard <[email protected]>
+
+ lint-test-expectations should be run during style checking
+ https://bugs.webkit.org/show_bug.cgi?id=173559
+ <rdar://problem/32854941>
+
+ Reviewed by David Kilzer.
+
+ Running the test expectation linter requires reading both files and lines not in the
+ patch because, for example, deletion of a test can cause a lint failure even though
+ no test expectations where modified. This means that the linter will occasionally warn
+ about lines which were not changed in a given patch but whose error is related to a
+ change made in that patch.
+
+ * Scripts/webkitpy/common/system/filesystem_mock.py:
+ (MockFileSystem.open_text_file_for_reading): Add 'errors' argument to mimic filesystem.
+ * Scripts/webkitpy/layout_tests/models/test_expectations.py:
+ (TestExpectationWarning): Added class to pass warnings as an object instead of a string.
+ (TestExpectationWarning.__init__): Construct warning with file name, line number, line
+ content, description of the error and the name of the associated test.
+ (TestExpectationWarning.__str__): Convert to string so that existing printing code works.
+ (TestExpectationParser.__init__): Pass shorten_filename function to
+ TestExpectationsParser to add a deleted file to the related_files dictionary.
+ (TestExpectationParser._check_test_exists): If the test does not exist, add the
+ missing path to the related_files dictionary.
+ (TestExpectationLine.__init__): Add related_files dictionary, which tracks
+ files and line numbers related to this test expectation line. This will allow
+ tracking linter errors in the style checker occurring across multiple files.
+ (TestExpectationsModel._already_seen_better_match): Add redundant expectation
+ lines to related_files dictionary.
+ (TestExpectations.__init__): Pass self._shorten_filename to TestExpectationParser.
+ (TestExpectations._report_warnings): Construct warning object instead of string
+ when adding to warnings list.
+ * Scripts/webkitpy/port/win.py: Changed logging level form warning to debug to
+ clean-up log.
+ * Scripts/webkitpy/style/checker.py:
+ (ProcessorBase.do_association_check): Add required function for processor classes.
+ (StyleProcessor):
+ (StyleProcessor.do_association_check): Run the TestExpectations linter when
+ checking for errors between associated files.
+ * Scripts/webkitpy/style/checkers/test_expectations.py:
+ (TestExpectationsChecker.check_test_expectations): Reflect changed import statements.
+ (TestExpectationsChecker):
+ (TestExpectationsChecker._should_log_linter_warning): Given a warning, a dictionary
+ of modified files, the current working directory and the host, determine if the linter
+ warning is associated with the changes.
+ (TestExpectationsChecker.lint_test_expectations): Lint test expectations for the
+ style checker.
+ * Scripts/webkitpy/style/filereader.py:
+ (TextFileReader):
+ (TextFileReader.__init__): Track modified files in dictionary instead of a counter.
+ (TextFileReader.file_count): Use dictionary to determine the number of modified files.
+ (TextFileReader.process_file): Track both the number of files changed and which lines
+ in those files were changed.
+ (TextFileReader.do_association_check): Run the processor's association check on all
+ modified or deleted files processed by TextFileReader.
+ (TextFileReader.delete_file): Track deleted files in _files dictionary.
+ (TextFileReader.count_delete_only_file): Deleted.
+ * Scripts/webkitpy/style/filereader_unittest.py:
+ (TextFileReaderTest.test_delete_file): Renamed function to reflect new function name.
+ (TextFileReaderTest.test_count_delete_only_file): Moved to test_delete_file.
+ * Scripts/webkitpy/style/main.py:
+ (CheckWebKitStyle.main): When running the style checker on a specific list of files,
+ explicitly run the association check on the file reader.
+ * Scripts/webkitpy/style/main_unittest.py:
+ (ExpectationLinterInStyleCheckerTest): Added to test the TestExpectationLinter now
+ embedded in the style checker.
+ (ExpectationLinterInStyleCheckerTest.setUp): Set up the style checker configuration.
+ (ExpectationLinterInStyleCheckerTest._generate_file_reader): Given a filesystem object,
+ construct the TextFileReader object with a StyleProcessor used to run style checks on
+ specific files.
+ (ExpectationLinterInStyleCheckerTest._generate_testing_host): Generate a host used for
+ testing the test expectation linter inside the style checker. This host must contain a
+ mock file system with the basic structure of test expectations.
+ (ExpectationLinterInStyleCheckerTest.test_no_linter_errors):
+ (ExpectationLinterInStyleCheckerTest.test_linter_duplicate_line):
+ (ExpectationLinterInStyleCheckerTest.test_linter_duplicate_line_no_edit):
+ (ExpectationLinterInStyleCheckerTest.test_linter_deleted_file):
+ (ExpectationLinterInStyleCheckerTest.test_linter_deleted_file_no_edit):
+ * Scripts/webkitpy/style/patchreader.py:
+ (PatchReader.check): Specify which file was deleted, run the association check.
+ * Scripts/webkitpy/style/patchreader_unittest.py:
+ (PatchReaderTest.MockTextFileReader.delete_file): Renamed count_delete_only_file.
+ (PatchReaderTest.MockTextFileReader.do_association_check): Added.
+ (PatchReaderTest.MockTextFileReader.count_delete_only_file): Renamed delete_file.
+
2017-07-19 Yusuke Suzuki <[email protected]>
[WTF] Implement WTF::ThreadGroup
Modified: trunk/Tools/Scripts/webkitpy/common/system/filesystem_mock.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/common/system/filesystem_mock.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/common/system/filesystem_mock.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -332,7 +332,7 @@
self.files[path] = contents
self.written_files[path] = contents
- def open_text_file_for_reading(self, path):
+ def open_text_file_for_reading(self, path, errors='strict'):
if self.files[path] is None:
self._raise_not_found(path)
return ReadableTextFileObject(self, path, self.files[path])
Modified: trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -61,6 +61,20 @@
return 'ParseError(warnings=%s)' % self.warnings
+class TestExpectationWarning(object):
+ def __init__(self, filename, line_number, line, error, test=None):
+ self.filename = filename
+ self.line_number = line_number
+ self.line = line
+ self.error = error
+ self.test = test
+
+ self.related_files = {}
+
+ def __str__(self):
+ return '{}:{} {} {}'.format(self.filename, self.line_number, self.error, self.test if self.test else self.line)
+
+
class TestExpectationParser(object):
"""Provides parsing facilities for lines in the test_expectation.txt file."""
@@ -78,7 +92,7 @@
MISSING_BUG_WARNING = 'Test lacks BUG modifier.'
- def __init__(self, port, full_test_list, allow_rebaseline_modifier):
+ def __init__(self, port, full_test_list, allow_rebaseline_modifier, shorten_filename=str):
self._port = port
self._test_configuration_converter = TestConfigurationConverter(set(port.all_test_configurations()), port.configuration_specifier_macros())
if full_test_list is None:
@@ -86,6 +100,7 @@
else:
self._full_test_list = set(full_test_list)
self._allow_rebaseline_modifier = allow_rebaseline_modifier
+ self._shorten_filename = shorten_filename
def parse(self, filename, expectations_string):
expectation_lines = []
@@ -186,6 +201,8 @@
# time you update TestExpectations without syncing
# the LayoutTests directory
expectation_line.warnings.append('Path does not exist.')
+ expected_path = self._shorten_filename(self._port.abspath_for_test(expectation_line.name))
+ expectation_line.related_files[expected_path] = None
return False
return True
@@ -381,6 +398,7 @@
self.comment = None
self.matching_tests = []
self.warnings = []
+ self.related_files = {} # Dictionary of files to lines number in that file which may have caused the list of warnings.
self.not_applicable_to_current_platform = False
def is_invalid(self):
@@ -701,31 +719,38 @@
expectation_line.warnings.append('Duplicate or ambiguous entry lines %s:%d and %s:%d.' % (
self._shorten_filename(prev_expectation_line.filename), prev_expectation_line.line_number,
self._shorten_filename(expectation_line.filename), expectation_line.line_number))
- return True
- if prev_expectation_line.matching_configurations >= expectation_line.matching_configurations:
+ elif prev_expectation_line.matching_configurations >= expectation_line.matching_configurations:
expectation_line.warnings.append('More specific entry for %s on line %s:%d overrides line %s:%d.' % (expectation_line.name,
self._shorten_filename(prev_expectation_line.filename), prev_expectation_line.line_number,
self._shorten_filename(expectation_line.filename), expectation_line.line_number))
# FIXME: return False if we want more specific to win.
- return True
- if prev_expectation_line.matching_configurations <= expectation_line.matching_configurations:
+ elif prev_expectation_line.matching_configurations <= expectation_line.matching_configurations:
expectation_line.warnings.append('More specific entry for %s on line %s:%d overrides line %s:%d.' % (expectation_line.name,
self._shorten_filename(expectation_line.filename), expectation_line.line_number,
self._shorten_filename(prev_expectation_line.filename), prev_expectation_line.line_number))
- return True
- if prev_expectation_line.matching_configurations & expectation_line.matching_configurations:
+ elif prev_expectation_line.matching_configurations & expectation_line.matching_configurations:
expectation_line.warnings.append('Entries for %s on lines %s:%d and %s:%d match overlapping sets of configurations.' % (expectation_line.name,
self._shorten_filename(prev_expectation_line.filename), prev_expectation_line.line_number,
self._shorten_filename(expectation_line.filename), expectation_line.line_number))
- return True
- # Configuration sets are disjoint, then.
- return False
+ else:
+ # Configuration sets are disjoint.
+ return False
+ if expectation_line.related_files.get(self._shorten_filename(prev_expectation_line.filename), None) is None:
+ expectation_line.related_files[self._shorten_filename(prev_expectation_line.filename)] = []
+ expectation_line.related_files[self._shorten_filename(prev_expectation_line.filename)].append(prev_expectation_line.line_number)
+ if prev_expectation_line.related_files.get(self._shorten_filename(expectation_line.filename), None) is None:
+ prev_expectation_line.related_files[self._shorten_filename(expectation_line.filename)] = []
+ prev_expectation_line.related_files[self._shorten_filename(expectation_line.filename)].append(prev_expectation_line.line_number)
+
+ return True
+
+
class TestExpectations(object):
"""Test expectations consist of lines with specifications of what
to expect from layout test cases. The test cases can be directories
@@ -853,7 +878,7 @@
self._test_config = port.test_configuration()
self._is_lint_mode = expectations_to_lint is not None
self._model = TestExpectationsModel(self._shorten_filename)
- self._parser = TestExpectationParser(port, tests, self._is_lint_mode)
+ self._parser = TestExpectationParser(port, tests, self._is_lint_mode, self._shorten_filename)
self._port = port
self._skipped_tests_warnings = []
self._expectations = []
@@ -938,8 +963,14 @@
warnings = []
for expectation in self._expectations:
for warning in expectation.warnings:
- warnings.append('%s:%d %s %s' % (self._shorten_filename(expectation.filename), expectation.line_number,
- warning, expectation.name if expectation.expectations else expectation.original_string))
+ warning = TestExpectationWarning(
+ self._shorten_filename(expectation.filename),
+ expectation.line_number,
+ expectation.original_string,
+ warning,
+ expectation.name if expectation.expectations else None)
+ warning.related_files = expectation.related_files
+ warnings.append(warning)
if warnings:
self._has_warnings = True
Modified: trunk/Tools/Scripts/webkitpy/port/win.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/port/win.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/port/win.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -49,7 +49,7 @@
import _winreg
import win32com.client
except ImportError:
- _log.warn("Not running on native Windows.")
+ _log.debug("Not running on native Windows.")
class WinPort(ApplePort):
Modified: trunk/Tools/Scripts/webkitpy/style/checker.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/checker.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/checker.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -60,6 +60,7 @@
from filter import FilterConfiguration
from optparser import ArgumentParser
from optparser import DefaultCommandOptionValues
+from webkitpy.common.host import Host
from webkitpy.common.system.logutils import configure_logging as _configure_logging
from webkitpy.port.config import apple_additions
@@ -811,7 +812,23 @@
"""
raise NotImplementedError('Subclasses should implement.')
+ def do_association_check(self, files, cwd, host=Host()):
+ """It may be the case that changes in a file cause style errors to
+ occur elsewhere in the modified file or in other files (most notably,
+ this is the case for the test expectations linter). The association check
+ is designed to take find issues which require parsing of files outside
+ those provided to the processor.
+ Args:
+ files: A dictionary of file names to lists of lines modified in the provided
+ file. Files which have been removed will also be in the dictionary, they
+ will map to 'None'
+ cwd: Current working directory, assumed to be the root of the scm repository
+ host: Current host for testing
+ """
+ raise NotImplementedError('Subclasses should implement.')
+
+
class StyleProcessor(ProcessorBase):
"""A ProcessorBase for checking style.
@@ -918,3 +935,7 @@
_log.debug("Using class: " + checker.__class__.__name__)
checker.check(lines)
+
+ def do_association_check(self, files, cwd, host=Host()):
+ _log.debug("Running TestExpectations linter")
+ TestExpectationsChecker.lint_test_expectations(files, self._configuration, cwd, self._increment_error_count, host=host)
Modified: trunk/Tools/Scripts/webkitpy/style/checkers/test_expectations.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/checkers/test_expectations.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/checkers/test_expectations.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -36,7 +36,8 @@
from common import TabChecker
from webkitpy.common.host import Host
-from webkitpy.layout_tests.models.test_expectations import TestExpectationParser
+from webkitpy.layout_tests.models import test_expectations
+from webkitpy.style.error_handlers import DefaultStyleErrorHandler
_log = logging.getLogger(__name__)
@@ -78,7 +79,7 @@
pass
def check_test_expectations(self, expectations_str, tests=None):
- parser = TestExpectationParser(self._port_obj, tests, allow_rebaseline_modifier=False)
+ parser = test_expectations.TestExpectationParser(self._port_obj, tests, allow_rebaseline_modifier=False)
expectations = parser.parse('expectations', expectations_str)
level = 5
@@ -96,3 +97,52 @@
# Warn tabs in lines as well
self.check_tabs(lines)
+
+ @staticmethod
+ def _should_log_linter_warning(warning, files, cwd, host):
+ # Case 1, the line the warning was tied to is in our patch.
+ abs_filename = host.filesystem.join(cwd, warning.filename)
+ if abs_filename in files and (files[abs_filename] is None or warning.line_number in files[abs_filename]):
+ return True
+
+ for file, lines in warning.related_files.iteritems():
+ abs_filename = host.filesystem.join(cwd, file)
+ if abs_filename in files:
+ # Case 2, a file associated with the warning is in our patch
+ # Note that this will really only happen if you delete a test.
+ if lines is None:
+ return True
+
+ # Case 3, a line associated with the warning is in our patch.
+ for line in lines:
+ if line in files[abs_filename]:
+ return True
+ return False
+
+ @staticmethod
+ def lint_test_expectations(files, configuration, cwd, increment_error_count=lambda: 0, line_numbers=None, host=Host()):
+ error_count = 0
+ files_linted = set()
+ ports_to_lint = [host.port_factory.get(name) for name in host.port_factory.all_port_names()]
+ for port in ports_to_lint:
+ for expectations_file in port.expectations_dict().keys():
+ style_error_handler = DefaultStyleErrorHandler(
+ expectations_file,
+ configuration,
+ increment_error_count,
+ line_numbers)
+
+ try:
+ if expectations_file in files_linted:
+ continue
+ expectations = test_expectations.TestExpectations(
+ port,
+ expectations_to_lint={expectations_file: port.expectations_dict()[expectations_file]})
+ expectations.parse_all_expectations()
+ except test_expectations.ParseError as e:
+ for warning in e.warnings:
+ if TestExpectationsChecker._should_log_linter_warning(warning, files, cwd, host):
+ style_error_handler(warning.line_number, 'test/expectations', 5, warning.error)
+ error_count += 1
+ files_linted.add(expectations_file)
+ return error_count
Modified: trunk/Tools/Scripts/webkitpy/style/filereader.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/filereader.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/filereader.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -33,7 +33,9 @@
import logging
import sys
+from webkitpy.common.host import Host
+
_log = logging.getLogger(__name__)
@@ -62,9 +64,13 @@
self.filesystem = filesystem
self._processor = processor
- self.file_count = 0
+ self._files = {}
self.delete_only_file_count = 0
+ @property
+ def file_count(self):
+ return len(self._files) - self.delete_only_file_count
+
def _read_lines(self, file_path):
"""Read the file at a path, and return its lines.
@@ -103,7 +109,13 @@
SystemExit: If no file at file_path exists.
"""
- self.file_count += 1
+ abs_file_path = self.filesystem.abspath(file_path)
+ if abs_file_path not in self._files:
+ self._files[abs_file_path] = None
+ if 'line_numbers' in kwargs:
+ if self._files[abs_file_path] is None:
+ self._files[abs_file_path] = []
+ self._files[abs_file_path] = self._files[abs_file_path] + kwargs['line_numbers']
if not self.filesystem.exists(file_path) and file_path != "-":
_log.error("File does not exist: '%s'" % file_path)
@@ -135,7 +147,10 @@
else:
self.process_file(path)
- def count_delete_only_file(self):
+ def do_association_check(self, cwd, host=Host()):
+ self._processor.do_association_check(self._files, cwd, host=host)
+
+ def delete_file(self, file_path=None):
"""Count up files that contains only deleted lines.
Files which has no modified or newly-added lines don't need
@@ -142,4 +157,6 @@
to check style, but should be treated as checked. For that
purpose, we just count up the number of such files.
"""
+ if file_path:
+ self._files[self.filesystem.abspath(file_path)] = None
self.delete_only_file_count += 1
Modified: trunk/Tools/Scripts/webkitpy/style/filereader_unittest.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/filereader_unittest.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/filereader_unittest.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -150,8 +150,8 @@
(['foo'], file_path1, None)]
self._assert_file_reader(processed, 2)
- def test_count_delete_only_file(self):
- self._file_reader.count_delete_only_file()
+ def test_delete_file(self):
+ self._file_reader.delete_file()
delete_only_file_count = self._file_reader.delete_only_file_count
self.assertEqual(delete_only_file_count, 1)
Modified: trunk/Tools/Scripts/webkitpy/style/main.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/main.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/main.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -147,6 +147,7 @@
if paths and not options.diff_files:
file_reader.process_paths(paths)
+ file_reader.do_association_check(host.scm().checkout_root)
else:
changed_files = paths if options.diff_files else None
patch = host.scm().create_patch(options.git_commit, changed_files=changed_files, git_index=options.git_index)
Modified: trunk/Tools/Scripts/webkitpy/style/main_unittest.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/main_unittest.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/main_unittest.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -20,11 +20,18 @@
# 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.
+import optparse
import unittest
+import webkitpy.style.checker as checker
+
from main import change_directory
+from webkitpy.common.host import Host
from webkitpy.common.system.filesystem_mock import MockFileSystem
+from webkitpy.common.system.outputcapture import OutputCaptureScope
from webkitpy.common.system.logtesting import LogTesting
+from webkitpy.style.checker import StyleProcessor
+from webkitpy.style.filereader import TextFileReader
class ChangeDirectoryTest(unittest.TestCase):
@@ -73,3 +80,100 @@
"""]
self._assert_result(paths, paths, log_messages, self._original_directory)
+
+
+class ExpectationLinterInStyleCheckerTest(unittest.TestCase):
+
+ def setUp(self):
+ parser = checker.check_webkit_style_parser()
+ (_, options) = parser.parse([])
+ self._style_checker_configuration = checker.check_webkit_style_configuration(options)
+
+ def _generate_file_reader(self, file_system):
+ style_processor = StyleProcessor(self._style_checker_configuration)
+ return TextFileReader(file_system, style_processor)
+
+ def _generate_testing_host(self, files={}):
+ host = Host()
+ expectation_files = files
+
+ host.filesystem = MockFileSystem(dirs=['/mock-checkout/LayoutTests'])
+ options = optparse.Values()
+ setattr(options, 'layout_tests_dir', '/mock-checkout/LayoutTests')
+
+ all_ports = [host.port_factory.get(name, options=options) for name in host.port_factory.all_port_names()]
+ for port in all_ports:
+ for path in port.expectations_files():
+ if path not in expectation_files:
+ expectation_files[path] = '# Empty expectation file\n'
+
+ expectation_files['/mock-checkout/LayoutTests/css1/test.html'] = 'Test'
+ expectation_files['/mock-checkout/LayoutTests/css1/test-expected.txt'] = 'Test Expectation'
+ host.filesystem = MockFileSystem(files=expectation_files)
+ return host
+
+ def test_no_linter_errors(self):
+ host = self._generate_testing_host()
+
+ scope = OutputCaptureScope()
+ with scope:
+ file_reader = self._generate_file_reader(host.filesystem)
+ file_reader.do_association_check('/mock-checkout', host)
+ self.assertEqual(scope.captured_output, ('', '', ''))
+
+ def test_linter_duplicate_line(self):
+ files = {
+ '/mock-checkout/LayoutTests/TestExpectations':
+ '# TestExpectations\ncss1/test.html [ Failure ]\ncss1/test.html [ Failure ]\n'}
+ host = self._generate_testing_host(files)
+
+ scope = OutputCaptureScope()
+ with scope:
+ file_reader = self._generate_file_reader(host.filesystem)
+ file_reader.process_paths({'/mock-checkout/LayoutTests/TestExpectations'})
+ file_reader.do_association_check('/mock-checkout', host)
+ self.assertEqual(
+ scope.captured_output,
+ ('', '', '/mock-checkout/LayoutTests/TestExpectations:3: Duplicate or ambiguous entry lines LayoutTests/TestExpectations:2 and LayoutTests/TestExpectations:3. [test/expectations] [5]\n'))
+
+ def test_linter_duplicate_line_no_edit(self):
+ files = {
+ '/mock-checkout/LayoutTests/TestExpectations':
+ '# TestExpectations\ncss1/test.html [ Failure ]\ncss1/test.html [ Failure ]\n'}
+ host = self._generate_testing_host(files)
+
+ scope = OutputCaptureScope()
+ with scope:
+ file_reader = self._generate_file_reader(host.filesystem)
+ file_reader.process_paths(['/mock-checkout/LayoutTests/platform/ios/TestExpectations'])
+ file_reader.do_association_check('/mock-checkout', host)
+ self.assertEqual(scope.captured_output, ('', '', ''))
+
+ def test_linter_deleted_file(self):
+ files = {
+ '/mock-checkout/LayoutTests/TestExpectations':
+ '# TestExpectations\ncss1/deleted-test.html [ Failure ]\n'}
+ host = self._generate_testing_host(files)
+ host = self._generate_testing_host(files)
+
+ scope = OutputCaptureScope()
+ with scope:
+ file_reader = self._generate_file_reader(host.filesystem)
+ file_reader.delete_file('/mock-checkout/LayoutTests/css1/deleted-test.html')
+ file_reader.do_association_check('/mock-checkout', host)
+ self.assertEqual(
+ scope.captured_output,
+ ('', '', '/mock-checkout/LayoutTests/TestExpectations:2: Path does not exist. [test/expectations] [5]\n'))
+
+ def test_linter_deleted_file_no_edit(self):
+ files = {
+ '/mock-checkout/LayoutTests/TestExpectations':
+ '# TestExpectations\ncss1/deleted-test.html [ Failure ]\n'}
+ host = self._generate_testing_host(files)
+
+ scope = OutputCaptureScope()
+ with scope:
+ file_reader = self._generate_file_reader(host.filesystem)
+ file_reader.delete_file('/mock-checkout/LayoutTests/css1/other-deleted-test.html')
+ file_reader.do_association_check('/mock-checkout', host)
+ self.assertEqual(scope.captured_output, ('', '', ''))
Modified: trunk/Tools/Scripts/webkitpy/style/patchreader.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/patchreader.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/patchreader.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -77,7 +77,8 @@
# Don't check files which contain only deleted lines
# as they can never add style errors. However, mark them as
# processed so that we count up number of such files.
- self._text_file_reader.count_delete_only_file()
+ self._text_file_reader.delete_file(path)
continue
self._text_file_reader.process_file(file_path=path, line_numbers=line_numbers)
+ self._text_file_reader.do_association_check(fs.getcwd())
Modified: trunk/Tools/Scripts/webkitpy/style/patchreader_unittest.py (219656 => 219657)
--- trunk/Tools/Scripts/webkitpy/style/patchreader_unittest.py 2017-07-19 16:42:53 UTC (rev 219656)
+++ trunk/Tools/Scripts/webkitpy/style/patchreader_unittest.py 2017-07-19 17:09:32 UTC (rev 219657)
@@ -45,14 +45,17 @@
self.passed_to_process_file = []
"""A list of (file_path, line_numbers) pairs."""
self.delete_only_file_count = 0
- """A number of times count_delete_only_file() called"""
+ """A number of times delete_file() called"""
def process_file(self, file_path, line_numbers):
self.passed_to_process_file.append((file_path, line_numbers))
- def count_delete_only_file(self):
+ def delete_file(self, file_path=None):
self.delete_only_file_count += 1
+ def do_association_check(self, cwd):
+ pass
+
def setUp(self):
file_reader = self.MockTextFileReader()
self._file_reader = file_reader