7 from datetime
import datetime
14 def get_props(rf
, name
):
15 inf
= rf
.getinfo(name
)
17 inf
.is_file() and "F" or "-",
18 inf
.is_dir() and "D" or "-",
19 inf
.is_symlink() and "L" or "-",
24 return rarfile
.sanitize_filename(fn
, "/", False)
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")
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
:
60 assert got
.strip() == data
65 def check_subdir(rf
, tmp_path
):
67 ext1
= tmp_path
/ "ext1"
68 inf
= rf
.getinfo("sub/dir1/file1.txt")
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
)
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
)
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
)
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
)
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
)
104 ext6
= tmp_path
/ "ext6"
109 rf
.extract("sub/dir1")
110 assert os
.listdir(".") == ["sub"]
111 assert os
.listdir("sub") == ["dir1"]
112 assert os
.listdir("sub/dir1") == []
117 with pytest
.raises(io
.UnsupportedOperation
):
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")