11e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
21e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
31e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# found in the LICENSE file.
41e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
51e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import collections
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import os
7a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import sys
81e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import unittest
91e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)PERF_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)sys.path.insert(0, os.path.join(os.path.dirname(PERF_ROOT), 'telemetry'))
121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from telemetry.unittest import system_stub
131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
14a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)sys.path.insert(0, PERF_ROOT)
151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from page_sets import PRESUBMIT
161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)class AffectedFileStub(object):
19c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def __init__(self, absolute_local_path, action):
201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._absolute_local_path = absolute_local_path
21c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    self.action = action
221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def AbsoluteLocalPath(self):
241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return self._absolute_local_path
251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
26c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def Action(self):
27c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return self.action
281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)class InputAPIStub(object):
30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def __init__(self, paths, deleted_paths=None, added_paths=None):
311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._paths = paths
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    self._deleted_paths = deleted_paths if deleted_paths else []
33c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    self._added_paths = added_paths if added_paths else []
34c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
35c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def AffectedFiles(self, include_deletes=True, file_filter=None):
36c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if not file_filter:
37c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      file_filter = lambda x: True
38c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
39c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    affected_files = []
40c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    for path in self._paths:
41c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      affected_file_stub = AffectedFileStub(path, 'M')
42c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      if file_filter(affected_file_stub):
43c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        affected_files.append(affected_file_stub)
44c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
45c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    for path in self._added_paths:
46c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      affected_file_stub = AffectedFileStub(path, 'A')
47c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      if file_filter(affected_file_stub):
48c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        affected_files.append(affected_file_stub)
491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if include_deletes:
51c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      for path in self._deleted_paths:
52c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        affected_file_stub = AffectedFileStub(path, 'D')
53c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        if file_filter(affected_file_stub):
54c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          affected_files.append(affected_file_stub)
551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return affected_files
561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def AbsoluteLocalPaths(self):
581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return [af.AbsoluteLocalPath() for af in self.AffectedFiles()]
591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def PresubmitLocalPath(self):
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return PRESUBMIT.__file__
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)class OutputAPIStub(object):
651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  class PresubmitError(Exception):
661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    pass
671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  class PresubmitNotifyResult(Exception):
691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    pass
701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)PRESUBMIT.LoadSupport(InputAPIStub([]))   # do this to support monkey patching
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)class PresubmitTest(unittest.TestCase):
751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def setUp(self):
761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    success_file_hash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._stubs = system_stub.Override(
7923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        PRESUBMIT, ['cloud_storage', 'os', 'raw_input'])
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._stubs.raw_input.input = 'public'
811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # Files in Cloud Storage.
821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._stubs.cloud_storage.remote_paths = [
831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        'skip'.zfill(40),
841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    ]
851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # Local data files and their hashes.
861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._stubs.cloud_storage.local_file_hashes = {
871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/skip.wpr': 'skip'.zfill(40),
881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/success.wpr': success_file_hash,
891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/wrong_hash.wpr': success_file_hash,
901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # Local data files.
921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._stubs.os.path.files = (
931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        self._stubs.cloud_storage.local_file_hashes.keys())
941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # Local hash files and their contents.
9523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    self._stubs.cloud_storage.local_hash_files = {
961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/invalid_hash.wpr.sha1': 'invalid_hash',
971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/missing.wpr.sha1': 'missing'.zfill(40),
981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/success.wpr.sha1': success_file_hash,
991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/skip.wpr.sha1': 'skip'.zfill(40),
1001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        '/path/to/wrong_hash.wpr.sha1': 'wronghash'.zfill(40),
1011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def tearDown(self):
1041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._stubs.Restore()
1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def assertResultCount(self, results, expected_errors, expected_notifications):
1071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    counts = collections.defaultdict(int)
1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    for result in results:
1091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      counts[type(result)] += 1
1101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    actual_errors = counts[OutputAPIStub.PresubmitError]
1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    actual_notifications = counts[OutputAPIStub.PresubmitNotifyResult]
1121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertEqual(expected_errors, actual_errors,
1131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        msg='Expected %d errors, but got %d. Results: %s' %
1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        (expected_errors, actual_errors, results))
1151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertEqual(expected_notifications, actual_notifications,
1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        msg='Expected %d notifications, but got %d. Results: %s' %
1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        (expected_notifications, actual_notifications, results))
1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
119c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def _CheckUpload(self, paths, deleted_paths=None, added_paths=None):
120c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    input_api = InputAPIStub(paths, deleted_paths, added_paths)
1211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return PRESUBMIT.CheckChangeOnUpload(input_api, OutputAPIStub())
1221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def testIgnoreDeleted(self):
1241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    results = self._CheckUpload([], ['/path/to/deleted.wpr.sha1'])
1251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertResultCount(results, 0, 0)
1261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def testIgnoreNonHashes(self):
1281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    results = self._CheckUpload(['/path/to/irrelevant.py'])
1291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertResultCount(results, 0, 0)
1301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def testInvalidHash(self):
1321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    results = self._CheckUpload(['/path/to/invalid_hash.wpr.sha1'])
1331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertResultCount(results, 1, 0)
1341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertTrue('valid SHA-1 hash' in str(results[0]), msg=results[0])
1351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def testMissingFile(self):
1371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    results = self._CheckUpload(['/path/to/missing.wpr.sha1'])
1381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertResultCount(results, 1, 0)
1391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertTrue('not found' in str(results[0]), msg=results[0])
1401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def testSkip(self):
1421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    results = self._CheckUpload(['/path/to/skip.wpr.sha1'])
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.assertResultCount(results, 0, 0)
1441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def testSuccess(self):
1461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    results = self._CheckUpload(['/path/to/success.wpr.sha1'])
1471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertResultCount(results, 0, 1)
1481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertTrue('Uploaded' in str(results[0]), msg=results[0])
1491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def testWrongHash(self):
1511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    results = self._CheckUpload(['/path/to/wrong_hash.wpr.sha1'])
1521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.assertTrue('does not match' in str(results[0]), msg=results[0])
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)if __name__ == '__main__':
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  unittest.main()
157