Coverage for tests/unit_tests/managers/test_package_manager.py: 100%

47 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-07 05:08 +0000

1# This file is part of the Apio project 

2# (C) 2016-present FPGAwars 

3# Project authors: 

4# * Jesús Arroyo (Jesus89) (2016-2019) 

5# * Juan Gonzalez (obijuan) (2019-2024) 

6# * The Apio Team (2024-present) 

7# License: GPLv2 

8 

9""" 

10Tests of managers/package_manager.py 

11""" 

12 

13import io 

14import tarfile 

15import pytest 

16from tests.conftest import ApioRunner 

17from apio.managers.package_manager import unpack_tgz 

18 

19 

20def test_unpack_tgz(apio_runner: ApioRunner): 

21 """Tests unpack_tgz().""" 

22 

23 with apio_runner.in_sandbox() as sb: 

24 

25 # -- Create the source files, one of them in a sub dir. 

26 src_dir = sb.proj_dir / "src" 

27 (src_dir / "bin").mkdir(parents=True) 

28 (src_dir / "readme.txt").write_text("readme", encoding="utf-8") 

29 (src_dir / "bin" / "tool").write_text("tool", encoding="utf-8") 

30 

31 # -- Archive them. 

32 archive = sb.proj_dir / "package.tgz" 

33 with tarfile.open(archive, "w:gz") as tar: 

34 tar.add(src_dir / "readme.txt", arcname="readme.txt") 

35 tar.add(src_dir / "bin" / "tool", arcname="bin/tool") 

36 

37 # -- Case 1: Unpack to a new dir. Should extract all the files. 

38 dest_dir = sb.proj_dir / "dest1" 

39 unpack_tgz(archive, dest_dir) 

40 readme_text = (dest_dir / "readme.txt").read_text(encoding="utf-8") 

41 assert readme_text == "readme" 

42 tool_text = (dest_dir / "bin" / "tool").read_text(encoding="utf-8") 

43 assert tool_text == "tool" 

44 

45 # -- Case 2: Unpack to an existing dir. Should extract all the files 

46 # -- and keep the existing ones. 

47 dest_dir = sb.proj_dir / "dest2" 

48 dest_dir.mkdir() 

49 (dest_dir / "existing.txt").write_text("existing", encoding="utf-8") 

50 unpack_tgz(archive, dest_dir) 

51 assert (dest_dir / "readme.txt").is_file() 

52 assert (dest_dir / "bin" / "tool").is_file() 

53 assert (dest_dir / "existing.txt").is_file() 

54 

55 # -- Case 3: An archive without the .tgz extension. Should fail. 

56 archive_tar = sb.proj_dir / "package.tar" 

57 archive_tar.write_bytes(archive.read_bytes()) 

58 with apio_runner.with_logger() as log: 

59 with pytest.raises(SystemExit) as e: 

60 unpack_tgz(archive_tar, sb.proj_dir / "dest3") 

61 assert e.value.code == 1 

62 assert "it's not a .tgz file" in log.out 

63 assert not (sb.proj_dir / "dest3").exists() 

64 

65 

66def test_unpack_tgz_unsafe_item(apio_runner: ApioRunner): 

67 """Tests that unpack_tgz() rejects an item outside of the dest dir.""" 

68 

69 with apio_runner.in_sandbox() as sb: 

70 

71 # -- Create an archive with a '../' item. 

72 archive = sb.proj_dir / "package.tgz" 

73 with tarfile.open(archive, "w:gz") as tar: 

74 tar.addfile(tarfile.TarInfo("../escape.txt"), io.BytesIO()) 

75 

76 # -- Unpack. Should fail and not write the file. 

77 with apio_runner.with_logger() as log: 

78 with pytest.raises(SystemExit) as e: 

79 unpack_tgz(archive, sb.proj_dir / "dest") 

80 assert e.value.code == 1 

81 assert "Unsafe item '../escape.txt'" in log.out 

82 assert not (sb.proj_dir / "escape.txt").exists()