| Differences between
and this patch
- a/WebKitTools/ChangeLog +16 lines
Lines 1-3 a/WebKitTools/ChangeLog_sec1
1
2010-11-15  Dirk Pranke  <dpranke@chromium.org>
2
3
        Reviewed by NOBODY (OOPS!).
4
5
        new-run-webkit-tests: rename TestInfo to TestInput, move image hash to work thread
6
7
        Rename the TestInfo class to TestInput to be clearer about its
8
        function, and move the checksum-reading code into dump_render_tree_thread
9
        to avoid cross-thread access.
10
11
        https://bugs.webkit.org/show_bug.cgi?id=49573
12
13
        * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
14
        * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
15
        * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py:
16
1
2010-11-15  Andras Becsi  <abecsi@webkit.org>
17
2010-11-15  Andras Becsi  <abecsi@webkit.org>
2
18
3
        Reviewed by Andreas Kling.
19
        Reviewed by Andreas Kling.
- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py -37 / +42 lines
Lines 74-80 def log_stack(stack): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec1
74
            _log.error('  %s' % line.strip())
74
            _log.error('  %s' % line.strip())
75
75
76
76
77
def _process_output(port, options, test_info, test_types, test_args,
77
def _process_output(port, options, test_input, test_types, test_args,
78
                    crash, timeout, test_run_time, actual_checksum,
78
                    crash, timeout, test_run_time, actual_checksum,
79
                    output, error):
79
                    output, error):
80
    """Receives the output from a DumpRenderTree process, subjects it to a
80
    """Receives the output from a DumpRenderTree process, subjects it to a
