4838f13c8a352937870ec337ca09ca2b32184799
[WebKit-https.git] / Websites / webkit-perf.appspot.com / models_unittest.py
1 #!/usr/bin/env python
2 # Copyright (C) 2012 Google Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 import models
31 import unittest
32
33 from datetime import datetime
34 from google.appengine.api import memcache
35 from google.appengine.ext import testbed
36 from time import mktime
37
38
39 class DataStoreTestsBase(unittest.TestCase):
40     def setUp(self):
41         self.testbed = testbed.Testbed()
42         self.testbed.activate()
43         self.testbed.init_datastore_v3_stub()
44
45     def tearDown(self):
46         self.testbed.deactivate()
47
48     def assertThereIsNoInstanceOf(self, model):
49         self.assertEqual(len(model.all().fetch(5)), 0)
50
51     def assertOnlyInstance(self, only_instasnce):
52         self.assertOnlyInstances([only_instasnce])
53
54     def assertOnlyInstances(self, expected_instances):
55         actual_instances = expected_instances[0].__class__.all().fetch(len(expected_instances) + 1)
56         self.assertEqualUnorderedModelList(actual_instances, expected_instances)
57
58     def assertEqualUnorderedModelList(self, list1, list2):
59         self.assertEqualUnorderedList([item.key() for item in list1], [item.key() for item in list1])
60
61     def assertEqualUnorderedList(self, list1, list2):
62         self.assertEqual(set(list1), set(list2))
63         self.assertEqual(len(list1), len(list2))
64
65
66 class HelperTests(DataStoreTestsBase):
67     def _assert_there_is_exactly_one_id_holder_and_matches(self, id):
68         id_holders = models.NumericIdHolder.all().fetch(5)
69         self.assertEqual(len(id_holders), 1)
70         self.assertTrue(id_holders[0])
71         self.assertEqual(id_holders[0].key().id(), id)
72
73     def test_create_in_transaction_with_numeric_id_holder(self):
74
75         def execute(id):
76             return models.Branch(id=id, name='some branch', key_name='some-branch').put()
77
78         self.assertThereIsNoInstanceOf(models.Branch)
79         self.assertThereIsNoInstanceOf(models.NumericIdHolder)
80
81         self.assertTrue(models.create_in_transaction_with_numeric_id_holder(execute))
82
83         branches = models.Branch.all().fetch(5)
84         self.assertEqual(len(branches), 1)
85         self.assertEqual(branches[0].name, 'some branch')
86         self.assertEqual(branches[0].key().name(), 'some-branch')
87
88         self._assert_there_is_exactly_one_id_holder_and_matches(branches[0].id)
89
90     def test_failing_in_create_in_transaction_with_numeric_id_holder(self):
91
92         def execute(id):
93             return None
94
95         self.assertThereIsNoInstanceOf(models.Branch)
96         self.assertThereIsNoInstanceOf(models.NumericIdHolder)
97
98         self.assertFalse(models.create_in_transaction_with_numeric_id_holder(execute))
99
100         self.assertThereIsNoInstanceOf(models.Branch)
101         self.assertThereIsNoInstanceOf(models.NumericIdHolder)
102
103     def test_raising_in_create_in_transaction_with_numeric_id_holder(self):
104
105         def execute(id):
106             raise TypeError
107             return None
108
109         self.assertThereIsNoInstanceOf(models.Branch)
110         self.assertThereIsNoInstanceOf(models.NumericIdHolder)
111
112         self.assertRaises(TypeError, models.create_in_transaction_with_numeric_id_holder, (execute))
113
114         self.assertThereIsNoInstanceOf(models.Branch)
115         self.assertThereIsNoInstanceOf(models.NumericIdHolder)
116
117     def test_delete_model_with_numeric_id_holder(self):
118
119         def execute(id):
120             return models.Branch(id=id, name='some branch', key_name='some-branch').put()
121
122         branch = models.Branch.get(models.create_in_transaction_with_numeric_id_holder(execute))
123         self.assertOnlyInstance(branch)
124
125         models.delete_model_with_numeric_id_holder(branch)
126
127         self.assertThereIsNoInstanceOf(models.Branch)
128         self.assertThereIsNoInstanceOf(models.NumericIdHolder)
129
130     def test_model_from_numeric_id(self):
131
132         def execute(id):
133             return models.Branch(id=id, name='some branch', key_name='some-branch').put()
134
135         branch = models.Branch.get(models.create_in_transaction_with_numeric_id_holder(execute))
136
137         self.assertEqual(models.model_from_numeric_id(branch.id, models.Branch).key(), branch.key())
138         self.assertEqual(models.model_from_numeric_id(branch.id + 1, models.Branch), None)
139         models.delete_model_with_numeric_id_holder(branch)
140         self.assertEqual(models.model_from_numeric_id(branch.id, models.Branch), None)
141
142
143 class BranchTests(DataStoreTestsBase):
144     def test_create_if_possible(self):
145         self.assertThereIsNoInstanceOf(models.Branch)
146
147         branch = models.Branch.create_if_possible('some-branch', 'some branch')
148         self.assertTrue(branch)
149         self.assertTrue(branch.key().name(), 'some-branch')
150         self.assertTrue(branch.name, 'some branch')
151         self.assertOnlyInstance(branch)
152
153         self.assertFalse(models.Branch.create_if_possible('some-branch', 'some other branch'))
154         self.assertTrue(branch.name, 'some branch')
155         self.assertOnlyInstance(branch)
156
157
158 class PlatformTests(DataStoreTestsBase):
159     def test_create_if_possible(self):
160         self.assertThereIsNoInstanceOf(models.Platform)
161
162         platform = models.Platform.create_if_possible('some-platform', 'some platform')
163         self.assertTrue(platform)
164         self.assertTrue(platform.key().name(), 'some-platform')
165         self.assertTrue(platform.name, 'some platform')
166         self.assertOnlyInstance(platform)
167
168         self.assertFalse(models.Platform.create_if_possible('some-platform', 'some other platform'))
169         self.assertTrue(platform.name, 'some platform')
170         self.assertOnlyInstance(platform)
171
172
173 class BuilderTests(DataStoreTestsBase):
174     def test_create(self):
175         builder_key = models.Builder.create('some builder', 'some password')
176         self.assertTrue(builder_key)
177         builder = models.Builder.get(builder_key)
178         self.assertEqual(builder.key().name(), 'some builder')
179         self.assertEqual(builder.name, 'some builder')
180         self.assertEqual(builder.password, models.Builder._hashed_password('some password'))
181
182     def test_update_password(self):
183         builder = models.Builder.get(models.Builder.create('some builder', 'some password'))
184         self.assertEqual(builder.password, models.Builder._hashed_password('some password'))
185         builder.update_password('other password')
186         self.assertEqual(builder.password, models.Builder._hashed_password('other password'))
187
188         # Make sure it's saved
189         builder = models.Builder.get(builder.key())
190         self.assertEqual(builder.password, models.Builder._hashed_password('other password'))
191
192     def test_hashed_password(self):
193         self.assertNotEqual(models.Builder._hashed_password('some password'), 'some password')
194         self.assertFalse('some password' in models.Builder._hashed_password('some password'))
195         self.assertEqual(len(models.Builder._hashed_password('some password')), 64)
196
197     def test_authenticate(self):
198         builder = models.Builder.get(models.Builder.create('some builder', 'some password'))
199         self.assertTrue(builder.authenticate('some password'))
200         self.assertFalse(builder.authenticate('bad password'))
201
202
203 def _create_some_builder():
204     branch = models.Branch.create_if_possible('some-branch', 'Some Branch')
205     platform = models.Platform.create_if_possible('some-platform', 'Some Platform')
206     builder_key = models.Builder.create('some-builder', 'Some Builder')
207     return branch, platform, models.Builder.get(builder_key)
208
209
210 def _create_build(branch, platform, builder, key_name='some-build'):
211     build_key = models.Build(key_name=key_name, branch=branch, platform=platform, builder=builder,
212         buildNumber=1, revision=100, timestamp=datetime.now()).put()
213     return models.Build.get(build_key)
214
215
216 class BuildTests(DataStoreTestsBase):
217     def test_get_or_insert_from_log(self):
218         branch, platform, builder = _create_some_builder()
219
220         timestamp = datetime.now().replace(microsecond=0)
221         log = models.ReportLog(timestamp=timestamp, headers='some headers',
222             payload='{"branch": "some-branch", "platform": "some-platform", "builder-name": "some-builder",' +
223                 '"build-number": 123, "webkit-revision": 456, "timestamp": %d}' % int(mktime(timestamp.timetuple())))
224
225         self.assertThereIsNoInstanceOf(models.Build)
226
227         build = models.Build.get_or_insert_from_log(log)
228         self.assertTrue(build)
229         self.assertEqual(build.branch.key(), branch.key())
230         self.assertEqual(build.platform.key(), platform.key())
231         self.assertEqual(build.builder.key(), builder.key())
232         self.assertEqual(build.buildNumber, 123)
233         self.assertEqual(build.revision, 456)
234         self.assertEqual(build.chromiumRevision, None)
235         self.assertEqual(build.timestamp, timestamp)
236
237         self.assertOnlyInstance(build)
238
239
240 class TestModelTests(DataStoreTestsBase):
241     def test_update_or_insert(self):
242         branch = models.Branch.create_if_possible('some-branch', 'Some Branch')
243         platform = models.Platform.create_if_possible('some-platform', 'Some Platform')
244
245         self.assertThereIsNoInstanceOf(models.Test)
246
247         test = models.Test.update_or_insert('some-test', branch, platform)
248         self.assertTrue(test)
249         self.assertEqual(test.branches, [branch.key()])
250         self.assertEqual(test.platforms, [platform.key()])
251         self.assertOnlyInstance(test)
252
253     def test_update_or_insert_to_update(self):
254         branch = models.Branch.create_if_possible('some-branch', 'Some Branch')
255         platform = models.Platform.create_if_possible('some-platform', 'Some Platform')
256         test = models.Test.update_or_insert('some-test', branch, platform)
257         self.assertOnlyInstance(test)
258
259         other_branch = models.Branch.create_if_possible('other-branch', 'Other Branch')
260         other_platform = models.Platform.create_if_possible('other-platform', 'Other Platform')
261         test = models.Test.update_or_insert('some-test', other_branch, other_platform)
262         self.assertOnlyInstance(test)
263         self.assertEqualUnorderedList(test.branches, [branch.key(), other_branch.key()])
264         self.assertEqualUnorderedList(test.platforms, [platform.key(), other_platform.key()])
265
266         test = models.Test.get(test.key())
267         self.assertEqualUnorderedList(test.branches, [branch.key(), other_branch.key()])
268         self.assertEqualUnorderedList(test.platforms, [platform.key(), other_platform.key()])
269
270     def test_merge(self):
271         branch, platform, builder = _create_some_builder()
272         some_build = _create_build(branch, platform, builder)
273         some_result = models.TestResult.get_or_insert_from_parsed_json('some-test', some_build, 50)
274         some_test = models.Test.update_or_insert('some-test', branch, platform)
275
276         other_build = _create_build(branch, platform, builder, 'other-build')
277         other_result = models.TestResult.get_or_insert_from_parsed_json('other-test', other_build, 30)
278         other_test = models.Test.update_or_insert('other-test', branch, platform)
279
280         self.assertOnlyInstances([some_result, other_result])
281         self.assertNotEqual(some_result.key(), other_result.key())
282         self.assertOnlyInstances([some_test, other_test])
283
284         self.assertRaises(AssertionError, some_test.merge, (some_test))
285         self.assertOnlyInstances([some_test, other_test])
286
287         some_test.merge(other_test)
288         results_for_some_test = models.TestResult.all()
289         results_for_some_test.filter('name =', 'some-test')
290         results_for_some_test = results_for_some_test.fetch(5)
291         self.assertEqual(len(results_for_some_test), 2)
292
293         self.assertEqual(results_for_some_test[0].name, 'some-test')
294         self.assertEqual(results_for_some_test[1].name, 'some-test')
295
296         if results_for_some_test[0].value == 50:
297             self.assertEqual(results_for_some_test[1].value, 30)
298         else:
299             self.assertEqual(results_for_some_test[1].value, 50)
300
301
302 class TestResultTests(DataStoreTestsBase):
303     def test_get_or_insert_value(self):
304         branch, platform, builder = _create_some_builder()
305         build = _create_build(branch, platform, builder)
306         self.assertThereIsNoInstanceOf(models.TestResult)
307         result = models.TestResult.get_or_insert_from_parsed_json('some-test', build, 50)
308         self.assertOnlyInstance(result)
309         self.assertEqual(result.name, 'some-test')
310         self.assertEqual(result.build.key(), build.key())
311         self.assertEqual(result.value, 50.0)
312         self.assertEqual(result.valueMedian, None)
313         self.assertEqual(result.valueStdev, None)
314         self.assertEqual(result.valueMin, None)
315         self.assertEqual(result.valueMax, None)
316
317     def test_get_or_insert_stat_value(self):
318         branch, platform, builder = _create_some_builder()
319         build = _create_build(branch, platform, builder)
320         self.assertThereIsNoInstanceOf(models.TestResult)
321         result = models.TestResult.get_or_insert_from_parsed_json('some-test', build,
322             {"avg": 40, "median": "40.1", "stdev": 3.25, "min": 30.5, "max": 45})
323         self.assertOnlyInstance(result)
324         self.assertEqual(result.name, 'some-test')
325         self.assertEqual(result.build.key(), build.key())
326         self.assertEqual(result.value, 40.0)
327         self.assertEqual(result.valueMedian, 40.1)
328         self.assertEqual(result.valueStdev, 3.25)
329         self.assertEqual(result.valueMin, 30.5)
330         self.assertEqual(result.valueMax, 45)
331
332     def test_replace_to_change_test_name(self):
333         branch, platform, builder = _create_some_builder()
334         build = _create_build(branch, platform, builder)
335         self.assertThereIsNoInstanceOf(models.TestResult)
336         result = models.TestResult.get_or_insert_from_parsed_json('some-test', build, 50)
337         self.assertOnlyInstance(result)
338         self.assertEqual(result.name, 'some-test')
339
340         new_result = result.replace_to_change_test_name('other-test')
341         self.assertNotEqual(result, new_result)
342         self.assertOnlyInstance(new_result)
343
344         self.assertEqual(new_result.name, 'other-test')
345         self.assertEqual(new_result.build.key(), result.build.key())
346         self.assertEqual(new_result.value, result.value)
347         self.assertEqual(new_result.valueMedian, None)
348         self.assertEqual(new_result.valueStdev, None)
349         self.assertEqual(new_result.valueMin, None)
350         self.assertEqual(new_result.valueMax, None)
351
352     def test_replace_to_change_test_name_with_stat_value(self):
353         branch, platform, builder = _create_some_builder()
354         build = _create_build(branch, platform, builder)
355         self.assertThereIsNoInstanceOf(models.TestResult)
356         result = models.TestResult.get_or_insert_from_parsed_json('some-test', build,
357             {"avg": 40, "median": "40.1", "stdev": 3.25, "min": 30.5, "max": 45})
358         self.assertOnlyInstance(result)
359         self.assertEqual(result.name, 'some-test')
360
361         new_result = result.replace_to_change_test_name('other-test')
362         self.assertNotEqual(result, new_result)
363         self.assertOnlyInstance(new_result)
364
365         self.assertEqual(new_result.name, 'other-test')
366         self.assertEqual(new_result.build.key(), result.build.key())
367         self.assertEqual(new_result.value, result.value)
368         self.assertEqual(result.value, 40.0)
369         self.assertEqual(result.valueMedian, 40.1)
370         self.assertEqual(result.valueStdev, 3.25)
371         self.assertEqual(result.valueMin, 30.5)
372         self.assertEqual(result.valueMax, 45)
373
374     def test_replace_to_change_test_name_overrides_conflicting_result(self):
375         branch, platform, builder = _create_some_builder()
376         build = _create_build(branch, platform, builder)
377         self.assertThereIsNoInstanceOf(models.TestResult)
378         result = models.TestResult.get_or_insert_from_parsed_json('some-test', build, 20)
379         self.assertOnlyInstance(result)
380
381         conflicting_result = models.TestResult.get_or_insert_from_parsed_json('other-test', build, 10)
382
383         new_result = result.replace_to_change_test_name('other-test')
384         self.assertNotEqual(result, new_result)
385         self.assertOnlyInstance(new_result)
386
387         self.assertEqual(new_result.name, 'other-test')
388         self.assertEqual(models.TestResult.get(conflicting_result.key()).value, 20)
389
390
391 class ReportLogTests(DataStoreTestsBase):
392     def _create_log_with_payload(self, payload):
393         return models.ReportLog(timestamp=datetime.now(), headers='some headers', payload=payload)
394
395     def test_parsed_payload(self):
396         log = self._create_log_with_payload('')
397         self.assertFalse('_parsed' in log.__dict__)
398         self.assertEqual(log._parsed_payload(), False)
399         self.assertEqual(log._parsed, False)
400
401         log = self._create_log_with_payload('{"key": "value", "another key": 1}')
402         self.assertEqual(log._parsed_payload(), {"key": "value", "another key": 1})
403         self.assertEqual(log._parsed, {"key": "value", "another key": 1})
404
405     def test_get_value(self):
406         log = self._create_log_with_payload('{"string": "value", "integer": 1, "float": 1.1}')
407         self.assertEqual(log.get_value('string'), 'value')
408         self.assertEqual(log.get_value('integer'), 1)
409         self.assertEqual(log.get_value('float'), 1.1)
410         self.assertEqual(log.get_value('bad'), None)
411
412     def test_results(self):
413         log = self._create_log_with_payload('{"results": 123}')
414         self.assertEqual(log.results(), 123)
415
416         log = self._create_log_with_payload('{"key": "value"}')
417         self.assertEqual(log.results(), None)
418
419     def test_builder(self):
420         log = self._create_log_with_payload('{"key": "value"}')
421         self.assertEqual(log.builder(), None)
422
423         builder_name = "Chromium Mac Release (Perf)"
424         log = self._create_log_with_payload('{"builder-name": "%s"}' % builder_name)
425         self.assertEqual(log.builder(), None)
426
427         builder_key = models.Builder.create(builder_name, 'some password')
428         log = self._create_log_with_payload('{"builder-name": "%s"}' % builder_name)
429         self.assertEqual(log.builder().key(), builder_key)
430
431     def test_branch(self):
432         log = self._create_log_with_payload('{"key": "value"}')
433         self.assertEqual(log.branch(), None)
434
435         log = self._create_log_with_payload('{"branch": "some-branch"}')
436         self.assertEqual(log.branch(), None)
437
438         branch = models.Branch.create_if_possible("some-branch", "Some Branch")
439         log = self._create_log_with_payload('{"branch": "some-branch"}')
440         self.assertEqual(log.branch().key(), branch.key())
441
442     def test_platform(self):
443         log = self._create_log_with_payload('{"key": "value"}')
444         self.assertEqual(log.platform(), None)
445
446         log = self._create_log_with_payload('{"platform": "some-platform"}')
447         self.assertEqual(log.platform(), None)
448
449         platform = models.Platform.create_if_possible("some-platform", "Some Platform")
450         log = self._create_log_with_payload('{"platform": "some-platform"}')
451         self.assertEqual(log.platform().key(), platform.key())
452
453     def test_build_number(self):
454         log = self._create_log_with_payload('{"build-number": 123}')
455         self.assertEqual(log.build_number(), 123)
456
457         log = self._create_log_with_payload('{"key": "value"}')
458         self.assertEqual(log.build_number(), None)
459
460     def test_webkit_revision(self):
461         log = self._create_log_with_payload('{"key": "value"}')
462         self.assertEqual(log.webkit_revision(), None)
463
464         log = self._create_log_with_payload('{"webkit-revision": 123}')
465         self.assertEqual(log.webkit_revision(), 123)
466
467     def chromium_revision(self):
468         log = self._create_log_with_payload('{"chromium-revision": 123}')
469         self.assertEqual(log.webkit_revision(), 123)
470
471         log = self._create_log_with_payload('{"key": "value"}')
472         self.assertEqual(log.webkit_revision(), None)
473
474
475 class PersistentCacheTests(DataStoreTestsBase):
476     def setUp(self):
477         self.testbed = testbed.Testbed()
478         self.testbed.activate()
479         self.testbed.init_datastore_v3_stub()
480         self.testbed.init_memcache_stub()
481
482     def _assert_persistent_cache(self, name, value):
483         self.assertEqual(models.PersistentCache.get_by_key_name(name).value, value)
484         self.assertEqual(memcache.get(name), value)
485
486     def test_set(self):
487         self.assertThereIsNoInstanceOf(models.PersistentCache)
488
489         models.PersistentCache.set_cache('some-cache', 'some data')
490         self._assert_persistent_cache('some-cache', 'some data')
491
492         models.PersistentCache.set_cache('some-cache', 'some other data')
493
494         self._assert_persistent_cache('some-cache', 'some other data')
495
496     def test_get(self):
497         self.assertEqual(memcache.get('some-cache'), None)
498         self.assertEqual(models.PersistentCache.get_cache('some-cache'), None)
499
500         models.PersistentCache.set_cache('some-cache', 'some data')
501
502         self.assertEqual(memcache.get('some-cache'), 'some data')
503         self.assertEqual(models.PersistentCache.get_cache('some-cache'), 'some data')
504
505         memcache.delete('some-cache')
506         self.assertEqual(memcache.get('some-cache'), None)
507         self.assertEqual(models.PersistentCache.get_cache('some-cache'), 'some data')
508
509
510 if __name__ == '__main__':
511     unittest.main()