1
# Copyright (C) 2010 Google Inc. All rights reserved.
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions are
7
# * Redistributions of source code must retain the above copyright
8
# notice, this list of conditions and the following disclaimer.
9
# * Redistributions in binary form must reproduce the above
10
# copyright notice, this list of conditions and the following disclaimer
11
# in the documentation and/or other materials provided with the
13
# * Neither the name of Google Inc. nor the names of its
14
# contributors may be used to endorse or promote products derived from
15
# this software without specific prior written permission.
17
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
31
from webkitpy.common.system.outputcapture import OutputCapture
32
from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer
33
from webkitpy.common.net.buildbot.buildbot_mock import MockBuilder
34
from webkitpy.common.system.executive_mock import MockExecutive2
35
from webkitpy.thirdparty.mock import Mock
36
from webkitpy.tool.commands.rebaseline import *
37
from webkitpy.tool.mocktool import MockTool, MockOptions
40
class _BaseTestCase(unittest.TestCase):
41
MOCK_WEB_RESULT = 'MOCK Web result, convert 404 to None=True'
42
WEB_PREFIX = 'http://example.com/f/builders/WebKit Mac10.7/results/layout-test-results'
44
command_constructor = None
47
self.tool = MockTool()
48
self.command = self.command_constructor() # lint warns that command_constructor might not be set, but this is intentional; pylint: disable-msg=E1102
49
self.command.bind_to_tool(self.tool)
50
self.lion_port = self.tool.port_factory.get_from_builder_name("WebKit Mac10.7")
51
self.lion_expectations_path = self.lion_port.path_to_test_expectations_file()
53
# FIXME: we should override builders._exact_matches here to point to a set
54
# of test ports and restore the value in tearDown(), and that way the
55
# individual tests wouldn't have to worry about it.
57
def _expand(self, path):
58
if self.tool.filesystem.isabs(path):
60
return self.tool.filesystem.join(self.lion_port.layout_tests_dir(), path)
62
def _read(self, path):
63
return self.tool.filesystem.read_text_file(self._expand(path))
65
def _write(self, path, contents):
66
self.tool.filesystem.write_text_file(self._expand(path), contents)
68
def _zero_out_test_expectations(self):
69
for port_name in self.tool.port_factory.all_port_names():
70
port = self.tool.port_factory.get(port_name)
71
for path in port.expectations_files():
73
self.tool.filesystem.written_files = {}
76
class TestRebaselineTest(_BaseTestCase):
77
command_constructor = RebaselineTest # AKA webkit-patch rebaseline-test-internal
80
super(TestRebaselineTest, self).setUp()
81
self.options = MockOptions(builder="WebKit Mac10.7", test="userscripts/another-test.html", suffixes="txt",
82
move_overwritten_baselines_to=None, results_directory=None)
84
def test_baseline_directory(self):
85
command = self.command
86
self.assertEqual(command._baseline_directory("Apple Win XP Debug (Tests)"), "/mock-checkout/LayoutTests/platform/win-xp")
87
self.assertEqual(command._baseline_directory("Apple Win 7 Release (Tests)"), "/mock-checkout/LayoutTests/platform/win")
88
self.assertEqual(command._baseline_directory("Apple Lion Release WK1 (Tests)"), "/mock-checkout/LayoutTests/platform/mac-lion")
89
self.assertEqual(command._baseline_directory("Apple Lion Release WK2 (Tests)"), "/mock-checkout/LayoutTests/platform/mac-wk2")
90
self.assertEqual(command._baseline_directory("GTK Linux 32-bit Release"), "/mock-checkout/LayoutTests/platform/gtk")
91
self.assertEqual(command._baseline_directory("EFL Linux 64-bit Release WK2"), "/mock-checkout/LayoutTests/platform/efl-wk2")
92
self.assertEqual(command._baseline_directory("Qt Linux Release"), "/mock-checkout/LayoutTests/platform/qt")
93
self.assertEqual(command._baseline_directory("WebKit Mac10.7"), "/mock-checkout/LayoutTests/platform/chromium-mac-lion")
94
self.assertEqual(command._baseline_directory("WebKit Mac10.6"), "/mock-checkout/LayoutTests/platform/chromium-mac-snowleopard")
96
def test_rebaseline_updates_expectations_file_noop(self):
97
self._zero_out_test_expectations()
98
self._write(self.lion_expectations_path, """Bug(B) [ Mac Linux XP Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ]
99
Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]
101
self._write("fast/dom/Window/window-postmessage-clone-really-deep-array.html", "Dummy test contents")
102
self._write("fast/css/large-list-of-rules-crash.html", "Dummy test contents")
103
self._write("userscripts/another-test.html", "Dummy test contents")
105
self.options.suffixes = "png,wav,txt"
106
self.command._rebaseline_test_and_update_expectations(self.options)
108
self.assertEqual(self.tool.web.urls_fetched,
109
[self.WEB_PREFIX + '/userscripts/another-test-actual.png',
110
self.WEB_PREFIX + '/userscripts/another-test-actual.wav',
111
self.WEB_PREFIX + '/userscripts/another-test-actual.txt'])
112
new_expectations = self._read(self.lion_expectations_path)
113
self.assertEqual(new_expectations, """Bug(B) [ Mac Linux XP Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ]
114
Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]
117
def test_rebaseline_updates_expectations_file(self):
118
self._write(self.lion_expectations_path, "Bug(x) [ Mac ] userscripts/another-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/another-test.html [ ImageOnlyFailure ]\n")
119
self._write("userscripts/another-test.html", "Dummy test contents")
121
self.options.suffixes = 'png,wav,txt'
122
self.command._rebaseline_test_and_update_expectations(self.options)
124
self.assertEqual(self.tool.web.urls_fetched,
125
[self.WEB_PREFIX + '/userscripts/another-test-actual.png',
126
self.WEB_PREFIX + '/userscripts/another-test-actual.wav',
127
self.WEB_PREFIX + '/userscripts/another-test-actual.txt'])
128
new_expectations = self._read(self.lion_expectations_path)
129
self.assertEqual(new_expectations, "Bug(x) [ MountainLion SnowLeopard ] userscripts/another-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/another-test.html [ ImageOnlyFailure ]\n")
131
def test_rebaseline_does_not_include_overrides(self):
132
self._write(self.lion_expectations_path, "Bug(x) [ Mac ] userscripts/another-test.html [ ImageOnlyFailure ]\nBug(z) [ Linux ] userscripts/another-test.html [ ImageOnlyFailure ]\n")
133
self._write(self.lion_port.path_from_chromium_base('skia', 'skia_test_expectations.txt'), "Bug(y) [ Mac ] other-test.html [ Failure ]\n")
134
self._write("userscripts/another-test.html", "Dummy test contents")
136
self.options.suffixes = 'png,wav,txt'
137
self.command._rebaseline_test_and_update_expectations(self.options)
139
self.assertEqual(self.tool.web.urls_fetched,
140
[self.WEB_PREFIX + '/userscripts/another-test-actual.png',
141
self.WEB_PREFIX + '/userscripts/another-test-actual.wav',
142
self.WEB_PREFIX + '/userscripts/another-test-actual.txt'])
144
new_expectations = self._read(self.lion_expectations_path)
145
self.assertEqual(new_expectations, "Bug(x) [ MountainLion SnowLeopard ] userscripts/another-test.html [ ImageOnlyFailure ]\nBug(z) [ Linux ] userscripts/another-test.html [ ImageOnlyFailure ]\n")
147
def test_rebaseline_test(self):
148
self.command._rebaseline_test("WebKit Linux", "userscripts/another-test.html", None, "txt", self.WEB_PREFIX)
149
self.assertEqual(self.tool.web.urls_fetched, [self.WEB_PREFIX + '/userscripts/another-test-actual.txt'])
151
def test_rebaseline_test_with_results_directory(self):
152
self._write(self.lion_expectations_path, "Bug(x) [ Mac ] userscripts/another-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/another-test.html [ ImageOnlyFailure ]\n")
153
self.options.results_directory = '/tmp'
154
self.command._rebaseline_test_and_update_expectations(self.options)
155
self.assertEqual(self.tool.web.urls_fetched, ['file:///tmp/userscripts/another-test-actual.txt'])
157
def test_rebaseline_test_and_print_scm_changes(self):
158
self.command._print_scm_changes = True
159
self.command._scm_changes = {'add': [], 'delete': []}
160
self.tool._scm.exists = lambda x: False
162
self.command._rebaseline_test("WebKit Linux", "userscripts/another-test.html", None, "txt", None)
164
self.assertEqual(self.command._scm_changes, {'add': ['/mock-checkout/LayoutTests/platform/chromium-linux/userscripts/another-test-expected.txt'], 'delete': []})
166
def test_rebaseline_and_copy_test(self):
167
self._write("userscripts/another-test-expected.txt", "generic result")
169
self.command._rebaseline_test("WebKit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt", None)
171
self.assertEqual(self._read('platform/chromium-mac-lion/userscripts/another-test-expected.txt'), self.MOCK_WEB_RESULT)
172
self.assertEqual(self._read('platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt'), 'generic result')
174
def test_rebaseline_and_copy_test_no_existing_result(self):
175
self.command._rebaseline_test("WebKit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt", None)
177
self.assertEqual(self._read('platform/chromium-mac-lion/userscripts/another-test-expected.txt'), self.MOCK_WEB_RESULT)
178
self.assertFalse(self.tool.filesystem.exists(self._expand('platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt')))
180
def test_rebaseline_and_copy_test_with_lion_result(self):
181
self._write("platform/chromium-mac-lion/userscripts/another-test-expected.txt", "original lion result")
183
self.command._rebaseline_test("WebKit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt", self.WEB_PREFIX)
185
self.assertEqual(self.tool.web.urls_fetched, [self.WEB_PREFIX + '/userscripts/another-test-actual.txt'])
186
self.assertEqual(self._read("platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt"), "original lion result")
187
self.assertEqual(self._read("platform/chromium-mac-lion/userscripts/another-test-expected.txt"), self.MOCK_WEB_RESULT)
189
def test_rebaseline_and_copy_no_overwrite_test(self):
190
self._write("platform/chromium-mac-lion/userscripts/another-test-expected.txt", "original lion result")
191
self._write("platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt", "original snowleopard result")
193
self.command._rebaseline_test("WebKit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt", None)
195
self.assertEqual(self._read("platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt"), "original snowleopard result")
196
self.assertEqual(self._read("platform/chromium-mac-lion/userscripts/another-test-expected.txt"), self.MOCK_WEB_RESULT)
198
def test_rebaseline_test_internal_with_move_overwritten_baselines_to(self):
199
self.tool.executive = MockExecutive2()
201
# FIXME: it's confusing that this is the test- port, and not the regular lion port. Really all of the tests should be using the test ports.
202
port = self.tool.port_factory.get('test-mac-snowleopard')
203
self._write(port._filesystem.join(port.layout_tests_dir(), 'platform/test-mac-snowleopard/failures/expected/image-expected.txt'), 'original snowleopard result')
205
old_exact_matches = builders._exact_matches
208
builders._exact_matches = {
209
"MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])},
210
"MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])},
213
options = MockOptions(optimize=True, builder="MOCK SnowLeopard", suffixes="txt",
214
move_overwritten_baselines_to=["test-mac-leopard"], verbose=True, test="failures/expected/image.html",
215
results_directory=None)
218
self.command.execute(options, [], self.tool)
220
out, _, _ = oc.restore_output()
221
builders._exact_matches = old_exact_matches
223
self.assertEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-mac-leopard/failures/expected/image-expected.txt')), 'original snowleopard result')
224
self.assertEqual(out, '{"add": []}\n')
227
class TestRebaselineJson(_BaseTestCase):
228
command_constructor = RebaselineJson
231
super(TestRebaselineJson, self).setUp()
232
self.tool.executive = MockExecutive2()
233
self.old_exact_matches = builders._exact_matches
234
builders._exact_matches = {
235
"MOCK builder": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"]),
236
"move_overwritten_baselines_to": ["test-mac-leopard"]},
237
"MOCK builder (Debug)": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier", "debug"])},
241
builders._exact_matches = self.old_exact_matches
242
super(TestRebaselineJson, self).tearDown()
244
def test_rebaseline_all(self):
245
options = MockOptions(optimize=True, verbose=True, move_overwritten_baselines=False, results_directory=None)
246
self.command._rebaseline(options, {"user-scripts/another-test.html": {"MOCK builder": ["txt", "png"]}})
248
# Note that we have one run_in_parallel() call followed by a run_command()
249
self.assertEqual(self.tool.executive.calls,
250
[[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'user-scripts/another-test.html', '--verbose']],
251
['echo', '--verbose', 'optimize-baselines', '--suffixes', 'txt,png', 'user-scripts/another-test.html']])
253
def test_rebaseline_debug(self):
254
options = MockOptions(optimize=True, verbose=True, move_overwritten_baselines=False, results_directory=None)
255
self.command._rebaseline(options, {"user-scripts/another-test.html": {"MOCK builder (Debug)": ["txt", "png"]}})
257
# Note that we have one run_in_parallel() call followed by a run_command()
258
self.assertEqual(self.tool.executive.calls,
259
[[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'user-scripts/another-test.html', '--verbose']],
260
['echo', '--verbose', 'optimize-baselines', '--suffixes', 'txt,png', 'user-scripts/another-test.html']])
262
def test_move_overwritten(self):
263
options = MockOptions(optimize=True, verbose=True, move_overwritten_baselines=True, results_directory=None)
264
self.command._rebaseline(options, {"user-scripts/another-test.html": {"MOCK builder": ["txt", "png"]}})
266
# Note that we have one run_in_parallel() call followed by a run_command()
267
self.assertEqual(self.tool.executive.calls,
268
[[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'user-scripts/another-test.html', '--move-overwritten-baselines-to', 'test-mac-leopard', '--verbose']],
269
['echo', '--verbose', 'optimize-baselines', '--suffixes', 'txt,png', 'user-scripts/another-test.html']])
271
def test_no_optimize(self):
272
options = MockOptions(optimize=False, verbose=True, move_overwritten_baselines=False, results_directory=None)
273
self.command._rebaseline(options, {"user-scripts/another-test.html": {"MOCK builder (Debug)": ["txt", "png"]}})
275
# Note that we have only one run_in_parallel() call
276
self.assertEqual(self.tool.executive.calls,
277
[[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'user-scripts/another-test.html', '--verbose']]])
279
def test_results_directory(self):
280
options = MockOptions(optimize=False, verbose=True, move_overwritten_baselines=False, results_directory='/tmp')
281
self.command._rebaseline(options, {"user-scripts/another-test.html": {"MOCK builder": ["txt", "png"]}})
283
# Note that we have only one run_in_parallel() call
284
self.assertEqual(self.tool.executive.calls,
285
[[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'user-scripts/another-test.html', '--results-directory', '/tmp', '--verbose']]])
288
class TestRebaseline(_BaseTestCase):
289
# This command shares most of its logic with RebaselineJson, so these tests just test what is different.
291
command_constructor = Rebaseline # AKA webkit-patch rebaseline
293
def test_tests_to_update(self):
295
OutputCapture().assert_outputs(self, self.command._tests_to_update, [build])
297
def test_rebaseline(self):
298
self.command._builders_to_pull_from = lambda: [MockBuilder('MOCK builder')]
299
self.command._tests_to_update = lambda builder: ['mock/path/to/test.html']
301
self._zero_out_test_expectations()
303
old_exact_matches = builders._exact_matches
306
builders._exact_matches = {
307
"MOCK builder": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])},
310
self.command.execute(MockOptions(optimize=False, builders=None, suffixes="txt,png", verbose=True, move_overwritten_baselines=False), [], self.tool)
313
builders._exact_matches = old_exact_matches
315
calls = filter(lambda x: x != ['qmake', '-v'] and x[0] != 'perl', self.tool.executive.calls)
316
self.assertEqual(calls,
317
[[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'mock/path/to/test.html', '--verbose']]])
320
class TestRebaselineExpectations(_BaseTestCase):
321
command_constructor = RebaselineExpectations
324
super(TestRebaselineExpectations, self).setUp()
325
self.options = MockOptions(optimize=False, builders=None, suffixes=['txt'], verbose=False, platform=None,
326
move_overwritten_baselines=False, results_directory=None)
328
def test_rebaseline_expectations(self):
329
self._zero_out_test_expectations()
331
self.tool.executive = MockExecutive2()
333
self.command._tests_to_rebaseline = lambda port: {'userscripts/another-test.html': set(['txt']), 'userscripts/images.svg': set(['png'])}
334
self.command.execute(self.options, [], self.tool)
336
# FIXME: change this to use the test- ports.
337
calls = filter(lambda x: x != ['qmake', '-v'], self.tool.executive.calls)
338
self.assertTrue(len(calls) == 1)
339
self.assertTrue(len(calls[0]) == 26)
341
def test_rebaseline_expectations_noop(self):
342
self._zero_out_test_expectations()
347
self.command.execute(self.options, [], self.tool)
349
_, _, logs = oc.restore_output()
350
self.assertEqual(self.tool.filesystem.written_files, {})
351
self.assertEqual(logs, 'Did not find any tests marked Rebaseline.\n')
353
def disabled_test_overrides_are_included_correctly(self):
354
# This tests that the any tests marked as REBASELINE in the overrides are found, but
355
# that the overrides do not get written into the main file.
356
self._zero_out_test_expectations()
358
self._write(self.lion_expectations_path, '')
359
self.lion_port.expectations_dict = lambda: {
360
self.lion_expectations_path: '',
361
'overrides': ('Bug(x) userscripts/another-test.html [ Failure Rebaseline ]\n'
362
'Bug(y) userscripts/test.html [ Crash ]\n')}
363
self._write('/userscripts/another-test.html', '')
365
self.assertEqual(self.command._tests_to_rebaseline(self.lion_port), {'userscripts/another-test.html': set(['png', 'txt', 'wav'])})
366
self.assertEqual(self._read(self.lion_expectations_path), '')
369
class _FakeOptimizer(BaselineOptimizer):
370
def read_results_by_directory(self, baseline_name):
371
if baseline_name.endswith('txt'):
372
return {'LayoutTests/passes/text.html': '123456',
373
'LayoutTests/platform/test-mac-leopard/passes/text.html': 'abcdef'}
377
class TestAnalyzeBaselines(_BaseTestCase):
378
command_constructor = AnalyzeBaselines
381
super(TestAnalyzeBaselines, self).setUp()
382
self.port = self.tool.port_factory.get('test')
383
self.tool.port_factory.get = (lambda port_name=None, options=None: self.port)
385
self.command._optimizer_class = _FakeOptimizer
386
self.command._write = (lambda msg: self.lines.append(msg)) # pylint bug warning about unnecessary lambda? pylint: disable-msg=W0108
388
def test_default(self):
389
self.command.execute(MockOptions(suffixes='txt', missing=False, platform=None), ['passes/text.html'], self.tool)
390
self.assertEqual(self.lines,
391
['passes/text-expected.txt:',
392
' (generic): 123456',
393
' test-mac-leopard: abcdef'])
395
def test_missing_baselines(self):
396
self.command.execute(MockOptions(suffixes='png,txt', missing=True, platform=None), ['passes/text.html'], self.tool)
397
self.assertEqual(self.lines,
398
['passes/text-expected.png: (no baselines found)',
399
'passes/text-expected.txt:',
400
' (generic): 123456',
401
' test-mac-leopard: abcdef'])