xref: /aosp_15_r20/external/angle/build/android/gyp/util/md5_check_test.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 = [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_newer_than_record=False):
52      output_paths = None
53      if outputs_specified:
54        output_file1 = tempfile.NamedTemporaryFile()
55        if outputs_missing:
56          output_file1.close()  # Gets deleted on close().
57        output_paths = [output_file1.name]
58      if output_newer_than_record:
59        output_mtime = os.path.getmtime(output_file1.name)
60        os.utime(record_path.name, (output_mtime - 1, output_mtime - 1))
61      else:
62        # touch the record file so it doesn't look like it's older that
63        # the output we've just created
64        os.utime(record_path.name, None)
65
66      self.called = False
67      self.changes = None
68      if expected_changes or added_or_modified_only is not None:
69        def MarkCalled(changes):
70          self.called = True
71          self.changes = changes
72      else:
73        def MarkCalled():
74          self.called = True
75
76      md5_check.CallAndRecordIfStale(
77          MarkCalled,
78          record_path=record_path.name,
79          input_paths=input_files,
80          input_strings=input_strings,
81          output_paths=output_paths,
82          force=force,
83          pass_changes=(expected_changes or added_or_modified_only) is not None,
84          track_subpaths_allowlist=zip_paths if track_subentries else None)
85      self.assertEqual(should_call, self.called, message)
86      if expected_changes:
87        description = self.changes.DescribeDifference()
88        self.assertTrue(fnmatch.fnmatch(description, expected_changes),
89                        'Expected %s to match %s' % (
90                        repr(description), repr(expected_changes)))
91      if should_call and added_or_modified_only is not None:
92        self.assertEqual(added_or_modified_only,
93                         self.changes.AddedOrModifiedOnly())
94
95    CheckCallAndRecord(True, 'should call when record doesn\'t exist',
96                       expected_changes='Previous stamp file not found.',
97                       added_or_modified_only=False)
98    CheckCallAndRecord(False, 'should not call when nothing changed')
99    input_files = input_files[::-1]
100    CheckCallAndRecord(False, 'reordering of inputs shouldn\'t trigger call')
101
102    CheckCallAndRecord(False, 'should not call when nothing changed #2',
103                       outputs_specified=True, outputs_missing=False)
104    CheckCallAndRecord(True, 'should call when output missing',
105                       outputs_specified=True, outputs_missing=True,
106                       expected_changes='Outputs do not exist:*',
107                       added_or_modified_only=False)
108    CheckCallAndRecord(True,
109                       'should call when output is newer than record',
110                       expected_changes='Outputs newer than stamp file:*',
111                       outputs_specified=True,
112                       outputs_missing=False,
113                       added_or_modified_only=False,
114                       output_newer_than_record=True)
115    CheckCallAndRecord(True, force=True, message='should call when forced',
116                       expected_changes='force=True',
117                       added_or_modified_only=False)
118
119    input_file1.write(b'some more input')
120    input_file1.flush()
121    CheckCallAndRecord(True, 'changed input file should trigger call',
122                       expected_changes='*Modified: %s' % input_file1.name,
123                       added_or_modified_only=True)
124
125    input_files = input_files[:1]
126    CheckCallAndRecord(True, 'removing file should trigger call',
127                       expected_changes='*Removed: %s' % input_file1.name,
128                       added_or_modified_only=False)
129
130    input_files.append(input_file1.name)
131    CheckCallAndRecord(True, 'added input file should trigger call',
132                       expected_changes='*Added: %s' % input_file1.name,
133                       added_or_modified_only=True)
134
135    input_strings[0] = input_strings[0] + ' a bit longer'
136    CheckCallAndRecord(True, 'changed input string should trigger call',
137                       expected_changes='*Input strings changed*',
138                       added_or_modified_only=False)
139
140    input_strings = input_strings[::-1]
141    CheckCallAndRecord(True, 'reordering of string inputs should trigger call',
142                       expected_changes='*Input strings changed*')
143
144    input_strings = input_strings[:1]
145    CheckCallAndRecord(True, 'removing a string should trigger call')
146
147    input_strings.append('a brand new string')
148    CheckCallAndRecord(
149        True,
150        'added input string should trigger call',
151        added_or_modified_only=False)
152
153    _WriteZipFile(input_file2.name, [('path/1.txt', '1')])
154    CheckCallAndRecord(
155        True,
156        'added subpath should trigger call',
157        expected_changes='*Modified: %s*Subpath added: %s' % (input_file2.name,
158                                                              'path/1.txt'),
159        added_or_modified_only=True,
160        track_subentries=True)
161    _WriteZipFile(input_file2.name, [('path/1.txt', '2')])
162    CheckCallAndRecord(
163        True,
164        'changed subpath should trigger call',
165        expected_changes='*Modified: %s*Subpath modified: %s' %
166        (input_file2.name, 'path/1.txt'),
167        added_or_modified_only=True,
168        track_subentries=True)
169
170    _WriteZipFile(input_file2.name, [])
171    CheckCallAndRecord(True, 'removed subpath should trigger call',
172                       expected_changes='*Modified: %s*Subpath removed: %s' % (
173                                        input_file2.name, 'path/1.txt'),
174                       added_or_modified_only=False)
175
176
177if __name__ == '__main__':
178  unittest.main()
179