1# Copyright (C) 2011 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
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
12# distribution.
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.
16#
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.
28
29import unittest
30
31from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer
32from webkitpy.common.checkout.scm.scm_mock import MockSCM
33from webkitpy.common.host_mock import MockHost
34from webkitpy.common.webkit_finder import WebKitFinder
35
36
37class ExcludingMockSCM(MockSCM):
38    def __init__(self, exclusion_list, filesystem=None, executive=None):
39        MockSCM.__init__(self, filesystem, executive)
40        self._exclusion_list = exclusion_list
41
42    def exists(self, path):
43        if path in self._exclusion_list:
44            return False
45        return MockSCM.exists(self, path)
46
47    def delete(self, path):
48        return self.delete_list([path])
49
50    def delete_list(self, paths):
51        for path in paths:
52            if path in self._exclusion_list:
53                raise Exception("File is not SCM managed: " + path)
54        return MockSCM.delete_list(self, paths)
55
56    def move(self, origin, destination):
57        if origin in self._exclusion_list:
58            raise Exception("File is not SCM managed: " + origin)
59        return MockSCM.move(self, origin, destination)
60
61
62class BaselineOptimizerTest(unittest.TestCase):
63    def test_move_baselines(self):
64        host = MockHost(scm=ExcludingMockSCM(['/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt']))
65        host.filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/VirtualTestSuites', '[]')
66        host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt', 'result A')
67        host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt', 'result A')
68        host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt', 'result B')
69        baseline_optimizer = BaselineOptimizer(host, host.port_factory.get(), host.port_factory.all_port_names(), skip_scm_commands=False)
70        baseline_optimizer._move_baselines('another/test-expected.txt', {
71            '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa',
72            '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa',
73            '/mock-checkout/third_party/WebKit/LayoutTests': 'bbb',
74        }, {
75            '/mock-checkout/third_party/WebKit/LayoutTests': 'aaa',
76        })
77        self.assertEqual(host.filesystem.read_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt'), 'result A')
78
79    def test_move_baselines_skip_scm_commands(self):
80        host = MockHost(scm=ExcludingMockSCM(['/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt']))
81        host.filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/VirtualTestSuites', '[]')
82        host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt', 'result A')
83        host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt', 'result A')
84        host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt', 'result B')
85        baseline_optimizer = BaselineOptimizer(host, host.port_factory.get(), host.port_factory.all_port_names(), skip_scm_commands=True)
86        baseline_optimizer._move_baselines('another/test-expected.txt', {
87            '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa',
88            '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa',
89            '/mock-checkout/third_party/WebKit/LayoutTests': 'bbb',
90        }, {
91            '/mock-checkout/third_party/WebKit/LayoutTests/platform/linux': 'bbb',
92            '/mock-checkout/third_party/WebKit/LayoutTests': 'aaa',
93        })
94        self.assertEqual(host.filesystem.read_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt'), 'result A')
95
96        self.assertEqual(baseline_optimizer._files_to_delete, [
97            '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt',
98        ])
99
100        self.assertEqual(baseline_optimizer._files_to_add, [
101            '/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt',
102            '/mock-checkout/third_party/WebKit/LayoutTests/platform/linux/another/test-expected.txt',
103        ])
104
105    def _assertOptimization(self, results_by_directory, expected_new_results_by_directory, baseline_dirname='', expected_files_to_delete=None, host=None):
106        if not host:
107            host = MockHost()
108        fs = host.filesystem
109        webkit_base = WebKitFinder(fs).webkit_base()
110        baseline_name = 'mock-baseline-expected.txt'
111        fs.write_text_file(fs.join(webkit_base, 'LayoutTests', 'VirtualTestSuites'),
112                           '[{"prefix": "gpu", "base": "fast/canvas", "args": ["--foo"]}]')
113
114        for dirname, contents in results_by_directory.items():
115            path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
116            fs.write_binary_file(path, contents)
117
118        baseline_optimizer = BaselineOptimizer(host, host.port_factory.get(), host.port_factory.all_port_names(), skip_scm_commands=expected_files_to_delete is not None)
119        self.assertTrue(baseline_optimizer.optimize(fs.join(baseline_dirname, baseline_name)))
120
121        for dirname, contents in expected_new_results_by_directory.items():
122            path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
123            if contents is None:
124                self.assertTrue(not fs.exists(path) or path in baseline_optimizer._files_to_delete)
125            else:
126                self.assertEqual(fs.read_binary_file(path), contents)
127
128        # Check that the files that were in the original set have been deleted where necessary.
129        for dirname in results_by_directory:
130            path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
131            if not dirname in expected_new_results_by_directory:
132                self.assertTrue(not fs.exists(path) or path in baseline_optimizer._files_to_delete)
133
134        if expected_files_to_delete:
135            self.assertEqual(sorted(baseline_optimizer._files_to_delete), sorted(expected_files_to_delete))
136
137    def test_linux_redundant_with_win(self):
138        self._assertOptimization({
139            'platform/win': '1',
140            'platform/linux': '1',
141        }, {
142            'platform/win': '1',
143        })
144
145    def test_covers_mac_win_linux(self):
146        self._assertOptimization({
147            'platform/mac': '1',
148            'platform/win': '1',
149            'platform/linux': '1',
150            '': None,
151        }, {
152            '': '1',
153        })
154
155    def test_overwrites_root(self):
156        self._assertOptimization({
157            'platform/mac': '1',
158            'platform/win': '1',
159            'platform/linux': '1',
160            '': '2',
161        }, {
162            '': '1',
163        })
164
165    def test_no_new_common_directory(self):
166        self._assertOptimization({
167            'platform/mac': '1',
168            'platform/linux': '1',
169            '': '2',
170        }, {
171            'platform/mac': '1',
172            'platform/linux': '1',
173            '': '2',
174        })
175
176
177    def test_local_optimization(self):
178        self._assertOptimization({
179            'platform/mac': '1',
180            'platform/linux': '1',
181            'platform/linux-x86': '1',
182        }, {
183            'platform/mac': '1',
184            'platform/linux': '1',
185        })
186
187    def test_local_optimization_skipping_a_port_in_the_middle(self):
188        self._assertOptimization({
189            'platform/mac-snowleopard': '1',
190            'platform/win': '1',
191            'platform/linux-x86': '1',
192        }, {
193            'platform/mac-snowleopard': '1',
194            'platform/win': '1',
195        })
196
197    def test_baseline_redundant_with_root(self):
198        self._assertOptimization({
199            'platform/mac': '1',
200            'platform/win': '2',
201            '': '2',
202        }, {
203            'platform/mac': '1',
204            '': '2',
205        })
206
207    def test_root_baseline_unused(self):
208        self._assertOptimization({
209            'platform/mac': '1',
210            'platform/win': '2',
211            '': '3',
212        }, {
213            'platform/mac': '1',
214            'platform/win': '2',
215        })
216
217    def test_root_baseline_unused_and_non_existant(self):
218        self._assertOptimization({
219            'platform/mac': '1',
220            'platform/win': '2',
221        }, {
222            'platform/mac': '1',
223            'platform/win': '2',
224        })
225
226    def test_virtual_root_redundant_with_actual_root(self):
227        self._assertOptimization({
228            'virtual/gpu/fast/canvas': '2',
229            'fast/canvas': '2',
230        }, {
231            'virtual/gpu/fast/canvas': None,
232            'fast/canvas': '2',
233        }, baseline_dirname='virtual/gpu/fast/canvas')
234
235    def test_virtual_root_redundant_with_ancestors(self):
236        self._assertOptimization({
237            'virtual/gpu/fast/canvas': '2',
238            'platform/mac/fast/canvas': '2',
239            'platform/win/fast/canvas': '2',
240        }, {
241            'virtual/gpu/fast/canvas': None,
242            'fast/canvas': '2',
243        }, baseline_dirname='virtual/gpu/fast/canvas')
244
245    def test_virtual_root_redundant_with_ancestors_skip_scm_commands(self):
246        self._assertOptimization({
247            'virtual/gpu/fast/canvas': '2',
248            'platform/mac/fast/canvas': '2',
249            'platform/win/fast/canvas': '2',
250        }, {
251            'virtual/gpu/fast/canvas': None,
252            'fast/canvas': '2',
253        },
254        baseline_dirname='virtual/gpu/fast/canvas',
255        expected_files_to_delete=[
256            '/mock-checkout/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/mock-baseline-expected.txt',
257            '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/mock-baseline-expected.txt',
258            '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/fast/canvas/mock-baseline-expected.txt',
259        ])
260
261    def test_virtual_root_redundant_with_ancestors_skip_scm_commands_with_file_not_in_scm(self):
262        self._assertOptimization({
263            'virtual/gpu/fast/canvas': '2',
264            'platform/mac/fast/canvas': '2',
265            'platform/win/fast/canvas': '2',
266        }, {
267            'virtual/gpu/fast/canvas': None,
268            'fast/canvas': '2',
269        },
270        baseline_dirname='virtual/gpu/fast/canvas',
271        expected_files_to_delete=[
272            '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/mock-baseline-expected.txt',
273            '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/fast/canvas/mock-baseline-expected.txt',
274        ],
275        host=MockHost(scm=ExcludingMockSCM(['/mock-checkout/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/mock-baseline-expected.txt'])))
276
277    def test_virtual_root_not_redundant_with_ancestors(self):
278        self._assertOptimization({
279            'virtual/gpu/fast/canvas': '2',
280            'platform/mac/fast/canvas': '1',
281        }, {
282            'virtual/gpu/fast/canvas': '2',
283            'platform/mac/fast/canvas': '1',
284        }, baseline_dirname='virtual/gpu/fast/canvas')
285