Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baselineoptimizer_unittest.py

Issue 2130093003: Fix pylint warnings in webkitpy/common/. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright (C) 2011 Google Inc. All rights reserved. 1 # Copyright (C) 2011 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
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 if path in self._exclusion_list: 44 if path in self._exclusion_list:
45 return False 45 return False
46 return MockSCM.exists(self, path) 46 return MockSCM.exists(self, path)
47 47
48 def delete(self, path): 48 def delete(self, path):
49 return self.delete_list([path]) 49 return self.delete_list([path])
50 50
51 def delete_list(self, paths): 51 def delete_list(self, paths):
52 for path in paths: 52 for path in paths:
53 if path in self._exclusion_list: 53 if path in self._exclusion_list:
54 raise Exception("File is not SCM managed: " + path) 54 raise Exception('File is not SCM managed: ' + path)
55 return MockSCM.delete_list(self, paths) 55 return MockSCM.delete_list(self, paths)
56 56
57 def move(self, origin, destination): 57 def move(self, origin, destination):
58 if origin in self._exclusion_list: 58 if origin in self._exclusion_list:
59 raise Exception("File is not SCM managed: " + origin) 59 raise Exception('File is not SCM managed: ' + origin)
60 return MockSCM.move(self, origin, destination) 60 return MockSCM.move(self, origin, destination)
61 61
62 62
63 class BaselineOptimizerTest(unittest.TestCase): 63 class BaselineOptimizerTest(unittest.TestCase):
64 64
65 def test_move_baselines(self): 65 def test_move_baselines(self):
66 host = MockHost(scm=ExcludingMockSCM( 66 host = MockHost(scm=ExcludingMockSCM(
67 ['/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another /test-expected.txt'])) 67 ['/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another /test-expected.txt']))
68 host.filesystem.write_text_file('/mock-checkout/third_party/WebKit/Layou tTests/VirtualTestSuites', '[]') 68 host.filesystem.write_text_file('/mock-checkout/third_party/WebKit/Layou tTests/VirtualTestSuites', '[]')
69 host.filesystem.write_binary_file( 69 host.filesystem.write_binary_file(
70 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/ test-expected.txt', 'result A') 70 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/ test-expected.txt', 'result A')
71 host.filesystem.write_binary_file( 71 host.filesystem.write_binary_file(
72 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/ test-expected.txt', 'result A') 72 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/ test-expected.txt', 'result A')
73 host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/Lay outTests/another/test-expected.txt', 'result B') 73 host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/Lay outTests/another/test-expected.txt', 'result B')
74 baseline_optimizer = BaselineOptimizer(host, host.port_factory.get( 74 baseline_optimizer = BaselineOptimizer(host, host.port_factory.get(
75 ), host.port_factory.all_port_names(), skip_scm_commands=False) 75 ), host.port_factory.all_port_names(), skip_scm_commands=False)
76 baseline_optimizer._move_baselines('another/test-expected.txt', { 76 baseline_optimizer.move_baselines(
77 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa', 77 'another/test-expected.txt',
78 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa', 78 {
79 '/mock-checkout/third_party/WebKit/LayoutTests': 'bbb', 79 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'a aa',
80 }, { 80 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'a aa',
81 '/mock-checkout/third_party/WebKit/LayoutTests': 'aaa', 81 '/mock-checkout/third_party/WebKit/LayoutTests': 'bbb',
82 }) 82 },
83 {
84 '/mock-checkout/third_party/WebKit/LayoutTests': 'aaa',
85 })
83 self.assertEqual(host.filesystem.read_binary_file( 86 self.assertEqual(host.filesystem.read_binary_file(
84 '/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected .txt'), 'result A') 87 '/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected .txt'), 'result A')
85 88
86 def test_move_baselines_skip_scm_commands(self): 89 def test_move_baselines_skip_scm_commands(self):
87 host = MockHost(scm=ExcludingMockSCM( 90 host = MockHost(scm=ExcludingMockSCM(
88 ['/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another /test-expected.txt'])) 91 ['/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another /test-expected.txt']))
89 host.filesystem.write_text_file('/mock-checkout/third_party/WebKit/Layou tTests/VirtualTestSuites', '[]') 92 host.filesystem.write_text_file('/mock-checkout/third_party/WebKit/Layou tTests/VirtualTestSuites', '[]')
90 host.filesystem.write_binary_file( 93 host.filesystem.write_binary_file(
91 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/ test-expected.txt', 'result A') 94 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/ test-expected.txt', 'result A')
92 host.filesystem.write_binary_file( 95 host.filesystem.write_binary_file(
93 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/ test-expected.txt', 'result A') 96 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/ test-expected.txt', 'result A')
94 host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/Lay outTests/another/test-expected.txt', 'result B') 97 host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/Lay outTests/another/test-expected.txt', 'result B')
95 baseline_optimizer = BaselineOptimizer(host, host.port_factory.get( 98 baseline_optimizer = BaselineOptimizer(host, host.port_factory.get(
96 ), host.port_factory.all_port_names(), skip_scm_commands=True) 99 ), host.port_factory.all_port_names(), skip_scm_commands=True)
97 baseline_optimizer._move_baselines('another/test-expected.txt', { 100 baseline_optimizer.move_baselines(
98 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa', 101 'another/test-expected.txt',
99 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa', 102 {
100 '/mock-checkout/third_party/WebKit/LayoutTests': 'bbb', 103 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'a aa',
101 }, { 104 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'a aa',
102 '/mock-checkout/third_party/WebKit/LayoutTests/platform/linux': 'bbb ', 105 '/mock-checkout/third_party/WebKit/LayoutTests': 'bbb',
103 '/mock-checkout/third_party/WebKit/LayoutTests': 'aaa', 106 },
104 }) 107 {
108 '/mock-checkout/third_party/WebKit/LayoutTests/platform/linux': 'bbb',
109 '/mock-checkout/third_party/WebKit/LayoutTests': 'aaa',
110 })
105 self.assertEqual(host.filesystem.read_binary_file( 111 self.assertEqual(host.filesystem.read_binary_file(
106 '/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected .txt'), 'result A') 112 '/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected .txt'), 'result A')
107 113
108 self.assertEqual(baseline_optimizer._files_to_delete, [ 114 self.assertEqual(baseline_optimizer.files_to_delete, [
109 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/ test-expected.txt', 115 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/ test-expected.txt',
110 ]) 116 ])
111 117
112 self.assertEqual(baseline_optimizer._files_to_add, [ 118 self.assertEqual(baseline_optimizer.files_to_add, [
113 '/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected .txt', 119 '/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected .txt',
114 '/mock-checkout/third_party/WebKit/LayoutTests/platform/linux/anothe r/test-expected.txt', 120 '/mock-checkout/third_party/WebKit/LayoutTests/platform/linux/anothe r/test-expected.txt',
115 ]) 121 ])
116 122
117 def _assertOptimization(self, results_by_directory, expected_new_results_by_ directory, 123 def _assert_optimization(self, dir_to_results, dir_to_expected_new_results,
118 baseline_dirname='', expected_files_to_delete=None, host=None): 124 baseline_dirname='', expected_files_to_delete=None, host=None):
119 if not host: 125 if not host:
120 host = MockHost() 126 host = MockHost()
121 fs = host.filesystem 127 fs = host.filesystem
122 webkit_base = WebKitFinder(fs).webkit_base() 128 webkit_base = WebKitFinder(fs).webkit_base()
123 baseline_name = 'mock-baseline-expected.txt' 129 baseline_name = 'mock-baseline-expected.txt'
124 fs.write_text_file(fs.join(webkit_base, 'LayoutTests', 'VirtualTestSuite s'), 130 fs.write_text_file(fs.join(webkit_base, 'LayoutTests', 'VirtualTestSuite s'),
125 '[{"prefix": "gpu", "base": "fast/canvas", "args": [" --foo"]}]') 131 '[{"prefix": "gpu", "base": "fast/canvas", "args": [" --foo"]}]')
126 132
127 for dirname, contents in results_by_directory.items(): 133 for dirname, contents in dir_to_results.items():
128 path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name) 134 path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
129 fs.write_binary_file(path, contents) 135 fs.write_binary_file(path, contents)
130 136
131 baseline_optimizer = BaselineOptimizer(host, host.port_factory.get( 137 baseline_optimizer = BaselineOptimizer(host, host.port_factory.get(
132 ), host.port_factory.all_port_names(), skip_scm_commands=expected_files_ to_delete is not None) 138 ), host.port_factory.all_port_names(), skip_scm_commands=expected_files_ to_delete is not None)
133 self.assertTrue(baseline_optimizer.optimize(fs.join(baseline_dirname, ba seline_name))) 139 self.assertTrue(baseline_optimizer.optimize(fs.join(baseline_dirname, ba seline_name)))
134 140
135 for dirname, contents in expected_new_results_by_directory.items(): 141 for dirname, contents in dir_to_expected_new_results.items():
136 path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name) 142 path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
137 if contents is None: 143 if contents is None:
138 self.assertTrue(not fs.exists(path) or path in baseline_optimize r._files_to_delete) 144 self.assertTrue(not fs.exists(path) or path in baseline_optimize r.files_to_delete)
139 else: 145 else:
140 self.assertEqual(fs.read_binary_file(path), contents) 146 self.assertEqual(fs.read_binary_file(path), contents)
141 147
142 # Check that the files that were in the original set have been deleted w here necessary. 148 # Check that the files that were in the original set have been deleted w here necessary.
143 for dirname in results_by_directory: 149 for dirname in dir_to_results:
144 path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name) 150 path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
145 if not dirname in expected_new_results_by_directory: 151 if dirname not in dir_to_expected_new_results:
146 self.assertTrue(not fs.exists(path) or path in baseline_optimize r._files_to_delete) 152 self.assertTrue(not fs.exists(path) or path in baseline_optimize r.files_to_delete)
147 153
148 if expected_files_to_delete: 154 if expected_files_to_delete:
149 self.assertEqual(sorted(baseline_optimizer._files_to_delete), sorted (expected_files_to_delete)) 155 self.assertEqual(sorted(baseline_optimizer.files_to_delete), sorted( expected_files_to_delete))
150 156
151 def test_linux_redundant_with_win(self): 157 def test_linux_redundant_with_win(self):
152 self._assertOptimization({ 158 self._assert_optimization(
153 'platform/win': '1', 159 {
154 'platform/linux': '1', 160 'platform/win': '1',
155 }, { 161 'platform/linux': '1',
156 'platform/win': '1', 162 },
157 }) 163 {
164 'platform/win': '1',
165 })
158 166
159 def test_covers_mac_win_linux(self): 167 def test_covers_mac_win_linux(self):
160 self._assertOptimization({ 168 self._assert_optimization(
161 'platform/mac': '1', 169 {
162 'platform/win': '1', 170 'platform/mac': '1',
163 'platform/linux': '1', 171 'platform/win': '1',
164 '': None, 172 'platform/linux': '1',
165 }, { 173 '': None,
166 '': '1', 174 },
167 }) 175 {
176 '': '1',
177 })
168 178
169 def test_overwrites_root(self): 179 def test_overwrites_root(self):
170 self._assertOptimization({ 180 self._assert_optimization(
171 'platform/mac': '1', 181 {
172 'platform/win': '1', 182 'platform/mac': '1',
173 'platform/linux': '1', 183 'platform/win': '1',
174 '': '2', 184 'platform/linux': '1',
175 }, { 185 '': '2',
176 '': '1', 186 },
177 }) 187 {
188 '': '1',
189 })
178 190
179 def test_no_new_common_directory(self): 191 def test_no_new_common_directory(self):
180 self._assertOptimization({ 192 self._assert_optimization(
181 'platform/mac': '1', 193 {
182 'platform/linux': '1', 194 'platform/mac': '1',
183 '': '2', 195 'platform/linux': '1',
184 }, { 196 '': '2',
185 'platform/mac': '1', 197 },
186 'platform/linux': '1', 198 {
187 '': '2', 199 'platform/mac': '1',
188 }) 200 'platform/linux': '1',
201 '': '2',
202 })
189 203
190 def test_local_optimization(self): 204 def test_local_optimization(self):
191 self._assertOptimization({ 205 self._assert_optimization(
192 'platform/mac': '1', 206 {
193 'platform/linux': '1', 207 'platform/mac': '1',
194 'platform/linux-precise': '1', 208 'platform/linux': '1',
195 }, { 209 'platform/linux-precise': '1',
196 'platform/mac': '1', 210 },
197 'platform/linux': '1', 211 {
198 }) 212 'platform/mac': '1',
213 'platform/linux': '1',
214 })
199 215
200 def test_local_optimization_skipping_a_port_in_the_middle(self): 216 def test_local_optimization_skipping_a_port_in_the_middle(self):
201 self._assertOptimization({ 217 self._assert_optimization(
202 'platform/mac-snowleopard': '1', 218 {
203 'platform/win': '1', 219 'platform/mac-snowleopard': '1',
204 'platform/linux': '1', 220 'platform/win': '1',
205 'platform/linux-precise': '1', 221 'platform/linux': '1',
206 }, { 222 'platform/linux-precise': '1',
207 'platform/mac-snowleopard': '1', 223 },
208 'platform/win': '1', 224 {
209 }) 225 'platform/mac-snowleopard': '1',
226 'platform/win': '1',
227 })
210 228
211 def test_baseline_redundant_with_root(self): 229 def test_baseline_redundant_with_root(self):
212 self._assertOptimization({ 230 self._assert_optimization(
213 'platform/mac': '1', 231 {
214 'platform/win': '2', 232 'platform/mac': '1',
215 '': '2', 233 'platform/win': '2',
216 }, { 234 '': '2',
217 'platform/mac': '1', 235 },
218 '': '2', 236 {
219 }) 237 'platform/mac': '1',
238 '': '2',
239 })
220 240
221 def test_root_baseline_unused(self): 241 def test_root_baseline_unused(self):
222 self._assertOptimization({ 242 self._assert_optimization(
223 'platform/mac': '1', 243 {
224 'platform/win': '2', 244 'platform/mac': '1',
225 '': '3', 245 'platform/win': '2',
226 }, { 246 '': '3',
227 'platform/mac': '1', 247 },
228 'platform/win': '2', 248 {
229 }) 249 'platform/mac': '1',
250 'platform/win': '2',
251 })
230 252
231 def test_root_baseline_unused_and_non_existant(self): 253 def test_root_baseline_unused_and_non_existant(self):
232 self._assertOptimization({ 254 self._assert_optimization(
233 'platform/mac': '1', 255 {
234 'platform/win': '2', 256 'platform/mac': '1',
235 }, { 257 'platform/win': '2',
236 'platform/mac': '1', 258 },
237 'platform/win': '2', 259 {
238 }) 260 'platform/mac': '1',
261 'platform/win': '2',
262 })
239 263
240 def test_virtual_root_redundant_with_actual_root(self): 264 def test_virtual_root_redundant_with_actual_root(self):
241 self._assertOptimization({ 265 self._assert_optimization(
242 'virtual/gpu/fast/canvas': '2', 266 {
243 'fast/canvas': '2', 267 'virtual/gpu/fast/canvas': '2',
244 }, { 268 'fast/canvas': '2',
245 'virtual/gpu/fast/canvas': None, 269 },
246 'fast/canvas': '2', 270 {
247 }, baseline_dirname='virtual/gpu/fast/canvas') 271 'virtual/gpu/fast/canvas': None,
272 'fast/canvas': '2',
273 },
274 baseline_dirname='virtual/gpu/fast/canvas')
248 275
249 def test_virtual_root_redundant_with_ancestors(self): 276 def test_virtual_root_redundant_with_ancestors(self):
250 self._assertOptimization({ 277 self._assert_optimization(
251 'virtual/gpu/fast/canvas': '2', 278 {
252 'platform/mac/fast/canvas': '2', 279 'virtual/gpu/fast/canvas': '2',
253 'platform/win/fast/canvas': '2', 280 'platform/mac/fast/canvas': '2',
254 }, { 281 'platform/win/fast/canvas': '2',
255 'virtual/gpu/fast/canvas': None, 282 },
256 'fast/canvas': '2', 283 {
257 }, baseline_dirname='virtual/gpu/fast/canvas') 284 'virtual/gpu/fast/canvas': None,
285 'fast/canvas': '2',
286 },
287 baseline_dirname='virtual/gpu/fast/canvas')
258 288
259 def test_virtual_root_redundant_with_ancestors_skip_scm_commands(self): 289 def test_virtual_root_redundant_with_ancestors_skip_scm_commands(self):
260 self._assertOptimization({ 290 self._assert_optimization(
261 'virtual/gpu/fast/canvas': '2', 291 {
262 'platform/mac/fast/canvas': '2', 292 'virtual/gpu/fast/canvas': '2',
263 'platform/win/fast/canvas': '2', 293 'platform/mac/fast/canvas': '2',
264 }, { 294 'platform/win/fast/canvas': '2',
265 'virtual/gpu/fast/canvas': None, 295 },
266 'fast/canvas': '2', 296 {
267 }, 297 'virtual/gpu/fast/canvas': None,
298 'fast/canvas': '2',
299 },
268 baseline_dirname='virtual/gpu/fast/canvas', 300 baseline_dirname='virtual/gpu/fast/canvas',
269 expected_files_to_delete=[ 301 expected_files_to_delete=[
270 '/mock-checkout/third_party/WebKit/LayoutTests/virtual/gpu/fast/canv as/mock-baseline-expected.txt', 302 '/mock-checkout/third_party/WebKit/LayoutTests/virtual/gpu/fast/ canvas/mock-baseline-expected.txt',
271 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/fast/can vas/mock-baseline-expected.txt', 303 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/fast /canvas/mock-baseline-expected.txt',
272 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/fast/can vas/mock-baseline-expected.txt', 304 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/fast /canvas/mock-baseline-expected.txt',
273 ]) 305 ])
274 306
275 def test_virtual_root_redundant_with_ancestors_skip_scm_commands_with_file_n ot_in_scm(self): 307 def test_virtual_root_redundant_with_ancestors_skip_scm_commands_with_file_n ot_in_scm(self):
276 self._assertOptimization({ 308 self._assert_optimization(
277 'virtual/gpu/fast/canvas': '2', 309 {
278 'platform/mac/fast/canvas': '2', 310 'virtual/gpu/fast/canvas': '2',
279 'platform/win/fast/canvas': '2', 311 'platform/mac/fast/canvas': '2',
280 }, { 312 'platform/win/fast/canvas': '2',
281 'virtual/gpu/fast/canvas': None, 313 },
282 'fast/canvas': '2', 314 {
283 }, 315 'virtual/gpu/fast/canvas': None,
316 'fast/canvas': '2',
317 },
284 baseline_dirname='virtual/gpu/fast/canvas', 318 baseline_dirname='virtual/gpu/fast/canvas',
285 expected_files_to_delete=[ 319 expected_files_to_delete=[
286 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/fast/can vas/mock-baseline-expected.txt', 320 '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/fast /canvas/mock-baseline-expected.txt',
287 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/fast/can vas/mock-baseline-expected.txt', 321 '/mock-checkout/third_party/WebKit/LayoutTests/platform/win/fast /canvas/mock-baseline-expected.txt',
288 ], 322 ],
289 host=MockHost(scm=ExcludingMockSCM(['/mock-checkout/third_party/WebK it/LayoutTests/virtual/gpu/fast/canvas/mock-baseline-expected.txt']))) 323 host=MockHost(scm=ExcludingMockSCM([
324 '/mock-checkout/third_party/WebKit/LayoutTests/virtual/gpu/fast/ canvas/mock-baseline-expected.txt'
325 ])))
290 326
291 def test_virtual_root_not_redundant_with_ancestors(self): 327 def test_virtual_root_not_redundant_with_ancestors(self):
292 self._assertOptimization({ 328 self._assert_optimization(
293 'virtual/gpu/fast/canvas': '2', 329 {
294 'platform/mac/fast/canvas': '1', 330 'virtual/gpu/fast/canvas': '2',
295 }, { 331 'platform/mac/fast/canvas': '1',
296 'virtual/gpu/fast/canvas': '2', 332 },
297 'platform/mac/fast/canvas': '1', 333 {
298 }, baseline_dirname='virtual/gpu/fast/canvas') 334 'virtual/gpu/fast/canvas': '2',
335 'platform/mac/fast/canvas': '1',
336 },
337 baseline_dirname='virtual/gpu/fast/canvas')
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698