doc: add readthedocs config
[rarfile.git] / test / test_extract.py
blob51ea4e09843e6a27766b892941b6cc34293cd65e
1 """Extract tests.
2 """
4 import io
5 import os
6 import sys
7 from datetime import datetime
9 import pytest
11 import rarfile
14 def get_props(rf, name):
15 inf = rf.getinfo(name)
16 return "".join([
17 inf.is_file() and "F" or "-",
18 inf.is_dir() and "D" or "-",
19 inf.is_symlink() and "L" or "-",
23 def san_unix(fn):
24 return rarfile.sanitize_filename(fn, "/", False)
27 def san_win32(fn):
28 return rarfile.sanitize_filename(fn, "/", True)
31 def test_sanitize_unix():
32 assert san_unix("asd/asd") == "asd/asd"
33 assert san_unix("asd/../asd") == "asd/asd"
34 assert san_unix("c:/a/x") == r"c:/a/x"
35 assert san_unix("z/./../a /b./c") == "z/a /b./c"
36 assert san_unix("z<>*?:") == "z____:"
39 def test_sanitize_win32():
40 assert san_win32("asd/asd") == "asd/asd"
41 assert san_win32("asd/../asd") == "asd/asd"
42 assert san_win32("c:/a/x") == "a/x"
43 assert san_win32("z/./../a /b./c") == "z/a_/b_/c"
44 assert san_win32("z<>*?:\\^") == "z_______"
47 def checktime(fn, exp_mtime):
48 # cannot check subsecond precision as filesystem may not support it
49 cut = len("0000-00-00 00:00:00")
50 st = os.stat(fn)
51 got_mtime = datetime.fromtimestamp(st.st_mtime, exp_mtime.tzinfo)
52 exp_stamp = exp_mtime.isoformat(" ", "seconds")[:cut]
53 got_stamp = got_mtime.isoformat(" ", "seconds")[:cut]
54 assert exp_stamp == got_stamp
57 def checkfile(fn, data, mtime):
58 with open(fn, "r", encoding="utf8") as f:
59 got = f.read()
60 assert got.strip() == data
62 checktime(fn, mtime)
65 def check_subdir(rf, tmp_path):
66 # pre-mkdir
67 ext1 = tmp_path / "ext1"
68 inf = rf.getinfo("sub/dir1/file1.txt")
69 os.mkdir(ext1)
70 rf.extract(inf, ext1)
71 assert sorted(os.listdir(tmp_path)) == ["ext1"]
72 assert os.listdir(ext1 / "sub") == ["dir1"]
73 checkfile(ext1 / "sub/dir1/file1.txt", "file1", inf.mtime)
75 # no mkdir
76 ext2 = tmp_path / "ext2"
77 inf = rf.getinfo("sub/dir2/file2.txt")
78 rf.extract("sub/dir2/file2.txt", ext2)
79 assert sorted(os.listdir(tmp_path)) == ["ext1", "ext2"]
80 assert os.listdir(ext2 / "sub") == ["dir2"]
81 checkfile(ext2 / "sub/dir2/file2.txt", "file2", inf.mtime)
83 # spaced
84 ext3 = tmp_path / "ext3"
85 inf = rf.getinfo("sub/with space/long fn.txt")
86 rf.extract("sub/with space/long fn.txt", ext3)
87 checkfile(ext3 / "sub/with space/long fn.txt", "long fn", inf.mtime)
89 # unicode
90 ext4 = tmp_path / "ext4"
91 inf = rf.getinfo("sub/üȵĩöḋè/file.txt")
92 rf.extract("sub/üȵĩöḋè/file.txt", ext4)
93 checkfile(ext4 / "sub/üȵĩöḋè/file.txt", "file", inf.mtime)
95 # dir only
96 ext5 = tmp_path / "ext5"
97 inf = rf.getinfo("sub/dir2")
98 rf.extract("sub/dir2", ext5)
99 assert os.listdir(ext5 / "sub") == ["dir2"]
100 assert os.listdir(ext5 / "sub/dir2") == []
101 checktime(ext5 / "sub/dir2", inf.mtime)
103 # cwd
104 ext6 = tmp_path / "ext6"
105 os.mkdir(ext6)
106 old = os.getcwd()
107 try:
108 os.chdir(ext6)
109 rf.extract("sub/dir1")
110 assert os.listdir(".") == ["sub"]
111 assert os.listdir("sub") == ["dir1"]
112 assert os.listdir("sub/dir1") == []
113 finally:
114 os.chdir(old)
116 # errors
117 with pytest.raises(io.UnsupportedOperation):
118 rf.open("sub/dir1")
121 @pytest.mark.parametrize("fn", [
122 "test/files/rar3-subdirs.rar",
123 "test/files/rar5-subdirs.rar",
125 def test_subdirs(fn, tmp_path):
126 with rarfile.RarFile(fn) as rf:
127 check_subdir(rf, tmp_path)
130 @pytest.mark.parametrize("fn", [
131 "test/files/rar3-readonly-unix.rar",
132 "test/files/rar3-readonly-win.rar",
133 "test/files/rar5-readonly-unix.rar",
134 "test/files/rar5-readonly-win.rar",
136 def test_readonly(fn, tmp_path):
137 with rarfile.RarFile(fn) as rf:
138 assert get_props(rf, "ro_dir") == "-D-"
139 assert get_props(rf, "ro_dir/ro_file.txt") == "F--"
141 rf.extractall(tmp_path)
143 assert os.access(tmp_path / "ro_dir/ro_file.txt", os.R_OK)
144 assert not os.access(tmp_path / "ro_dir/ro_file.txt", os.W_OK)
146 if sys.platform != "win32":
147 assert os.access(tmp_path / "ro_dir", os.R_OK)
148 assert not os.access(tmp_path / "ro_dir", os.W_OK)
151 @pytest.mark.parametrize("fn", [
152 "test/files/rar3-symlink-unix.rar",
153 "test/files/rar5-symlink-unix.rar",
155 def test_symlink(fn, tmp_path):
156 with rarfile.RarFile(fn) as rf:
157 assert get_props(rf, "data.txt") == "F--"
158 assert get_props(rf, "data_link") == "--L"
159 assert get_props(rf, "random_link") == "--L"
161 rf.extractall(tmp_path)
163 assert sorted(os.listdir(tmp_path)) == ["data.txt", "data_link", "random_link"]
165 data = rf.getinfo("data.txt")
166 data_link = rf.getinfo("data_link")
167 random_link = rf.getinfo("random_link")
169 assert not data.is_symlink()
170 assert data_link.is_symlink()
171 assert random_link.is_symlink()
173 assert rf.read(data) == b"data\n"
174 assert rf.read(data_link) == b"data.txt"
175 assert rf.read(random_link) == b"../random123"
177 assert os.path.isfile(tmp_path / "data.txt")
178 assert os.path.islink(tmp_path / "data_link")
179 assert os.path.islink(tmp_path / "random_link")
181 # str - work around pypy3 bug
182 assert os.readlink(str(tmp_path / "data_link")) == "data.txt"
183 assert os.readlink(str(tmp_path / "random_link")) == "../random123"
186 def test_symlink_win(tmp_path):
187 fn = "test/files/rar5-symlink-win.rar"
188 with rarfile.RarFile(fn) as rf:
189 assert get_props(rf, "content/dir1") == "-D-"
190 assert get_props(rf, "content/dir2") == "-D-"
191 assert get_props(rf, "content/file.txt") == "F--"
192 assert get_props(rf, "links/bad_link") == "--L"
193 assert get_props(rf, "links/dir_junction") == "--L"
194 assert get_props(rf, "links/dir_link") == "--L"
195 assert get_props(rf, "links/file_link") == "--L"
197 with pytest.warns(rarfile.UnsupportedWarning):
198 rf.extractall(tmp_path)
200 assert sorted(os.listdir(tmp_path)) == ["content", "links"]
201 assert sorted(os.listdir(tmp_path / "content")) == ["dir1", "dir2", "file.txt"]
202 assert sorted(os.listdir(tmp_path / "links")) == ["bad_link", "dir_link", "file_link"]
204 assert os.path.islink(tmp_path / "links/bad_link")
205 assert os.path.islink(tmp_path / "links/dir_link")
206 assert os.path.islink(tmp_path / "links/file_link")
208 @pytest.mark.parametrize("fn", [
209 "test/files/rar3-old.rar",
210 "test/files/rar3-vols.part1.rar",
211 "test/files/rar5-vols.part1.rar",
213 def test_vols(fn, tmp_path):
214 with rarfile.RarFile(fn) as rf:
215 rarfile.FORCE_TOOL = True
216 try:
217 rf.extractall(str(tmp_path))
218 finally:
219 rarfile.FORCE_TOOL = False
222 assert sorted(os.listdir(tmp_path)) == ["vols"]
223 assert sorted(os.listdir(tmp_path / "vols")) == ["bigfile.txt", "smallfile.txt"]
225 assert os.path.isfile(tmp_path / "vols" / "bigfile.txt")
226 assert os.path.isfile(tmp_path / "vols" / "smallfile.txt")