| # Copyright 2021 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Test time stamping in pkg_tar""" |
| |
| import datetime |
| import tarfile |
| import time |
| import unittest |
| import zipfile |
| |
| from bazel_tools.tools.python.runfiles import runfiles |
| |
| # keep in sync with archive.py |
| PORTABLE_MTIME = 946684800 # 2000-01-01 00:00:00.000 UTC |
| |
| |
| class StampTest(unittest.TestCase): |
| """Test for non-epoch time stamps in packages.""" |
| |
| target_mtime = int(time.time()) |
| zip_epoch_dt = datetime.datetime(1980, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc) |
| ZIP_EPOCH = int(zip_epoch_dt.timestamp()) |
| # We allow the stamp on the build file to be within this delta back in |
| # time from now. That allows for the test data to be built early in a clean |
| # CI run and have the test run a few seconds later. Ideally, this would |
| # be no greater than the expected total CI run time, but the extra margin |
| # does not hurt. |
| ALLOWED_DELTA_FROM_NOW = 30000 # seconds |
| |
| def check_mtime(self, mtime, file_path, file_name): |
| """Checks that a time stamp is reasonable. |
| |
| This checks that a timestamp is not 0 or any of the well known EPOCH values, |
| and that it is within some delta from the current time. |
| |
| Args: |
| mtime: timestamp in seconds |
| file_path: path to archive name |
| file_name: file within archive |
| """ |
| if mtime == 0: |
| self.fail('Archive %s contains file %s with mtime == 0' % ( |
| file_path, file_name)) |
| if mtime == PORTABLE_MTIME: |
| self.fail('Archive %s contains file %s with portable mtime' % ( |
| file_path, file_name)) |
| if mtime == StampTest.ZIP_EPOCH: |
| self.fail('Archive %s contains file %s with ZIP epoch' % ( |
| file_path, file_name)) |
| if ((mtime < self.target_mtime - StampTest.ALLOWED_DELTA_FROM_NOW) |
| or (mtime > self.target_mtime)): |
| self.fail( |
| 'Archive %s contains file %s with mtime:%d, expected:%d +/- %d.' % ( |
| file_path, file_name, mtime, self.target_mtime, |
| StampTest.ALLOWED_DELTA_FROM_NOW) + |
| ' This may be a false positive if your build cache is more than' + |
| ' %s seconds old. If so, try bazel clean and rerun the test.' % |
| StampTest.ALLOWED_DELTA_FROM_NOW) |
| |
| def assertTarFilesAreAlmostNew(self, file_name): |
| """Assert that tarfile contains files with an mtime of roughly now. |
| |
| This is used to prove that the test data was a file which was presumably: |
| built with 'stamp=1' or ('stamp=-1' and --stamp) contains files which |
| all have a fairly recent mtime, thus indicating they are "current" time |
| rather than the epoch or some other time. |
| |
| Args: |
| file_name: the path to the TAR file to test. |
| """ |
| file_path = runfiles.Create().Rlocation('rules_pkg/tests/' + file_name) |
| with tarfile.open(file_path, 'r:*') as f: |
| for info in f: |
| self.check_mtime(info.mtime, file_path, info.name) |
| |
| def assertZipFilesAreAlmostNew(self, file_name): |
| """Assert that zipfile contains files with an mtime of roughly now. |
| |
| This is used to prove that the test data was a file which was presumably: |
| built with 'stamp=1' or ('stamp=-1' and --stamp) contains files which |
| all have a fairly recent mtime, thus indicating they are "current" time |
| rather than the epoch or some other time. |
| |
| Args: |
| file_name: the path to the ZIP file to test. |
| """ |
| file_path = runfiles.Create().Rlocation('rules_pkg/tests/' + file_name) |
| target_mtime = int(time.time()) |
| with zipfile.ZipFile(file_path, mode='r') as f: |
| for info in f.infolist(): |
| d = info.date_time |
| dt = datetime.datetime(d[0], d[1], d[2], d[3], d[4], d[5], tzinfo=datetime.timezone.utc) |
| self.check_mtime(int(dt.timestamp()), file_path, info.filename) |
| |
| def test_not_epoch_times_tar(self): |
| self.assertTarFilesAreAlmostNew('stamped_tar.tar') |
| |
| def test_not_epoch_times_zip(self): |
| self.assertZipFilesAreAlmostNew('stamped_zip.zip') |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |