diff options
Diffstat (limited to 'tests/test_tarfile.py')
| -rw-r--r-- | tests/test_tarfile.py | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/tests/test_tarfile.py b/tests/test_tarfile.py new file mode 100644 index 00000000..12fd9b26 --- /dev/null +++ b/tests/test_tarfile.py @@ -0,0 +1,111 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2025 Canonical Ltd. +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +"""Unit tests for verifying the correctness of tarfile management used in apt.debfile.""" + +import os +import resource +import sys +import unittest +from collections.abc import Callable +from typing import override + +import apt_inst +from test_all import get_library_dir +from testcommon import TestCase + +libdir = get_library_dir() +if libdir: + sys.path.insert(0, libdir) + + +class TestTarfile(TestCase): + """test the tarfile""" + + # Large tarfile with single member called 'large_member' + TEST_LARGE_TARFILE: str = "./data/misc/large_tar.zst" + TEST_LARGE_TARFILE_MEMBER: str = "large_member" + + # Setting resource limit to trigger MemoryError for large files + VR_MEM_LIMIT_HARD: int = 2**31 + VR_MEM_LIMIT_SOFT: int = VR_MEM_LIMIT_HARD + # resource.setrlimit(resource.RLIMIT_AS, (VR_MEM_LIMIT_SOFT, VR_MEM_LIMIT_HARD)) + + tarfile: apt_inst.TarFile | None = None + + @classmethod + @override + def setUpClass(cls): + super().setUpClass() + resource.setrlimit( + resource.RLIMIT_AS, (cls.VR_MEM_LIMIT_SOFT, cls.VR_MEM_LIMIT_HARD) + ) + + def _make_large_tarfile(self) -> apt_inst.TarFile: + """ + Helper method to create a TarFile instance for the large tarfile. + + Having one shared class members for the tarfile introduces test errors + during building. This method ensures that each test gets a fresh + instance of the TarFile. + + """ + return apt_inst.TarFile(self.TEST_LARGE_TARFILE, comp="zstd") + + @override + def setUp(self): + super().setUp() + self.tarfile = self._make_large_tarfile() + + def _collect_filenames_callback( + self, files: list[str] + ) -> Callable[[apt_inst.TarMember, bytes], None]: + """Helper method to collect filenames from tarfile.go callback.""" + return lambda item, data: files.append(item.name) + + def test_go_extract_data_large_file_oom(self): + assert self.tarfile is not None + files: list[str] = [] + with self.assertRaises(MemoryError): + self.tarfile.go( + (self._collect_filenames_callback(files)), + self.TEST_LARGE_TARFILE_MEMBER, + True, + ) + self.assertEqual(files, []) + + def test_go_extract_false_large_file_no_oom(self): + assert self.tarfile is not None + files: list[str] = [] + try: + self.tarfile.go( + self._collect_filenames_callback(files), + self.TEST_LARGE_TARFILE_MEMBER, + False, + ) + except MemoryError: + self.fail( + "tarfile.go(...) raised MemoryError with extract_data set to False!" + ) + self.assertEqual(files, [self.TEST_LARGE_TARFILE_MEMBER]) + + def test_go_skip_extract_large_file_if_not_specified(self): + assert self.tarfile is not None + files: list[str] = [] + try: + self.tarfile.go(self._collect_filenames_callback(files), "", True) + except MemoryError: + self.fail( + "tarfile.go(...) raised MemoryError when no member was specified!" + ) + self.assertEqual(files, [self.TEST_LARGE_TARFILE_MEMBER]) + + +if __name__ == "__main__": + # logging.basicConfig(level=logging.DEBUG) + os.chdir(os.path.dirname(__file__)) + _ = unittest.main() |
