• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2013 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import fnmatch
7import os
8import sys
9import tempfile
10import unittest
11import zipfile
12
13sys.path.insert(
14    0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))
15from util import md5_check
16
17
18def _WriteZipFile(path, entries):
19  with zipfile.ZipFile(path, 'w') as zip_file:
20    for subpath, data in entries:
21      zip_file.writestr(subpath, data)
22
23
24class TestMd5Check(unittest.TestCase):
25  def setUp(self):
26    self.called = False
27    self.changes = None
28
29  def testCallAndRecordIfStale(self):
30    input_strings = ['string1', 'string2']
31    input_file1 = tempfile.NamedTemporaryFile(suffix='.txt')
32    input_file2 = tempfile.NamedTemporaryFile(suffix='.zip')
33    file1_contents = b'input file 1'
34    input_file1.write(file1_contents)
35    input_file1.flush()
36    # Test out empty zip file to start.
37    _WriteZipFile(input_file2.name, [])
38    input_files = [input_file1.name, input_file2.name]
39    zip_paths = [os.path.relpath(input_file2.name)]
40
41    record_path = tempfile.NamedTemporaryFile(suffix='.stamp')
42
43    def CheckCallAndRecord(should_call,
44                           message,
45                           force=False,
46                           outputs_specified=False,
47                           outputs_missing=False,
48                           expected_changes=None,
49                           added_or_modified_only=None,
50                           track_subentries=False):
51      output_paths = None
52      if outputs_specified:
53        output_file1 = tempfile.NamedTemporaryFile()
54        if outputs_missing:
55          output_file1.close()  # Gets deleted on close().
56        output_paths = [output_file1.name]
57
58      self.called = False
59      self.changes = None
60      if expected_changes or added_or_modified_only is not None:
61        def MarkCalled(changes):
62          self.called = True
63          self.changes = changes
64      else:
65        def MarkCalled():
66          self.called = True
67
68      md5_check.CallAndRecordIfStale(
69          MarkCalled,
70          record_path=record_path.name,
71          input_paths=input_files,
72          input_strings=input_strings,
73          output_paths=output_paths,
74          force=force,
75          pass_changes=(expected_changes or added_or_modified_only) is not None,
76          track_subpaths_allowlist=zip_paths if track_subentries else None)
77      self.assertEqual(should_call, self.called, message)
78      if expected_changes:
79        description = self.changes.DescribeDifference()
80        self.assertTrue(fnmatch.fnmatch(description, expected_changes),
81                        'Expected %s to match %s' % (
82                        repr(description), repr(expected_changes)))
83      if should_call and added_or_modified_only is not None:
84        self.assertEqual(added_or_modified_only,
85                         self.changes.AddedOrModifiedOnly())
86
87    CheckCallAndRecord(True, 'should call when record doesn\'t exist',
88                       expected_changes='Previous stamp file not found.',
89                       added_or_modified_only=False)
90    CheckCallAndRecord(False, 'should not call when nothing changed')
91    input_files = input_files[::-1]
92    CheckCallAndRecord(False, 'reordering of inputs shouldn\'t trigger call')
93
94    CheckCallAndRecord(False, 'should not call when nothing changed #2',
95                       outputs_specified=True, outputs_missing=False)
96    CheckCallAndRecord(True, 'should call when output missing',
97                       outputs_specified=True, outputs_missing=True,
98                       expected_changes='Outputs do not exist:*',
99                       added_or_modified_only=False)
100    CheckCallAndRecord(True, force=True, message='should call when forced',
101                       expected_changes='force=True',
102                       added_or_modified_only=False)
103
104    input_file1.write(b'some more input')
105    input_file1.flush()
106    CheckCallAndRecord(True,
107                       'changed input file should trigger call',
108                       expected_changes='*Modified: %s' %
109                       os.path.relpath(input_file1.name),
110                       added_or_modified_only=True)
111
112    input_files = input_files[:1]
113    CheckCallAndRecord(True,
114                       'removing file should trigger call',
115                       expected_changes='*Removed: %s' %
116                       os.path.relpath(input_file1.name),
117                       added_or_modified_only=False)
118
119    input_files.append(input_file1.name)
120    CheckCallAndRecord(True,
121                       'added input file should trigger call',
122                       expected_changes='*Added: %s' %
123                       os.path.relpath(input_file1.name),
124                       added_or_modified_only=True)
125
126    input_strings[0] = input_strings[0] + ' a bit longer'
127    CheckCallAndRecord(True, 'changed input string should trigger call',
128                       expected_changes='*Input strings changed*',
129                       added_or_modified_only=False)
130
131    input_strings = input_strings[::-1]
132    CheckCallAndRecord(True, 'reordering of string inputs should trigger call',
133                       expected_changes='*Input strings changed*')
134
135    input_strings = input_strings[:1]
136    CheckCallAndRecord(True, 'removing a string should trigger call')
137
138    input_strings.append('a brand new string')
139    CheckCallAndRecord(
140        True,
141        'added input string should trigger call',
142        added_or_modified_only=False)
143
144    _WriteZipFile(input_file2.name, [('path/1.txt', '1')])
145    CheckCallAndRecord(True,
146                       'added subpath should trigger call',
147                       expected_changes='*Modified: %s*Subpath added: %s' %
148                       (os.path.relpath(input_file2.name), 'path/1.txt'),
149                       added_or_modified_only=True,
150                       track_subentries=True)
151    _WriteZipFile(input_file2.name, [('path/1.txt', '2')])
152    CheckCallAndRecord(True,
153                       'changed subpath should trigger call',
154                       expected_changes='*Modified: %s*Subpath modified: %s' %
155                       (os.path.relpath(input_file2.name), 'path/1.txt'),
156                       added_or_modified_only=True,
157                       track_subentries=True)
158
159    _WriteZipFile(input_file2.name, [])
160    CheckCallAndRecord(True,
161                       'removed subpath should trigger call',
162                       expected_changes='*Modified: %s*Subpath removed: %s' %
163                       (os.path.relpath(input_file2.name), 'path/1.txt'),
164                       added_or_modified_only=False)
165
166
167if __name__ == '__main__':
168  unittest.main()
169