17db96d56Sopenharmony_ci"""Test harness for the zipapp module.""" 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciimport io 47db96d56Sopenharmony_ciimport pathlib 57db96d56Sopenharmony_ciimport stat 67db96d56Sopenharmony_ciimport sys 77db96d56Sopenharmony_ciimport tempfile 87db96d56Sopenharmony_ciimport unittest 97db96d56Sopenharmony_ciimport zipapp 107db96d56Sopenharmony_ciimport zipfile 117db96d56Sopenharmony_cifrom test.support import requires_zlib 127db96d56Sopenharmony_cifrom test.support import os_helper 137db96d56Sopenharmony_ci 147db96d56Sopenharmony_cifrom unittest.mock import patch 157db96d56Sopenharmony_ci 167db96d56Sopenharmony_ciclass ZipAppTest(unittest.TestCase): 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ci """Test zipapp module functionality.""" 197db96d56Sopenharmony_ci 207db96d56Sopenharmony_ci def setUp(self): 217db96d56Sopenharmony_ci tmpdir = tempfile.TemporaryDirectory() 227db96d56Sopenharmony_ci self.addCleanup(tmpdir.cleanup) 237db96d56Sopenharmony_ci self.tmpdir = pathlib.Path(tmpdir.name) 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ci def test_create_archive(self): 267db96d56Sopenharmony_ci # Test packing a directory. 277db96d56Sopenharmony_ci source = self.tmpdir / 'source' 287db96d56Sopenharmony_ci source.mkdir() 297db96d56Sopenharmony_ci (source / '__main__.py').touch() 307db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 317db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target)) 327db96d56Sopenharmony_ci self.assertTrue(target.is_file()) 337db96d56Sopenharmony_ci 347db96d56Sopenharmony_ci def test_create_archive_with_pathlib(self): 357db96d56Sopenharmony_ci # Test packing a directory using Path objects for source and target. 367db96d56Sopenharmony_ci source = self.tmpdir / 'source' 377db96d56Sopenharmony_ci source.mkdir() 387db96d56Sopenharmony_ci (source / '__main__.py').touch() 397db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 407db96d56Sopenharmony_ci zipapp.create_archive(source, target) 417db96d56Sopenharmony_ci self.assertTrue(target.is_file()) 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci def test_create_archive_with_subdirs(self): 447db96d56Sopenharmony_ci # Test packing a directory includes entries for subdirectories. 457db96d56Sopenharmony_ci source = self.tmpdir / 'source' 467db96d56Sopenharmony_ci source.mkdir() 477db96d56Sopenharmony_ci (source / '__main__.py').touch() 487db96d56Sopenharmony_ci (source / 'foo').mkdir() 497db96d56Sopenharmony_ci (source / 'bar').mkdir() 507db96d56Sopenharmony_ci (source / 'foo' / '__init__.py').touch() 517db96d56Sopenharmony_ci target = io.BytesIO() 527db96d56Sopenharmony_ci zipapp.create_archive(str(source), target) 537db96d56Sopenharmony_ci target.seek(0) 547db96d56Sopenharmony_ci with zipfile.ZipFile(target, 'r') as z: 557db96d56Sopenharmony_ci self.assertIn('foo/', z.namelist()) 567db96d56Sopenharmony_ci self.assertIn('bar/', z.namelist()) 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_ci def test_create_archive_with_filter(self): 597db96d56Sopenharmony_ci # Test packing a directory and using filter to specify 607db96d56Sopenharmony_ci # which files to include. 617db96d56Sopenharmony_ci def skip_pyc_files(path): 627db96d56Sopenharmony_ci return path.suffix != '.pyc' 637db96d56Sopenharmony_ci source = self.tmpdir / 'source' 647db96d56Sopenharmony_ci source.mkdir() 657db96d56Sopenharmony_ci (source / '__main__.py').touch() 667db96d56Sopenharmony_ci (source / 'test.py').touch() 677db96d56Sopenharmony_ci (source / 'test.pyc').touch() 687db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 697db96d56Sopenharmony_ci 707db96d56Sopenharmony_ci zipapp.create_archive(source, target, filter=skip_pyc_files) 717db96d56Sopenharmony_ci with zipfile.ZipFile(target, 'r') as z: 727db96d56Sopenharmony_ci self.assertIn('__main__.py', z.namelist()) 737db96d56Sopenharmony_ci self.assertIn('test.py', z.namelist()) 747db96d56Sopenharmony_ci self.assertNotIn('test.pyc', z.namelist()) 757db96d56Sopenharmony_ci 767db96d56Sopenharmony_ci def test_create_archive_filter_exclude_dir(self): 777db96d56Sopenharmony_ci # Test packing a directory and using a filter to exclude a 787db96d56Sopenharmony_ci # subdirectory (ensures that the path supplied to include 797db96d56Sopenharmony_ci # is relative to the source location, as expected). 807db96d56Sopenharmony_ci def skip_dummy_dir(path): 817db96d56Sopenharmony_ci return path.parts[0] != 'dummy' 827db96d56Sopenharmony_ci source = self.tmpdir / 'source' 837db96d56Sopenharmony_ci source.mkdir() 847db96d56Sopenharmony_ci (source / '__main__.py').touch() 857db96d56Sopenharmony_ci (source / 'test.py').touch() 867db96d56Sopenharmony_ci (source / 'dummy').mkdir() 877db96d56Sopenharmony_ci (source / 'dummy' / 'test2.py').touch() 887db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 897db96d56Sopenharmony_ci 907db96d56Sopenharmony_ci zipapp.create_archive(source, target, filter=skip_dummy_dir) 917db96d56Sopenharmony_ci with zipfile.ZipFile(target, 'r') as z: 927db96d56Sopenharmony_ci self.assertEqual(len(z.namelist()), 2) 937db96d56Sopenharmony_ci self.assertIn('__main__.py', z.namelist()) 947db96d56Sopenharmony_ci self.assertIn('test.py', z.namelist()) 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_ci def test_create_archive_default_target(self): 977db96d56Sopenharmony_ci # Test packing a directory to the default name. 987db96d56Sopenharmony_ci source = self.tmpdir / 'source' 997db96d56Sopenharmony_ci source.mkdir() 1007db96d56Sopenharmony_ci (source / '__main__.py').touch() 1017db96d56Sopenharmony_ci zipapp.create_archive(str(source)) 1027db96d56Sopenharmony_ci expected_target = self.tmpdir / 'source.pyz' 1037db96d56Sopenharmony_ci self.assertTrue(expected_target.is_file()) 1047db96d56Sopenharmony_ci 1057db96d56Sopenharmony_ci @requires_zlib() 1067db96d56Sopenharmony_ci def test_create_archive_with_compression(self): 1077db96d56Sopenharmony_ci # Test packing a directory into a compressed archive. 1087db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1097db96d56Sopenharmony_ci source.mkdir() 1107db96d56Sopenharmony_ci (source / '__main__.py').touch() 1117db96d56Sopenharmony_ci (source / 'test.py').touch() 1127db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci zipapp.create_archive(source, target, compressed=True) 1157db96d56Sopenharmony_ci with zipfile.ZipFile(target, 'r') as z: 1167db96d56Sopenharmony_ci for name in ('__main__.py', 'test.py'): 1177db96d56Sopenharmony_ci self.assertEqual(z.getinfo(name).compress_type, 1187db96d56Sopenharmony_ci zipfile.ZIP_DEFLATED) 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_ci def test_no_main(self): 1217db96d56Sopenharmony_ci # Test that packing a directory with no __main__.py fails. 1227db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1237db96d56Sopenharmony_ci source.mkdir() 1247db96d56Sopenharmony_ci (source / 'foo.py').touch() 1257db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1267db96d56Sopenharmony_ci with self.assertRaises(zipapp.ZipAppError): 1277db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target)) 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_ci def test_main_and_main_py(self): 1307db96d56Sopenharmony_ci # Test that supplying a main argument with __main__.py fails. 1317db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1327db96d56Sopenharmony_ci source.mkdir() 1337db96d56Sopenharmony_ci (source / '__main__.py').touch() 1347db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1357db96d56Sopenharmony_ci with self.assertRaises(zipapp.ZipAppError): 1367db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), main='pkg.mod:fn') 1377db96d56Sopenharmony_ci 1387db96d56Sopenharmony_ci def test_main_written(self): 1397db96d56Sopenharmony_ci # Test that the __main__.py is written correctly. 1407db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1417db96d56Sopenharmony_ci source.mkdir() 1427db96d56Sopenharmony_ci (source / 'foo.py').touch() 1437db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1447db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), main='pkg.mod:fn') 1457db96d56Sopenharmony_ci with zipfile.ZipFile(str(target), 'r') as z: 1467db96d56Sopenharmony_ci self.assertIn('__main__.py', z.namelist()) 1477db96d56Sopenharmony_ci self.assertIn(b'pkg.mod.fn()', z.read('__main__.py')) 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci def test_main_only_written_once(self): 1507db96d56Sopenharmony_ci # Test that we don't write multiple __main__.py files. 1517db96d56Sopenharmony_ci # The initial implementation had this bug; zip files allow 1527db96d56Sopenharmony_ci # multiple entries with the same name 1537db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1547db96d56Sopenharmony_ci source.mkdir() 1557db96d56Sopenharmony_ci # Write 2 files, as the original bug wrote __main__.py 1567db96d56Sopenharmony_ci # once for each file written :-( 1577db96d56Sopenharmony_ci # See http://bugs.python.org/review/23491/diff/13982/Lib/zipapp.py#newcode67Lib/zipapp.py:67 1587db96d56Sopenharmony_ci # (line 67) 1597db96d56Sopenharmony_ci (source / 'foo.py').touch() 1607db96d56Sopenharmony_ci (source / 'bar.py').touch() 1617db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1627db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), main='pkg.mod:fn') 1637db96d56Sopenharmony_ci with zipfile.ZipFile(str(target), 'r') as z: 1647db96d56Sopenharmony_ci self.assertEqual(1, z.namelist().count('__main__.py')) 1657db96d56Sopenharmony_ci 1667db96d56Sopenharmony_ci def test_main_validation(self): 1677db96d56Sopenharmony_ci # Test that invalid values for main are rejected. 1687db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1697db96d56Sopenharmony_ci source.mkdir() 1707db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1717db96d56Sopenharmony_ci problems = [ 1727db96d56Sopenharmony_ci '', 'foo', 'foo:', ':bar', '12:bar', 'a.b.c.:d', 1737db96d56Sopenharmony_ci '.a:b', 'a:b.', 'a:.b', 'a:silly name' 1747db96d56Sopenharmony_ci ] 1757db96d56Sopenharmony_ci for main in problems: 1767db96d56Sopenharmony_ci with self.subTest(main=main): 1777db96d56Sopenharmony_ci with self.assertRaises(zipapp.ZipAppError): 1787db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), main=main) 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci def test_default_no_shebang(self): 1817db96d56Sopenharmony_ci # Test that no shebang line is written to the target by default. 1827db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1837db96d56Sopenharmony_ci source.mkdir() 1847db96d56Sopenharmony_ci (source / '__main__.py').touch() 1857db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1867db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target)) 1877db96d56Sopenharmony_ci with target.open('rb') as f: 1887db96d56Sopenharmony_ci self.assertNotEqual(f.read(2), b'#!') 1897db96d56Sopenharmony_ci 1907db96d56Sopenharmony_ci def test_custom_interpreter(self): 1917db96d56Sopenharmony_ci # Test that a shebang line with a custom interpreter is written 1927db96d56Sopenharmony_ci # correctly. 1937db96d56Sopenharmony_ci source = self.tmpdir / 'source' 1947db96d56Sopenharmony_ci source.mkdir() 1957db96d56Sopenharmony_ci (source / '__main__.py').touch() 1967db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 1977db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), interpreter='python') 1987db96d56Sopenharmony_ci with target.open('rb') as f: 1997db96d56Sopenharmony_ci self.assertEqual(f.read(2), b'#!') 2007db96d56Sopenharmony_ci self.assertEqual(b'python\n', f.readline()) 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci def test_pack_to_fileobj(self): 2037db96d56Sopenharmony_ci # Test that we can pack to a file object. 2047db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2057db96d56Sopenharmony_ci source.mkdir() 2067db96d56Sopenharmony_ci (source / '__main__.py').touch() 2077db96d56Sopenharmony_ci target = io.BytesIO() 2087db96d56Sopenharmony_ci zipapp.create_archive(str(source), target, interpreter='python') 2097db96d56Sopenharmony_ci self.assertTrue(target.getvalue().startswith(b'#!python\n')) 2107db96d56Sopenharmony_ci 2117db96d56Sopenharmony_ci def test_read_shebang(self): 2127db96d56Sopenharmony_ci # Test that we can read the shebang line correctly. 2137db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2147db96d56Sopenharmony_ci source.mkdir() 2157db96d56Sopenharmony_ci (source / '__main__.py').touch() 2167db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 2177db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), interpreter='python') 2187db96d56Sopenharmony_ci self.assertEqual(zipapp.get_interpreter(str(target)), 'python') 2197db96d56Sopenharmony_ci 2207db96d56Sopenharmony_ci def test_read_missing_shebang(self): 2217db96d56Sopenharmony_ci # Test that reading the shebang line of a file without one returns None. 2227db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2237db96d56Sopenharmony_ci source.mkdir() 2247db96d56Sopenharmony_ci (source / '__main__.py').touch() 2257db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 2267db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target)) 2277db96d56Sopenharmony_ci self.assertEqual(zipapp.get_interpreter(str(target)), None) 2287db96d56Sopenharmony_ci 2297db96d56Sopenharmony_ci def test_modify_shebang(self): 2307db96d56Sopenharmony_ci # Test that we can change the shebang of a file. 2317db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2327db96d56Sopenharmony_ci source.mkdir() 2337db96d56Sopenharmony_ci (source / '__main__.py').touch() 2347db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 2357db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), interpreter='python') 2367db96d56Sopenharmony_ci new_target = self.tmpdir / 'changed.pyz' 2377db96d56Sopenharmony_ci zipapp.create_archive(str(target), str(new_target), interpreter='python2.7') 2387db96d56Sopenharmony_ci self.assertEqual(zipapp.get_interpreter(str(new_target)), 'python2.7') 2397db96d56Sopenharmony_ci 2407db96d56Sopenharmony_ci def test_write_shebang_to_fileobj(self): 2417db96d56Sopenharmony_ci # Test that we can change the shebang of a file, writing the result to a 2427db96d56Sopenharmony_ci # file object. 2437db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2447db96d56Sopenharmony_ci source.mkdir() 2457db96d56Sopenharmony_ci (source / '__main__.py').touch() 2467db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 2477db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), interpreter='python') 2487db96d56Sopenharmony_ci new_target = io.BytesIO() 2497db96d56Sopenharmony_ci zipapp.create_archive(str(target), new_target, interpreter='python2.7') 2507db96d56Sopenharmony_ci self.assertTrue(new_target.getvalue().startswith(b'#!python2.7\n')) 2517db96d56Sopenharmony_ci 2527db96d56Sopenharmony_ci def test_read_from_pathobj(self): 2537db96d56Sopenharmony_ci # Test that we can copy an archive using a pathlib.Path object 2547db96d56Sopenharmony_ci # for the source. 2557db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2567db96d56Sopenharmony_ci source.mkdir() 2577db96d56Sopenharmony_ci (source / '__main__.py').touch() 2587db96d56Sopenharmony_ci target1 = self.tmpdir / 'target1.pyz' 2597db96d56Sopenharmony_ci target2 = self.tmpdir / 'target2.pyz' 2607db96d56Sopenharmony_ci zipapp.create_archive(source, target1, interpreter='python') 2617db96d56Sopenharmony_ci zipapp.create_archive(target1, target2, interpreter='python2.7') 2627db96d56Sopenharmony_ci self.assertEqual(zipapp.get_interpreter(target2), 'python2.7') 2637db96d56Sopenharmony_ci 2647db96d56Sopenharmony_ci def test_read_from_fileobj(self): 2657db96d56Sopenharmony_ci # Test that we can copy an archive using an open file object. 2667db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2677db96d56Sopenharmony_ci source.mkdir() 2687db96d56Sopenharmony_ci (source / '__main__.py').touch() 2697db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 2707db96d56Sopenharmony_ci temp_archive = io.BytesIO() 2717db96d56Sopenharmony_ci zipapp.create_archive(str(source), temp_archive, interpreter='python') 2727db96d56Sopenharmony_ci new_target = io.BytesIO() 2737db96d56Sopenharmony_ci temp_archive.seek(0) 2747db96d56Sopenharmony_ci zipapp.create_archive(temp_archive, new_target, interpreter='python2.7') 2757db96d56Sopenharmony_ci self.assertTrue(new_target.getvalue().startswith(b'#!python2.7\n')) 2767db96d56Sopenharmony_ci 2777db96d56Sopenharmony_ci def test_remove_shebang(self): 2787db96d56Sopenharmony_ci # Test that we can remove the shebang from a file. 2797db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2807db96d56Sopenharmony_ci source.mkdir() 2817db96d56Sopenharmony_ci (source / '__main__.py').touch() 2827db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 2837db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), interpreter='python') 2847db96d56Sopenharmony_ci new_target = self.tmpdir / 'changed.pyz' 2857db96d56Sopenharmony_ci zipapp.create_archive(str(target), str(new_target), interpreter=None) 2867db96d56Sopenharmony_ci self.assertEqual(zipapp.get_interpreter(str(new_target)), None) 2877db96d56Sopenharmony_ci 2887db96d56Sopenharmony_ci def test_content_of_copied_archive(self): 2897db96d56Sopenharmony_ci # Test that copying an archive doesn't corrupt it. 2907db96d56Sopenharmony_ci source = self.tmpdir / 'source' 2917db96d56Sopenharmony_ci source.mkdir() 2927db96d56Sopenharmony_ci (source / '__main__.py').touch() 2937db96d56Sopenharmony_ci target = io.BytesIO() 2947db96d56Sopenharmony_ci zipapp.create_archive(str(source), target, interpreter='python') 2957db96d56Sopenharmony_ci new_target = io.BytesIO() 2967db96d56Sopenharmony_ci target.seek(0) 2977db96d56Sopenharmony_ci zipapp.create_archive(target, new_target, interpreter=None) 2987db96d56Sopenharmony_ci new_target.seek(0) 2997db96d56Sopenharmony_ci with zipfile.ZipFile(new_target, 'r') as z: 3007db96d56Sopenharmony_ci self.assertEqual(set(z.namelist()), {'__main__.py'}) 3017db96d56Sopenharmony_ci 3027db96d56Sopenharmony_ci # (Unix only) tests that archives with shebang lines are made executable 3037db96d56Sopenharmony_ci @unittest.skipIf(sys.platform == 'win32', 3047db96d56Sopenharmony_ci 'Windows does not support an executable bit') 3057db96d56Sopenharmony_ci @os_helper.skip_unless_working_chmod 3067db96d56Sopenharmony_ci def test_shebang_is_executable(self): 3077db96d56Sopenharmony_ci # Test that an archive with a shebang line is made executable. 3087db96d56Sopenharmony_ci source = self.tmpdir / 'source' 3097db96d56Sopenharmony_ci source.mkdir() 3107db96d56Sopenharmony_ci (source / '__main__.py').touch() 3117db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 3127db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), interpreter='python') 3137db96d56Sopenharmony_ci self.assertTrue(target.stat().st_mode & stat.S_IEXEC) 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_ci @unittest.skipIf(sys.platform == 'win32', 3167db96d56Sopenharmony_ci 'Windows does not support an executable bit') 3177db96d56Sopenharmony_ci def test_no_shebang_is_not_executable(self): 3187db96d56Sopenharmony_ci # Test that an archive with no shebang line is not made executable. 3197db96d56Sopenharmony_ci source = self.tmpdir / 'source' 3207db96d56Sopenharmony_ci source.mkdir() 3217db96d56Sopenharmony_ci (source / '__main__.py').touch() 3227db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 3237db96d56Sopenharmony_ci zipapp.create_archive(str(source), str(target), interpreter=None) 3247db96d56Sopenharmony_ci self.assertFalse(target.stat().st_mode & stat.S_IEXEC) 3257db96d56Sopenharmony_ci 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ciclass ZipAppCmdlineTest(unittest.TestCase): 3287db96d56Sopenharmony_ci 3297db96d56Sopenharmony_ci """Test zipapp module command line API.""" 3307db96d56Sopenharmony_ci 3317db96d56Sopenharmony_ci def setUp(self): 3327db96d56Sopenharmony_ci tmpdir = tempfile.TemporaryDirectory() 3337db96d56Sopenharmony_ci self.addCleanup(tmpdir.cleanup) 3347db96d56Sopenharmony_ci self.tmpdir = pathlib.Path(tmpdir.name) 3357db96d56Sopenharmony_ci 3367db96d56Sopenharmony_ci def make_archive(self): 3377db96d56Sopenharmony_ci # Test that an archive with no shebang line is not made executable. 3387db96d56Sopenharmony_ci source = self.tmpdir / 'source' 3397db96d56Sopenharmony_ci source.mkdir() 3407db96d56Sopenharmony_ci (source / '__main__.py').touch() 3417db96d56Sopenharmony_ci target = self.tmpdir / 'source.pyz' 3427db96d56Sopenharmony_ci zipapp.create_archive(source, target) 3437db96d56Sopenharmony_ci return target 3447db96d56Sopenharmony_ci 3457db96d56Sopenharmony_ci def test_cmdline_create(self): 3467db96d56Sopenharmony_ci # Test the basic command line API. 3477db96d56Sopenharmony_ci source = self.tmpdir / 'source' 3487db96d56Sopenharmony_ci source.mkdir() 3497db96d56Sopenharmony_ci (source / '__main__.py').touch() 3507db96d56Sopenharmony_ci args = [str(source)] 3517db96d56Sopenharmony_ci zipapp.main(args) 3527db96d56Sopenharmony_ci target = source.with_suffix('.pyz') 3537db96d56Sopenharmony_ci self.assertTrue(target.is_file()) 3547db96d56Sopenharmony_ci 3557db96d56Sopenharmony_ci def test_cmdline_copy(self): 3567db96d56Sopenharmony_ci # Test copying an archive. 3577db96d56Sopenharmony_ci original = self.make_archive() 3587db96d56Sopenharmony_ci target = self.tmpdir / 'target.pyz' 3597db96d56Sopenharmony_ci args = [str(original), '-o', str(target)] 3607db96d56Sopenharmony_ci zipapp.main(args) 3617db96d56Sopenharmony_ci self.assertTrue(target.is_file()) 3627db96d56Sopenharmony_ci 3637db96d56Sopenharmony_ci def test_cmdline_copy_inplace(self): 3647db96d56Sopenharmony_ci # Test copying an archive in place fails. 3657db96d56Sopenharmony_ci original = self.make_archive() 3667db96d56Sopenharmony_ci target = self.tmpdir / 'target.pyz' 3677db96d56Sopenharmony_ci args = [str(original), '-o', str(original)] 3687db96d56Sopenharmony_ci with self.assertRaises(SystemExit) as cm: 3697db96d56Sopenharmony_ci zipapp.main(args) 3707db96d56Sopenharmony_ci # Program should exit with a non-zero return code. 3717db96d56Sopenharmony_ci self.assertTrue(cm.exception.code) 3727db96d56Sopenharmony_ci 3737db96d56Sopenharmony_ci def test_cmdline_copy_change_main(self): 3747db96d56Sopenharmony_ci # Test copying an archive doesn't allow changing __main__.py. 3757db96d56Sopenharmony_ci original = self.make_archive() 3767db96d56Sopenharmony_ci target = self.tmpdir / 'target.pyz' 3777db96d56Sopenharmony_ci args = [str(original), '-o', str(target), '-m', 'foo:bar'] 3787db96d56Sopenharmony_ci with self.assertRaises(SystemExit) as cm: 3797db96d56Sopenharmony_ci zipapp.main(args) 3807db96d56Sopenharmony_ci # Program should exit with a non-zero return code. 3817db96d56Sopenharmony_ci self.assertTrue(cm.exception.code) 3827db96d56Sopenharmony_ci 3837db96d56Sopenharmony_ci @patch('sys.stdout', new_callable=io.StringIO) 3847db96d56Sopenharmony_ci def test_info_command(self, mock_stdout): 3857db96d56Sopenharmony_ci # Test the output of the info command. 3867db96d56Sopenharmony_ci target = self.make_archive() 3877db96d56Sopenharmony_ci args = [str(target), '--info'] 3887db96d56Sopenharmony_ci with self.assertRaises(SystemExit) as cm: 3897db96d56Sopenharmony_ci zipapp.main(args) 3907db96d56Sopenharmony_ci # Program should exit with a zero return code. 3917db96d56Sopenharmony_ci self.assertEqual(cm.exception.code, 0) 3927db96d56Sopenharmony_ci self.assertEqual(mock_stdout.getvalue(), "Interpreter: <none>\n") 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ci def test_info_error(self): 3957db96d56Sopenharmony_ci # Test the info command fails when the archive does not exist. 3967db96d56Sopenharmony_ci target = self.tmpdir / 'dummy.pyz' 3977db96d56Sopenharmony_ci args = [str(target), '--info'] 3987db96d56Sopenharmony_ci with self.assertRaises(SystemExit) as cm: 3997db96d56Sopenharmony_ci zipapp.main(args) 4007db96d56Sopenharmony_ci # Program should exit with a non-zero return code. 4017db96d56Sopenharmony_ci self.assertTrue(cm.exception.code) 4027db96d56Sopenharmony_ci 4037db96d56Sopenharmony_ci 4047db96d56Sopenharmony_ciif __name__ == "__main__": 4057db96d56Sopenharmony_ci unittest.main() 406