Lines 84-90 def _process_output(port, options, test_info, test_types, test_args, a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec2
84
      port: port-specific hooks
84
      port: port-specific hooks
85
      options: command line options argument from optparse
85
      options: command line options argument from optparse
86
      proc: an active DumpRenderTree process
86
      proc: an active DumpRenderTree process
87
      test_info: Object containing the test filename, uri and timeout
87
      test_input: Object containing the test filename, uri and timeout
88
      test_types: list of test types to subject the output to
88
      test_types: list of test types to subject the output to
89
      test_args: arguments to be passed to each test
89
      test_args: arguments to be passed to each test
90
90
Lines 104-115 def _process_output(port, options, test_info, test_types, test_args, a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec3
104
        failures.append(test_failures.FailureTimeout())
104
        failures.append(test_failures.FailureTimeout())
105
105
106
    if crash:
106
    if crash:
107
        _log.debug("Stacktrace for %s:\n%s" % (test_info.filename, error))
107
        _log.debug("Stacktrace for %s:\n%s" % (test_input.filename, error))
108
        # Strip off "file://" since RelativeTestFilename expects
108
        # Strip off "file://" since RelativeTestFilename expects
109
        # filesystem paths.
109
        # filesystem paths.
110
        filename = os.path.join(options.results_directory,
110
        filename = os.path.join(options.results_directory,
111
                                port.relative_test_filename(
111
                                port.relative_test_filename(
112
                                test_info.filename))
112
                                test_input.filename))
113
        filename = os.path.splitext(filename)[0] + "-stack.txt"
113
        filename = os.path.splitext(filename)[0] + "-stack.txt"
114
        port.maybe_make_directory(os.path.split(filename)[0])
114
        port.maybe_make_directory(os.path.split(filename)[0])
115
        with codecs.open(filename, "wb", "utf-8") as file:
115
        with codecs.open(filename, "wb", "utf-8") as file:
Lines 122-128 def _process_output(port, options, test_info, test_types, test_args, a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec4
122
    time_for_diffs = {}
122
    time_for_diffs = {}
123
    for test_type in test_types:
123
    for test_type in test_types:
124
        start_diff_time = time.time()
124
        start_diff_time = time.time()
125
        new_failures = test_type.compare_output(port, test_info.filename,
125
        new_failures = test_type.compare_output(port, test_input.filename,
126
                                                output, local_test_args,
126
                                                output, local_test_args,
127
                                                options.configuration)
127
                                                options.configuration)
128
        # Don't add any more failures if we already have a crash, so we don't
128
        # Don't add any more failures if we already have a crash, so we don't
Lines 134-140 def _process_output(port, options, test_info, test_types, test_args, a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec5
134
            time.time() - start_diff_time)
134
            time.time() - start_diff_time)
135
135
136
    total_time_for_all_diffs = time.time() - start_diff_time
136
    total_time_for_all_diffs = time.time() - start_diff_time
137
    return test_results.TestResult(test_info.filename, failures, test_run_time,
137
    return test_results.TestResult(test_input.filename, failures, test_run_time,
138
                                   total_time_for_all_diffs, time_for_diffs)
138
                                   total_time_for_all_diffs, time_for_diffs)
139
139
140
140
Lines 153-174 def _milliseconds_to_seconds(msecs): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec6
153
    return float(msecs) / 1000.0
153
    return float(msecs) / 1000.0
154
154
155
155
156
def _image_hash(test_info, test_args, options):
156
def _should_fetch_expected_checksum(options):
157
    """Returns the image hash of the test if it's needed, otherwise None."""
157
    return options.pixel_tests and not (options.new_baseline or options.reset_results)
158
    if (test_args.new_baseline or test_args.reset_results or not options.pixel_tests):
159
        return None
160
    return test_info.image_hash()
161
158
162
159
163
class SingleTestThread(threading.Thread):
160
class SingleTestThread(threading.Thread):
164
    """Thread wrapper for running a single test file."""
161
    """Thread wrapper for running a single test file."""
165
162
166
    def __init__(self, port, options, test_info, test_types, test_args):
163
    def __init__(self, port, options, test_input, test_types, test_args):
167
        """
164
        """
168
        Args:
165
        Args:
169
          port: object implementing port-specific hooks
166
          port: object implementing port-specific hooks
170
          options: command line argument object from optparse
167
          options: command line argument object from optparse
171
          test_info: Object containing the test filename, uri and timeout
168
          test_input: Object containing the test filename, uri and timeout
172
          test_types: A list of TestType objects to run the test output
169
          test_types: A list of TestType objects to run the test output
173
              against.
170
              against.
174
          test_args: A TestArguments object to pass to each TestType.
171
          test_args: A TestArguments object to pass to each TestType.
Lines 177-183 class SingleTestThread(threading.Thread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec7
177
        threading.Thread.__init__(self)
174
        threading.Thread.__init__(self)
178
        self._port = port
175
        self._port = port
179
        self._options = options
176
        self._options = options
180
        self._test_info = test_info
177
        self._test_input = test_input
181
        self._test_types = test_types
178
        self._test_types = test_types
182
        self._test_args = test_args
179
        self._test_args = test_args
183
        self._driver = None
180
        self._driver = None
Lines 188-205 class SingleTestThread(threading.Thread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec8
188
    def _covered_run(self):
185
    def _covered_run(self):
189
        # FIXME: this is a separate routine to work around a bug
186
        # FIXME: this is a separate routine to work around a bug
190
        # in coverage: see http://bitbucket.org/ned/coveragepy/issue/85.
187
        # in coverage: see http://bitbucket.org/ned/coveragepy/issue/85.
191
        test_info = self._test_info
188
189
        # FIXME: Pull this into TestShellThread._run().
190
        test_input = self._test_input
191
        test_input.uri = self._port.filename_to_uri(test_input.filename)
192
        if _should_fetch_expected_checksum(self._options):
193
            test_input.image_checksum = self._port.expected_checksum(test_input.filename)
194
195
        start = time.time()
192
        self._driver = self._port.create_driver(self._test_args.png_path,
196
        self._driver = self._port.create_driver(self._test_args.png_path,
193
                                                self._options)
197
                                                self._options)
194
        self._driver.start()
198
        self._driver.start()
195
        image_hash = _image_hash(test_info, self._test_args, self._options)
196
        start = time.time()
197
        crash, timeout, actual_checksum, output, error = \
199
        crash, timeout, actual_checksum, output, error = \
198
            self._driver.run_test(test_info.uri.strip(), test_info.timeout,
200
            self._driver.run_test(test_input.uri, test_input.timeout,
199
                                  image_hash)
201
                                  test_input.image_checksum)
200
        end = time.time()
202
        end = time.time()
201
        self._test_result = _process_output(self._port, self._options,
203
        self._test_result = _process_output(self._port, self._options,
202
            test_info, self._test_types, self._test_args,
204
            test_input, self._test_types, self._test_args,
203
            crash, timeout, end - start,
205
            crash, timeout, end - start,
204
            actual_checksum, output, error)
206
            actual_checksum, output, error)
205
        self._driver.stop()
207
        self._driver.stop()
Lines 258-264 class TestShellThread(WatchableThread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec9
258
          test_types: A list of TestType objects to run the test output
260
          test_types: A list of TestType objects to run the test output
259
              against.
261
              against.
260
          test_args: A TestArguments object to pass to each TestType.
262
          test_args: A TestArguments object to pass to each TestType.
261
262
        """
263
        """
263
        WatchableThread.__init__(self)
264
        WatchableThread.__init__(self)
264
        self._port = port
265
        self._port = port
Lines 402-418 class TestShellThread(WatchableThread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec10
402
                self._num_tests_in_current_group = len(self._filename_list)
403
                self._num_tests_in_current_group = len(self._filename_list)
403
                self._current_group_start_time = time.time()
404
                self._current_group_start_time = time.time()
404
405
405
            test_info = self._filename_list.pop()
406
            test_input = self._filename_list.pop()
406
407
407
            # We have a url, run tests.
408
            # We have a url, run tests.
408
            batch_count += 1
409
            batch_count += 1
409
            self._num_tests += 1
410
            self._num_tests += 1
410
            if self._options.run_singly:
411
            if self._options.run_singly:
411
                result = self._run_test_singly(test_info)
412
                result = self._run_test_singly(test_input)
412
            else:
413
            else:
413
                result = self._run_test(test_info)
414
                result = self._run_test(test_input)
414
415
415
            filename = test_info.filename
416
            filename = test_input.filename
416
            tests_run_file.write(filename + "\n")
417
            tests_run_file.write(filename + "\n")
417
            if result.failures:
418
            if result.failures:
418
                # Check and kill DumpRenderTree if we need to.
419
                # Check and kill DumpRenderTree if we need to.
Lines 440-446 class TestShellThread(WatchableThread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec11
440
            if test_runner:
441
            if test_runner:
441
                test_runner.update_summary(result_summary)
442
                test_runner.update_summary(result_summary)
442
443
443
    def _run_test_singly(self, test_info):
444
    def _run_test_singly(self, test_input):
444
        """Run a test in a separate thread, enforcing a hard time limit.
445
        """Run a test in a separate thread, enforcing a hard time limit.
445
446
446
        Since we can only detect the termination of a thread, not any internal
447
        Since we can only detect the termination of a thread, not any internal
Lines 448-454 class TestShellThread(WatchableThread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec12
448
        files singly.
449
        files singly.
449
450
450
        Args:
451
        Args:
451
          test_info: Object containing the test filename, uri and timeout
452
          test_input: Object containing the test filename, uri and timeout
452
453
453
        Returns:
454
        Returns:
454
          A TestResult
455
          A TestResult
Lines 456-469 class TestShellThread(WatchableThread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec13
456
        """
457
        """
457
        worker = SingleTestThread(self._port,
458
        worker = SingleTestThread(self._port,
458
                                  self._options,
459
                                  self._options,
459
                                  test_info,
460
                                  test_input,
460
                                  self._test_types,
461
                                  self._test_types,
461
                                  self._test_args)
462
                                  self._test_args)
462
463
463
        worker.start()
464
        worker.start()
464
465
465
        thread_timeout = _milliseconds_to_seconds(
466
        thread_timeout = _milliseconds_to_seconds(
466
            _pad_timeout(int(test_info.timeout)))
467
            _pad_timeout(int(test_input.timeout)))
467
        thread._next_timeout = time.time() + thread_timeout
468
        thread._next_timeout = time.time() + thread_timeout
468
        worker.join(thread_timeout)
469
        worker.join(thread_timeout)
469
        if worker.isAlive():
470
        if worker.isAlive():
Lines 485-501 class TestShellThread(WatchableThread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec14
485
            # This gets raised if the worker thread has already exited.
486
            # This gets raised if the worker thread has already exited.
486
            failures = []
487
            failures = []
487
            _log.error('Cannot get results of test: %s' %
488
            _log.error('Cannot get results of test: %s' %
488
                       test_info.filename)
489
                       test_input.filename)
489
            result = test_results.TestResult(test_info.filename, failures=[],
490
            result = test_results.TestResult(test_input.filename, failures=[],
490
                test_run_time=0, total_time_for_all_diffs=0, time_for_diffs=0)
491
                test_run_time=0, total_time_for_all_diffs=0, time_for_diffs=0)
491
492
492
        return result
493
        return result
493
494
494
    def _run_test(self, test_info):
495
    def _run_test(self, test_input):
495
        """Run a single test file using a shared DumpRenderTree process.
496
        """Run a single test file using a shared DumpRenderTree process.
496
497
497
        Args:
498
        Args:
498
          test_info: Object containing the test filename, uri and timeout
499
          test_input: Object containing the test filename, uri and timeout
499
500
500
        Returns: a TestResult object.
501
        Returns: a TestResult object.
501
        """
502
        """
Lines 504-522 class TestShellThread(WatchableThread): a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py_sec15
504
        # checksums match, so it should be set to a blank value if we
505
        # checksums match, so it should be set to a blank value if we
505
        # are generating a new baseline.  (Otherwise, an image from a
506
        # are generating a new baseline.  (Otherwise, an image from a
506
        # previous run will be copied into the baseline.)
507
        # previous run will be copied into the baseline.)
507
        image_hash = _image_hash(test_info, self._test_args, self._options)
508
509
        # FIXME: Pull this into TestShellThread._run().
510
        test_input.uri = self._port.filename_to_uri(test_input.filename)
511
        if _should_fetch_expected_checksum(self._options):
512
            test_input.image_checksum = self._port.expected_checksum(test_input.filename)
508
        start = time.time()
513
        start = time.time()
509
514
510
        thread_timeout = _milliseconds_to_seconds(
515
        thread_timeout = _milliseconds_to_seconds(
511
             _pad_timeout(int(test_info.timeout)))
516
             _pad_timeout(int(test_input.timeout)))
512
        self._next_timeout = start + thread_timeout
517
        self._next_timeout = start + thread_timeout
513
518
514
        crash, timeout, actual_checksum, output, error = \
519
        crash, timeout, actual_checksum, output, error = \
515
           self._driver.run_test(test_info.uri, test_info.timeout, image_hash)
520
           self._driver.run_test(test_input.uri, test_input.timeout, test_input.image_checksum)
516
        end = time.time()
521
        end = time.time()
517
522
518
        result = _process_output(self._port, self._options,
523
        result = _process_output(self._port, self._options,
519
                                 test_info, self._test_types,
524
                                 test_input, self._test_types,
520
                                 self._test_args, crash,
525
                                 self._test_args, crash,
521
                                 timeout, end - start, actual_checksum,
526
                                 timeout, end - start, actual_checksum,
522
                                 output, error)
527
                                 output, error)
- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py -29 / +21 lines
Lines 90-120 LAYOUT_TESTS_DIRECTORY = "LayoutTests" + os.sep a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py_sec1
90
TestExpectationsFile = test_expectations.TestExpectationsFile
90
TestExpectationsFile = test_expectations.TestExpectationsFile
91
91
92
92
93
class TestInfo:
93
class TestInput:
94
    """Groups information about a test for easy passing of data."""
94
    """Groups information about a test for easy passing of data."""
95
95
96
    def __init__(self, port, filename, timeout):
96
    def __init__(self, filename, timeout):
97
        """Generates the URI and stores the filename and timeout for this test.
97
        """Holds the input parameters for a test.
98
        Args:
98
        Args:
99
          filename: Full path to the test.
99
          filename: Full path to the test.
100
          timeout: Timeout for running the test in TestShell.
100
          timeout: Timeout in msecs the driver should use while running the test
101
          """
101
          """
102
        # FIXME: filename should really be test_name as a relative path.
102
        self.filename = filename
103
        self.filename = filename
103
        self._port = port
104
        # The image checksum is passed to the driver so that the driver can optionally not have
104
        self.uri = port.filename_to_uri(filename)
105
        # to output the image if the checksums match.
106
        self.image_checksum = None
105
        self.timeout = timeout
107
        self.timeout = timeout
106
        self._image_checksum = -1
107
108
108
    def image_hash(self):
109
        # FIXME: Maybe the URI shouldn't be part of the TestInput at all?
109
        # Read the image_hash lazily to reduce startup time.
110
        self.uri = None
110
        # This class is accessed across threads, but only one thread should
111
        # ever be dealing with any given TestInfo so no locking is needed.
112
        #
113
        # Note that we use -1 to indicate that we haven't read the value,
114
        # because expected_checksum() returns a string or None.
115
        if self._image_checksum == -1:
116
            self._image_checksum = self._port.expected_checksum(self.filename)
117
        return self._image_checksum
118
111
119
112
120
class ResultSummary(object):
113
class ResultSummary(object):
Lines 497-510 class TestRunner: a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py_sec2
497
490
498
        return return_value
491
        return return_value
499
492
500
    def _get_test_info_for_file(self, test_file):
493
    def _get_test_input_for_file(self, test_file):
501
        """Returns the appropriate TestInfo object for the file. Mostly this
494
        """Returns the appropriate TestInput object for the file. Mostly this
502
        is used for looking up the timeout value (in ms) to use for the given
495
        is used for looking up the timeout value (in ms) to use for the given
503
        test."""
496
        test."""
504
        if self._expectations.has_modifier(test_file, test_expectations.SLOW):
497
        if self._expectations.has_modifier(test_file, test_expectations.SLOW):
505
            return TestInfo(self._port, test_file,
498
            return TestInput(test_file, self._options.slow_time_out_ms)
506
                            self._options.slow_time_out_ms)
499
        return TestInput(test_file, self._options.time_out_ms)
507
        return TestInfo(self._port, test_file, self._options.time_out_ms)
508
500
509
    def _test_requires_lock(self, test_file):
501
    def _test_requires_lock(self, test_file):
510
        """Return True if the test needs to be locked when
502
        """Return True if the test needs to be locked when
Lines 522-528 class TestRunner: a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py_sec3
522
        cross-tests dependencies tend to occur within the same directory.
514
        cross-tests dependencies tend to occur within the same directory.
523
515
524
        Return:
516
        Return:
525
          The Queue of lists of TestInfo objects.
517
          The Queue of lists of TestInput objects.
526
        """
518
        """
527
519
528
        test_lists = []
520
        test_lists = []
Lines 530-550 class TestRunner: a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py_sec4
530
        if (self._options.experimental_fully_parallel or
522
        if (self._options.experimental_fully_parallel or
531
            self._is_single_threaded()):
523
            self._is_single_threaded()):
532
            for test_file in test_files:
524
            for test_file in test_files:
533
                test_info = self._get_test_info_for_file(test_file)
525
                test_input = self._get_test_input_for_file(test_file)
534
                if self._test_requires_lock(test_file):
526
                if self._test_requires_lock(test_file):
535
                    tests_to_http_lock.append(test_info)
527
                    tests_to_http_lock.append(test_input)
536
                else:
528
                else:
537
                    test_lists.append((".", [test_info]))
529
                    test_lists.append((".", [test_input]))
538
        else:
530
        else:
539
            tests_by_dir = {}
531
            tests_by_dir = {}
540
            for test_file in test_files:
532
            for test_file in test_files:
541
                directory = self._get_dir_for_test_file(test_file)
533
                directory = self._get_dir_for_test_file(test_file)
542
                test_info = self._get_test_info_for_file(test_file)
534
                test_input = self._get_test_input_for_file(test_file)
543
                if self._test_requires_lock(test_file):
535
                if self._test_requires_lock(test_file):
544
                    tests_to_http_lock.append(test_info)
536
                    tests_to_http_lock.append(test_input)
545
                else:
537
                else:
546
                    tests_by_dir.setdefault(directory, [])
538
                    tests_by_dir.setdefault(directory, [])
547
                    tests_by_dir[directory].append(test_info)
539
                    tests_by_dir[directory].append(test_input)
548
            # Sort by the number of tests in the dir so that the ones with the
540
            # Sort by the number of tests in the dir so that the ones with the
549
            # most tests get run first in order to maximize parallelization.
541
            # most tests get run first in order to maximize parallelization.
550
            # Number of tests is a good enough, but not perfect, approximation
542
            # Number of tests is a good enough, but not perfect, approximation
- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py -1 / +4 lines
Lines 380-385 class RebaselineTest(unittest.TestCase): a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py_sec1
380
            baseline = file + "-expected" + ext
380
            baseline = file + "-expected" + ext
381
            self.assertTrue(any(f.find(baseline) != -1 for f in file_list))
381
            self.assertTrue(any(f.find(baseline) != -1 for f in file_list))
382
382
383
    # FIXME: Add tests to ensure that we're *not* writing baselines when we're not
384
    # supposed to be.
385
383
    def disabled_test_reset_results(self):
386
    def disabled_test_reset_results(self):
384
        # FIXME: This test is disabled until we can rewrite it to use a
387
        # FIXME: This test is disabled until we can rewrite it to use a
385
        # mock filesystem.
388
        # mock filesystem.
Lines 426-432 class RebaselineTest(unittest.TestCase): a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py_sec2
426
429
427
430
428
class TestRunnerWrapper(run_webkit_tests.TestRunner):
431
class TestRunnerWrapper(run_webkit_tests.TestRunner):
429
    def _get_test_info_for_file(self, test_file):
432
    def _get_test_input_for_file(self, test_file):
430
        return test_file
433
        return test_file
431
434
432
435

Return to Bug 49573