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.
This commit is contained in:
parent
afdeb189e9
commit
3f4db4a0ba
|
@ -5,6 +5,8 @@ import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import posixpath
|
import posixpath
|
||||||
import struct
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
import zipfile
|
import zipfile
|
||||||
|
@ -2470,6 +2472,44 @@ def build_alpharep_fixture():
|
||||||
return zf
|
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):
|
class TestPath(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.fixtures = contextlib.ExitStack()
|
self.fixtures = contextlib.ExitStack()
|
||||||
|
|
|
@ -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 <testdata_module_inside_zip.py >zip64.zip
|
||||||
|
cat header.sh zip64.zip >exe_with_z64
|
||||||
|
rm zip64.zip
|
||||||
|
```
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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 - <<END_OF_PYTHON
|
||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
namespace = {}
|
||||||
|
|
||||||
|
filename = os.environ['EXECUTABLE']
|
||||||
|
print(f'Opening {filename} as a zipfile.')
|
||||||
|
with zipfile.ZipFile(filename, mode='r') as exe_zip:
|
||||||
|
for file_info in exe_zip.infolist():
|
||||||
|
data = exe_zip.read(file_info)
|
||||||
|
exec(data, namespace, namespace)
|
||||||
|
break # Only use the first file in the archive.
|
||||||
|
|
||||||
|
print('Favorite number in executable:', namespace["FAVORITE_NUMBER"])
|
||||||
|
|
||||||
|
### Archive contents will be appended after this file. ###
|
||||||
|
END_OF_PYTHON
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Test data file to be stored within a zip file.
|
||||||
|
FAVORITE_NUMBER = 5
|
Loading…
Reference in New Issue