text: # Copyright 2019 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.
  
  import datetime
  import filecmp
  import unittest
  import zipfile
  
  from bazel_tools.tools.python.runfiles import runfiles
  
  HELLO_CRC = 2069210904
  LOREM_CRC = 2178844372
  EXECUTABLE_CRC = 342626072
  
  # Unix dir bit and Windows dir bit. Magic from zip spec
  UNIX_DIR_BIT = 0o40000
  MSDOS_DIR_BIT = 0x10
  UNIX_RWX_BITS = 0o777
  
  # The ZIP epoch date: (1980, 1, 1, 0, 0, 0)
  _ZIP_EPOCH_DT = datetime.datetime(1980, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
  _ZIP_EPOCH_S = int(_ZIP_EPOCH_DT.timestamp())
  
  def seconds_to_ziptime(s):
    dt = datetime.datetime.utcfromtimestamp(s)
    return (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
  
  
  class ZipTest(unittest.TestCase):
  
    def get_test_zip(self, zip_file):
      """Get the file path to a generated zip in the runfiles."""
  
      return self.data_files.Rlocation(
          "rules_pkg/tests/zip/" + zip_file
      )
  
    def setUp(self):
      super(ZipTest, self).setUp()
      self.data_files = runfiles.Create()
  
  
  class ZipContentsCase(ZipTest):
    """Use zipfile to check the contents of some generated zip files."""
  
    def assertZipFileContent(self, zip_file, content):
      """Assert that zip_file contains the entries described by content.
  
      Args:
          zip_file: the test-package-relative path to a zip file to test.
          content: an array of dictionaries containing a filename and crc key,
                   and optionally a timestamp key.
      """
      with zipfile.ZipFile(self.get_test_zip(zip_file)) as f:
        infos = f.infolist()
        self.assertEqual(len(infos), len(content))
  
        for info, expected in zip(infos, content):
          self.assertEqual(info.filename, expected["filename"])
          if "crc" in expected:
            self.assertEqual(info.CRC, expected["crc"])
  
          ts = seconds_to_ziptime(expected.get("timestamp", _ZIP_EPOCH_S))
          self.assertEqual(info.date_time, ts)
          if "isdir" in expected:
            expect_dir_bits = UNIX_DIR_BIT << 16 | MSDOS_DIR_BIT
            self.assertEqual(info.external_attr & expect_dir_bits,
                             expect_dir_bits)
          self.assertEqual((info.external_attr >> 16) & UNIX_RWX_BITS,
                           expected.get("attr", 0o555))
  
    def test_empty(self):
      self.assertZipFileContent("test_zip_empty.zip", [])
  
    def test_basic(self):
      self.assertZipFileContent("test_zip_basic.zip", [
          {"filename": "foodir/", "isdir": True, "attr": 0o711},
          {"filename": "hello.txt", "crc": HELLO_CRC},
          {"filename": "loremipsum.txt", "crc": LOREM_CRC},
          {"filename": "usr/bin/foo", "attr": 0o555, "data": "/usr/local/foo/foo.real"},
      ])
  
    def test_timestamp(self):
      self.assertZipFileContent("test_zip_timestamp.zip", [
          {"filename": "hello.txt", "crc": HELLO_CRC, "timestamp": 1234567890},
      ])
  
    def test_permissions(self):
      self.assertZipFileContent("test_zip_permissions.zip", [
          {
              "filename": "executable.sh",
              "crc": EXECUTABLE_CRC,
              "timestamp": 1234567890,
              "attr": 0o644,
          }
      ])
  
    def test_package_dir(self):
      self.assertZipFileContent("test_zip_package_dir0.zip", [
          {"filename": "abc/def/hello.txt", "crc": HELLO_CRC},
          {"filename": "abc/def/loremipsum.txt", "crc": LOREM_CRC},
      ])
  
    def test_zip_strip_prefix_empty(self):
      self.assertZipFileContent("test-zip-strip_prefix-empty.zip", [
          {"filename": "loremipsum.txt", "crc": LOREM_CRC},
      ])
  
    def test_zip_strip_prefix_none(self):
      self.assertZipFileContent("test-zip-strip_prefix-none.zip", [
          {"filename": "loremipsum.txt", "crc": LOREM_CRC},
      ])
  
    def test_zip_strip_prefix_zipcontent(self):
      self.assertZipFileContent("test-zip-strip_prefix-zipcontent.zip", [
          {"filename": "loremipsum.txt", "crc": LOREM_CRC},
      ])
  
    def test_zip_strip_prefix_dot(self):
      self.assertZipFileContent("test-zip-strip_prefix-dot.zip", [
          {"filename": "zipcontent/loremipsum.txt", "crc": LOREM_CRC},
      ])
  
  
  class ZipEquivalency(ZipTest):
    """Check that some generated zip files are equivalent to each-other."""
  
    def assertFilesEqual(self, actual, expected):
      """Assert that two zip files contain the same bytes."""
  
      zips_are_equal = filecmp.cmp(
          self.get_test_zip(actual),
          self.get_test_zip(expected),
      )
      self.assertTrue(zips_are_equal)
  
    def test_small_timestamp(self):
      self.assertFilesEqual(
          "test_zip_basic_timestamp_before_epoch.zip",
          "test_zip_basic.zip",
      )
  
    def test_extension(self):
      self.assertFilesEqual(
          "test_zip_basic_renamed.foo",
          "test_zip_basic.zip",
      )
  
    def test_package_dir1(self):
      self.assertFilesEqual(
          "test_zip_package_dir1.zip",
          "test_zip_package_dir0.zip",
      )
  
    def test_package_dir2(self):
      self.assertFilesEqual(
          "test_zip_package_dir2.zip",
          "test_zip_package_dir0.zip",
      )
  
  if __name__ == "__main__":
    unittest.main()
  