From 3f4db4a0bab073b768fae958e93288bd5d24eadd Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 10 Sep 2019 17:14:11 +0100 Subject: [PATCH] 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 +++++++++++ .../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 f9ee740eb6fa..99d599eac79a 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 000000000000..6b9147db76e1 --- /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 0000000000000000000000000000000000000000..82b03cf39d919d9de05866b4f7cdf1dfe47e6eb3 GIT binary patch literal 978 zc-nnZ-H*~h5HFr$vQIwftC=g86bv7l=*!&^!+9kV6bM(0ilkZE?Xj`lHrs_m2@n1+ zCjOZ^OIwaJ#(9|8o&EiGJ}bwqITx)tO&6p$aP0ADY@>E3gGA}Z3Lio)7*Kh=jF?bO zGh?o~f|n%V#Qtc%nK+$`cQ)qn+{72={WpV#4GlOyw+EN*u?85la*5F=)ePONjPrA710M$;67wSWKWdx0V*&Q;Wt>O*@yxzabEgAorCsJ z=`Hw*HA&tCt4mk0mbn7>~=m3$34e(2b1>~_Sl@&2&q&m z(8)fZu*qZT~fWzz(tk-H-v^^fj13@uN-JRA1h*H^!D zDH)HAM#*?&*hVIWt$@R@zFiM@MXFdwO;s#0rXC$uvm0j?mHbrzHvSdHE!}&1bT_h{ u2Pql1^7Dt*jZwc`uIH|TW*k5Z+sf@ax5#^_?XLsesQ;jxol84>hW!J=MGy`E literal 0 Hc-jL100001 diff --git a/Lib/test/ziptestdata/exe_with_zip b/Lib/test/ziptestdata/exe_with_zip new file mode 100755 index 0000000000000000000000000000000000000000..c833cdf9f934b08fc449da077efae44aa9a06f18 GIT binary patch literal 990 zc-n91E8r>$q8yxkmyTkg<9%}sE|adgMbiO8++ZXu-De^I!TEt z!7uR4BVWUp@EOe7btx5>U3vC4XME<&)YrEsTx?IMnvzb}a|iu_i`E}@kI;JViMMZW zJhlmsU^Iet*jj+SvHY{=xeep-Q_xc;2;gABGzGgOG?STRXd%g!h6yt+5Tr>$MF=q$ z45&QK6DD-aE|Cka;R%U2ao@Plhu*>AOBZ=Ovhjrl|75V`!WQi9x!ohb*Y^8wy%)VM z;YpTC4XLVjA9)t>m=Pjq!c;~B2K%r)C&b*EJ3OYU#l(!uRE(ToG@LQXBp137IlYVt zE>2-tfzE*{pjGge(m&XpgH?DoOy-k{^Tes}ol z&>h(0CL#5D9S+K%*b&$&LMxc_I5urUGgLTaA<)QlkDhW>uxSCp$nPcYAJYpoG)%$E2}?hmQ|QK zcnB?ZE`-pjVWxB{k(VhiW?YoKWJazfiN)DLZYwt))c(fqu2qe1pN;(w*R_)CHJ<(B PwVMXKf-}JNYZ34#Dk~}N literal 0 Hc-jL100001 diff --git a/Lib/test/ziptestdata/header.sh b/Lib/test/ziptestdata/header.sh new file mode 100755 index 000000000000..52dc91acf740 --- /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 - <