11 from xattr import NS_USER, XATTR_CREATE, XATTR_REPLACE
13 NAMESPACE = os.environ.get("NAMESPACE", NS_USER)
15 if sys.hexversion >= 0x03000000:
22 TEST_DIR = os.environ.get("TEST_DIR", ".")
23 TEST_IGNORE_XATTRS = os.environ.get("TEST_IGNORE_XATTRS", "")
24 if TEST_IGNORE_XATTRS == "":
25 TEST_IGNORE_XATTRS = []
27 TEST_IGNORE_XATTRS = TEST_IGNORE_XATTRS.split(",")
28 # The following has to be a list comprehension, not a generator, to
29 # avoid weird consequences of lazy evaluation.
30 TEST_IGNORE_XATTRS.extend([a.encode() for a in TEST_IGNORE_XATTRS])
33 USER_ATTR = NAMESPACE.decode() + "." + USER_NN
36 LARGE_VAL = "x" * 2048
40 USER_NN = USER_NN.encode()
41 USER_VAL = USER_VAL.encode()
42 USER_ATTR = USER_ATTR.encode()
43 EMPTY_VAL = EMPTY_VAL.encode()
44 LARGE_VAL = LARGE_VAL.encode()
48 def ignore_tuples(attrs):
49 """Remove ignored attributes from the output of xattr.get_all."""
50 return [attr for attr in attrs
51 if attr[0] not in TEST_IGNORE_XATTRS]
54 """Remove ignored attributes from the output of xattr.list"""
55 return [attr for attr in attrs
56 if attr not in TEST_IGNORE_XATTRS]
58 def lists_equal(attrs, value):
59 """Helper to check list equivalence, skipping TEST_IGNORE_XATTRS."""
60 assert ignore(attrs) == value
62 def tuples_equal(attrs, value):
63 """Helper to check list equivalence, skipping TEST_IGNORE_XATTRS."""
64 assert ignore_tuples(attrs) == value
66 # Fixtures and helpers
70 """per-test temp dir based in TEST_DIR"""
71 with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
75 fh, fname = tempfile.mkstemp(".test", "xattr-", path)
78 def get_file_name(path):
79 fh, fname = get_file(path)
83 def get_file_fd(path):
84 return get_file(path)[0]
86 def get_file_object(path):
87 fd = get_file(path)[0]
91 return tempfile.mkdtemp(".test", "xattr-", path)
93 def get_symlink(path, dangling=True):
94 """create a symlink"""
95 fh, fname = get_file(path)
99 sname = fname + ".symlink"
100 os.symlink(fname, sname)
103 def get_valid_symlink(path):
104 return get_symlink(path, dangling=False)[1]
106 def get_dangling_symlink(path):
107 return get_symlink(path, dangling=True)[1]
109 # Note: user attributes are only allowed on files and directories, so
110 # we have to skip the symlinks here. See xattr(7).
112 (get_file_name, False),
113 (get_file_fd, False),
114 (get_file_object, False),
116 (get_valid_symlink, False),
127 ALL_ITEMS_P = ITEMS_P + [ (get_valid_symlink, True),
128 (get_dangling_symlink, True)]
129 ALL_ITEMS_D = ITEMS_D + ["valid symlink", "dangling symlink"]
131 @pytest.fixture(params=ITEMS_P, ids=ITEMS_D)
132 def subject(testdir, request):
133 return request.param[0](testdir), request.param[1]
135 @pytest.fixture(params=ALL_ITEMS_P, ids=ALL_ITEMS_D)
136 def any_subject(testdir, request):
137 return request.param[0](testdir), request.param[1]
139 @pytest.fixture(params=[True, False], ids=["with namespace", "no namespace"])
143 @pytest.fixture(params=[True, False], ids=["dangling", "valid"])
144 def use_dangling(request):
149 def test_empty_value(subject):
150 item, nofollow = subject
151 xattr.set(item, USER_ATTR, EMPTY_VAL, nofollow=nofollow)
152 assert xattr.get(item, USER_ATTR, nofollow=nofollow) == EMPTY_VAL
154 def test_large_value(subject):
155 item, nofollow = subject
156 xattr.set(item, USER_ATTR, LARGE_VAL)
157 assert xattr.get(item, USER_ATTR, nofollow=nofollow) == LARGE_VAL
160 def test_file_mixed_access_deprecated(testdir):
161 """test mixed access to file (deprecated functions)"""
162 fh, fname = get_file(testdir)
163 with os.fdopen(fh) as fo:
164 lists_equal(xattr.listxattr(fname), [])
165 xattr.setxattr(fname, USER_ATTR, USER_VAL)
166 lists_equal(xattr.listxattr(fh), [USER_ATTR])
167 assert xattr.getxattr(fo, USER_ATTR) == USER_VAL
168 tuples_equal(xattr.get_all(fo), [(USER_ATTR, USER_VAL)])
169 tuples_equal(xattr.get_all(fname),
170 [(USER_ATTR, USER_VAL)])
172 def test_file_mixed_access(testdir):
173 """test mixed access to file"""
174 fh, fname = get_file(testdir)
175 with os.fdopen(fh) as fo:
176 lists_equal(xattr.list(fname), [])
177 xattr.set(fname, USER_ATTR, USER_VAL)
178 lists_equal(xattr.list(fh), [USER_ATTR])
179 assert xattr.list(fh, namespace=NAMESPACE) == [USER_NN]
180 assert xattr.get(fo, USER_ATTR) == USER_VAL
181 assert xattr.get(fo, USER_NN, namespace=NAMESPACE) == USER_VAL
182 tuples_equal(xattr.get_all(fo),
183 [(USER_ATTR, USER_VAL)])
184 assert xattr.get_all(fo, namespace=NAMESPACE) == \
185 [(USER_NN, USER_VAL)]
186 tuples_equal(xattr.get_all(fname), [(USER_ATTR, USER_VAL)])
187 assert xattr.get_all(fname, namespace=NAMESPACE) == \
188 [(USER_NN, USER_VAL)]
190 def test_replace_on_missing(subject, use_ns):
192 lists_equal(xattr.list(item), [])
193 with pytest.raises(EnvironmentError):
195 xattr.set(item, USER_NN, USER_VAL, flags=XATTR_REPLACE,
198 xattr.set(item, USER_ATTR, USER_VAL, flags=XATTR_REPLACE)
200 def test_create_on_existing(subject, use_ns):
202 lists_equal(xattr.list(item), [])
204 xattr.set(item, USER_NN, USER_VAL,
207 xattr.set(item, USER_ATTR, USER_VAL)
208 with pytest.raises(EnvironmentError):
210 xattr.set(item, USER_NN, USER_VAL,
211 flags=XATTR_CREATE, namespace=NAMESPACE)
213 xattr.set(item, USER_ATTR, USER_VAL, flags=XATTR_CREATE)
215 def test_remove_on_missing(subject, use_ns):
217 lists_equal(xattr.list(item), [])
218 with pytest.raises(EnvironmentError):
220 xattr.remove(item, USER_NN, namespace=NAMESPACE)
222 xattr.remove(item, USER_ATTR)
224 def test_set_get_remove(subject, use_ns):
226 lists_equal(xattr.list(item), [])
228 xattr.set(item, USER_NN, USER_VAL,
231 xattr.set(item, USER_ATTR, USER_VAL)
233 assert xattr.list(item, namespace=NAMESPACE) == [USER_NN]
235 lists_equal(xattr.list(item), [USER_ATTR])
236 lists_equal(xattr.list(item, namespace=EMPTY_NS),
239 assert xattr.get(item, USER_NN, namespace=NAMESPACE) == USER_VAL
241 assert xattr.get(item, USER_ATTR) == USER_VAL
243 assert xattr.get_all(item, namespace=NAMESPACE) == \
244 [(USER_NN, USER_VAL)]
246 tuples_equal(xattr.get_all(item),
247 [(USER_ATTR, USER_VAL)])
249 xattr.remove(item, USER_NN, namespace=NAMESPACE)
251 xattr.remove(item, USER_ATTR)
252 lists_equal(xattr.list(item), [])
253 tuples_equal(xattr.get_all(item), [])
255 def test_replace_on_missing_deprecated(subject):
257 lists_equal(xattr.listxattr(item), [])
258 with pytest.raises(EnvironmentError):
259 xattr.setxattr(item, USER_ATTR, USER_VAL, XATTR_REPLACE)
261 def test_create_on_existing_deprecated(subject):
263 lists_equal(xattr.listxattr(item), [])
264 xattr.setxattr(item, USER_ATTR, USER_VAL, 0)
265 with pytest.raises(EnvironmentError):
266 xattr.setxattr(item, USER_ATTR, USER_VAL, XATTR_CREATE)
268 def test_remove_on_missing_deprecated(subject):
269 """check deprecated list, set, get operations against an item"""
271 lists_equal(xattr.listxattr(item), [])
272 with pytest.raises(EnvironmentError):
273 xattr.removexattr(item, USER_ATTR)
275 def test_set_get_remove_deprecated(subject):
276 """check deprecated list, set, get operations against an item"""
278 lists_equal(xattr.listxattr(item), [])
279 xattr.setxattr(item, USER_ATTR, USER_VAL, 0)
280 lists_equal(xattr.listxattr(item), [USER_ATTR])
281 assert xattr.getxattr(item, USER_ATTR) == USER_VAL
282 tuples_equal(xattr.get_all(item), [(USER_ATTR, USER_VAL)])
283 xattr.removexattr(item, USER_ATTR)
284 lists_equal(xattr.listxattr(item), [])
285 tuples_equal(xattr.get_all(item), [])
287 def test_many_ops(subject):
290 xattr.set(item, USER_ATTR, USER_VAL)
293 for i in range(MANYOPS_COUNT):
294 lists_equal(xattr.list(item), VL)
295 lists_equal(xattr.list(item, namespace=EMPTY_NS), VL)
296 assert xattr.list(item, namespace=NAMESPACE) == VN
297 for i in range(MANYOPS_COUNT):
298 assert xattr.get(item, USER_ATTR) == USER_VAL
299 assert xattr.get(item, USER_NN, namespace=NAMESPACE) == USER_VAL
300 for i in range(MANYOPS_COUNT):
301 tuples_equal(xattr.get_all(item),
302 [(USER_ATTR, USER_VAL)])
303 assert xattr.get_all(item, namespace=NAMESPACE) == \
304 [(USER_NN, USER_VAL)]
306 def test_many_ops_deprecated(subject):
307 """test many ops (deprecated functions)"""
309 xattr.setxattr(item, USER_ATTR, USER_VAL)
311 for i in range(MANYOPS_COUNT):
312 lists_equal(xattr.listxattr(item), VL)
313 for i in range(MANYOPS_COUNT):
314 assert xattr.getxattr(item, USER_ATTR) == USER_VAL
315 for i in range(MANYOPS_COUNT):
316 tuples_equal(xattr.get_all(item),
317 [(USER_ATTR, USER_VAL)])
319 def test_no_attributes_deprecated(any_subject):
320 """test no attributes (deprecated functions)"""
321 item, nofollow = any_subject
322 lists_equal(xattr.listxattr(item, True), [])
323 tuples_equal(xattr.get_all(item, True), [])
324 with pytest.raises(EnvironmentError):
325 xattr.getxattr(item, USER_ATTR, True)
327 def test_no_attributes(any_subject):
328 """test no attributes"""
329 item, nofollow = any_subject
330 lists_equal(xattr.list(item, nofollow=nofollow), [])
331 assert xattr.list(item, nofollow=nofollow,
332 namespace=NAMESPACE) == []
333 tuples_equal(xattr.get_all(item, nofollow=nofollow), [])
334 assert xattr.get_all(item, nofollow=nofollow,
335 namespace=NAMESPACE) == []
336 with pytest.raises(EnvironmentError):
337 xattr.get(item, USER_NN, nofollow=nofollow,
340 def test_binary_payload_deprecated(subject):
341 """test binary values (deprecated functions)"""
344 xattr.setxattr(item, USER_ATTR, BINVAL)
345 lists_equal(xattr.listxattr(item), [USER_ATTR])
346 assert xattr.getxattr(item, USER_ATTR) == BINVAL
347 tuples_equal(xattr.get_all(item), [(USER_ATTR, BINVAL)])
348 xattr.removexattr(item, USER_ATTR)
350 def test_binary_payload(subject):
351 """test binary values"""
354 xattr.set(item, USER_ATTR, BINVAL)
355 lists_equal(xattr.list(item), [USER_ATTR])
356 assert xattr.list(item, namespace=NAMESPACE) == [USER_NN]
357 assert xattr.get(item, USER_ATTR) == BINVAL
358 assert xattr.get(item, USER_NN, namespace=NAMESPACE) == BINVAL
359 tuples_equal(xattr.get_all(item), [(USER_ATTR, BINVAL)])
360 assert xattr.get_all(item, namespace=NAMESPACE) == [(USER_NN, BINVAL)]
361 xattr.remove(item, USER_ATTR)
363 def test_symlinks_user_fail(testdir, use_dangling):
364 _, sname = get_symlink(testdir, dangling=use_dangling)
365 with pytest.raises(IOError):
366 xattr.set(sname, USER_ATTR, USER_VAL, nofollow=True)
367 with pytest.raises(IOError):
368 xattr.set(sname, USER_NN, USER_VAL, namespace=NAMESPACE,
370 with pytest.raises(IOError):
371 xattr.setxattr(sname, USER_ATTR, USER_VAL, XATTR_CREATE, True)
373 def test_none_namespace(subject):
374 with pytest.raises(TypeError):
375 xattr.get(subject[0], USER_ATTR, namespace=None)
377 @pytest.mark.parametrize(
379 [xattr.get, xattr.list, xattr.listxattr,
380 xattr.remove, xattr.removexattr,
381 xattr.set, xattr.setxattr,
382 xattr.get, xattr.getxattr])
383 def test_wrong_call(call):
384 with pytest.raises(TypeError):
387 @pytest.mark.parametrize(
388 "call, args", [(xattr.get, [USER_ATTR]),
389 (xattr.listxattr, []),
391 (xattr.remove, [USER_ATTR]),
392 (xattr.removexattr, [USER_ATTR]),
393 (xattr.get, [USER_ATTR]),
394 (xattr.getxattr, [USER_ATTR]),
395 (xattr.set, [USER_ATTR, USER_VAL]),
396 (xattr.setxattr, [USER_ATTR, USER_VAL])])
397 def test_wrong_argument_type(call, args):
398 with pytest.raises(TypeError):
399 call(object(), *args)