From 3f4db4a0bab073b768fae958e93288bd5d24eadd Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 10 Sep 2019 17:14:11 +0100 Subject: bpo-28494: Test existing zipfile working behavior. (GH-15853) Add unittests for executables with a zipfile appended to test_zipfile, as zipfile.is_zipfile and zipfile.ZipFile work properly on these today. --- Lib/test/test_zipfile.py | 40 +++++++++++++++++++++ Lib/test/ziptestdata/README.md | 35 ++++++++++++++++++ Lib/test/ziptestdata/exe_with_z64 | Bin 0 -> 978 bytes Lib/test/ziptestdata/exe_with_zip | Bin 0 -> 990 bytes Lib/test/ziptestdata/header.sh | 24 +++++++++++++ Lib/test/ziptestdata/testdata_module_inside_zip.py | 2 ++ 6 files changed, 101 insertions(+) create mode 100644 Lib/test/ziptestdata/README.md create mode 100755 Lib/test/ziptestdata/exe_with_z64 create mode 100755 Lib/test/ziptestdata/exe_with_zip create mode 100755 Lib/test/ziptestdata/header.sh create mode 100644 Lib/test/ziptestdata/testdata_module_inside_zip.py diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index f9ee740..99d599e 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -5,6 +5,8 @@ import os import pathlib import posixpath import struct +import subprocess +import sys import time import unittest import zipfile @@ -2470,6 +2472,44 @@ def build_alpharep_fixture(): return zf +class TestExecutablePrependedZip(unittest.TestCase): + """Test our ability to open zip files with an executable prepended.""" + + def setUp(self): + self.exe_zip = findfile('exe_with_zip', subdir='ziptestdata') + self.exe_zip64 = findfile('exe_with_z64', subdir='ziptestdata') + + def _test_zip_works(self, name): + # bpo-28494 sanity check: ensure is_zipfile works on these. + self.assertTrue(zipfile.is_zipfile(name), + f'is_zipfile failed on {name}') + # Ensure we can operate on these via ZipFile. + with zipfile.ZipFile(name) as zipfp: + for n in zipfp.namelist(): + data = zipfp.read(n) + self.assertIn(b'FAVORITE_NUMBER', data) + + def test_read_zip_with_exe_prepended(self): + self._test_zip_works(self.exe_zip) + + def test_read_zip64_with_exe_prepended(self): + self._test_zip_works(self.exe_zip64) + + @unittest.skipUnless(sys.executable, 'sys.executable required.') + @unittest.skipUnless(os.access('/bin/bash', os.X_OK), + 'Test relies on #!/bin/bash working.') + def test_execute_zip2(self): + output = subprocess.check_output([self.exe_zip, sys.executable]) + self.assertIn(b'number in executable: 5', output) + + @unittest.skipUnless(sys.executable, 'sys.executable required.') + @unittest.skipUnless(os.access('/bin/bash', os.X_OK), + 'Test relies on #!/bin/bash working.') + def test_execute_zip64(self): + output = subprocess.check_output([self.exe_zip64, sys.executable]) + self.assertIn(b'number in executable: 5', output) + + class TestPath(unittest.TestCase): def setUp(self): self.fixtures = contextlib.ExitStack() diff --git a/Lib/test/ziptestdata/README.md b/Lib/test/ziptestdata/README.md new file mode 100644 index 0000000..6b9147d --- /dev/null +++ b/Lib/test/ziptestdata/README.md @@ -0,0 +1,35 @@ +# Test data for `test_zipfile` + +The test executables in this directory are created manually from header.sh and +the `testdata_module_inside_zip.py` file. You must have infozip's zip utility +installed (`apt install zip` on Debian). + +## Purpose + +These are used to test executable files with an appended zipfile, in a scenario +where the executable is _not_ a Python interpreter itself so our automatic +zipimport machinery (that'd look for `__main__.py`) is not being used. + +## Updating the test executables + +If you update header.sh or the testdata_module_inside_zip.py file, rerun the +commands below. These are expected to be rarely changed, if ever. + +### Standard old format (2.0) zip file + +``` +zip -0 zip2.zip testdata_module_inside_zip.py +cat header.sh zip2.zip >exe_with_zip +rm zip2.zip +``` + +### Modern format (4.5) zip64 file + +Redirecting from stdin forces infozip's zip tool to create a zip64. + +``` +zip -0 zip64.zip +cat header.sh zip64.zip >exe_with_z64 +rm zip64.zip +``` + diff --git a/Lib/test/ziptestdata/exe_with_z64 b/Lib/test/ziptestdata/exe_with_z64 new file mode 100755 index 0000000..82b03cf Binary files /dev/null and b/Lib/test/ziptestdata/exe_with_z64 differ diff --git a/Lib/test/ziptestdata/exe_with_zip b/Lib/test/ziptestdata/exe_with_zip new file mode 100755 index 0000000..c833cdf Binary files /dev/null and b/Lib/test/ziptestdata/exe_with_zip differ diff --git a/Lib/test/ziptestdata/header.sh b/Lib/test/ziptestdata/header.sh new file mode 100755 index 0000000..52dc91a --- /dev/null +++ b/Lib/test/ziptestdata/header.sh @@ -0,0 +1,24 @@ +#!/bin/bash +INTERPRETER_UNDER_TEST="$1" +if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then + echo "Interpreter must be the command line argument." + exit 4 +fi +EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - <