11 from xattr import NS_USER, XATTR_CREATE, XATTR_REPLACE
13 NAMESPACE = os.environ.get("NAMESPACE", NS_USER)
17 TEST_DIR = os.environ.get("TEST_DIR", ".")
18 TEST_IGNORE_XATTRS = os.environ.get("TEST_IGNORE_XATTRS", "")
19 if TEST_IGNORE_XATTRS == "":
20 TEST_IGNORE_XATTRS = []
22 TEST_IGNORE_XATTRS = TEST_IGNORE_XATTRS.split(",")
23 # The following has to be a list comprehension, not a generator, to
24 # avoid weird consequences of lazy evaluation.
25 TEST_IGNORE_XATTRS.extend([a.encode() for a in TEST_IGNORE_XATTRS])
28 USER_ATTR = NAMESPACE.decode() + "." + USER_NN
31 LARGE_VAL = "x" * 2048
34 USER_NN = USER_NN.encode()
35 USER_VAL = USER_VAL.encode()
36 USER_ATTR = USER_ATTR.encode()
37 EMPTY_VAL = EMPTY_VAL.encode()
38 LARGE_VAL = LARGE_VAL.encode()
42 def ignore_tuples(attrs):
43 """Remove ignored attributes from the output of xattr.get_all."""
44 return [attr for attr in attrs
45 if attr[0] not in TEST_IGNORE_XATTRS]
48 """Remove ignored attributes from the output of xattr.list"""
49 return [attr for attr in attrs
50 if attr not in TEST_IGNORE_XATTRS]
52 def lists_equal(attrs, value):
53 """Helper to check list equivalence, skipping TEST_IGNORE_XATTRS."""
54 assert ignore(attrs) == value
56 def tuples_equal(attrs, value):
57 """Helper to check list equivalence, skipping TEST_IGNORE_XATTRS."""
58 assert ignore_tuples(attrs) == value
60 # Fixtures and helpers
64 """per-test temp dir based in TEST_DIR"""
65 with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
69 fh, fname = tempfile.mkstemp(".test", "xattr-", path)
72 def get_file_name(path):
73 fh, fname = get_file(path)
77 def get_file_fd(path):
78 return get_file(path)[0]
80 def get_file_object(path):
81 fd = get_file(path)[0]
85 return tempfile.mkdtemp(".test", "xattr-", path)
87 def get_symlink(path, dangling=True):
88 """create a symlink"""
89 fh, fname = get_file(path)
93 sname = fname + ".symlink"
94 os.symlink(fname, sname)
97 def get_valid_symlink(path):
98 return get_symlink(path, dangling=False)[1]
100 def get_dangling_symlink(path):
101 return get_symlink(path, dangling=True)[1]
103 # Note: user attributes are only allowed on files and directories, so
104 # we have to skip the symlinks here. See xattr(7).
106 (get_file_name, False),
107 (get_file_fd, False),
108 (get_file_object, False),
110 (get_valid_symlink, False),
121 ALL_ITEMS_P = ITEMS_P + [ (get_valid_symlink, True),
122 (get_dangling_symlink, True)]
123 ALL_ITEMS_D = ITEMS_D + ["valid symlink", "dangling symlink"]
125 @pytest.fixture(params=ITEMS_P, ids=ITEMS_D)
126 def subject(testdir, request):
127 return request.param[0](testdir), request.param[1]
129 @pytest.fixture(params=ALL_ITEMS_P, ids=ALL_ITEMS_D)
130 def any_subject(testdir, request):
131 return request.param[0](testdir), request.param[1]
133 @pytest.fixture(params=[True, False], ids=["with namespace", "no namespace"])
137 @pytest.fixture(params=[True, False], ids=["dangling", "valid"])
138 def use_dangling(request):
143 def test_empty_value(subject):
144 item, nofollow = subject
145 xattr.set(item, USER_ATTR, EMPTY_VAL, nofollow=nofollow)
146 assert xattr.get(item, USER_ATTR, nofollow=nofollow) == EMPTY_VAL
148 def test_large_value(subject):
149 item, nofollow = subject
150 xattr.set(item, USER_ATTR, LARGE_VAL)
151 assert xattr.get(item, USER_ATTR, nofollow=nofollow) == LARGE_VAL
154 def test_file_mixed_access_deprecated(testdir):
155 """test mixed access to file (deprecated functions)"""
156 fh, fname = get_file(testdir)
157 with os.fdopen(fh) as fo:
158 lists_equal(xattr.listxattr(fname), [])
159 xattr.setxattr(fname, USER_ATTR, USER_VAL)
160 lists_equal(xattr.listxattr(fh), [USER_ATTR])
161 assert xattr.getxattr(fo, USER_ATTR) == USER_VAL
162 tuples_equal(xattr.get_all(fo), [(USER_ATTR, USER_VAL)])
163 tuples_equal(xattr.get_all(fname),
164 [(USER_ATTR, USER_VAL)])
166 def test_file_mixed_access(testdir):
167 """test mixed access to file"""
168 fh, fname = get_file(testdir)
169 with os.fdopen(fh) as fo:
170 lists_equal(xattr.list(fname), [])
171 xattr.set(fname, USER_ATTR, USER_VAL)
172 lists_equal(xattr.list(fh), [USER_ATTR])
173 assert xattr.list(fh, namespace=NAMESPACE) == [USER_NN]
174 assert xattr.get(fo, USER_ATTR) == USER_VAL
175 assert xattr.get(fo, USER_NN, namespace=NAMESPACE) == USER_VAL
176 tuples_equal(xattr.get_all(fo),
177 [(USER_ATTR, USER_VAL)])
178 assert xattr.get_all(fo, namespace=NAMESPACE) == \
179 [(USER_NN, USER_VAL)]
180 tuples_equal(xattr.get_all(fname), [(USER_ATTR, USER_VAL)])
181 assert xattr.get_all(fname, namespace=NAMESPACE) == \
182 [(USER_NN, USER_VAL)]
184 def test_replace_on_missing(subject, use_ns):
186 lists_equal(xattr.list(item), [])
187 with pytest.raises(EnvironmentError):
189 xattr.set(item, USER_NN, USER_VAL, flags=XATTR_REPLACE,
192 xattr.set(item, USER_ATTR, USER_VAL, flags=XATTR_REPLACE)
194 def test_create_on_existing(subject, use_ns):
196 lists_equal(xattr.list(item), [])
198 xattr.set(item, USER_NN, USER_VAL,
201 xattr.set(item, USER_ATTR, USER_VAL)
202 with pytest.raises(EnvironmentError):
204 xattr.set(item, USER_NN, USER_VAL,
205 flags=XATTR_CREATE, namespace=NAMESPACE)
207 xattr.set(item, USER_ATTR, USER_VAL, flags=XATTR_CREATE)
209 def test_remove_on_missing(any_subject, use_ns):
210 item, nofollow = any_subject
211 lists_equal(xattr.list(item, nofollow=nofollow), [])
212 with pytest.raises(EnvironmentError):
214 xattr.remove(item, USER_NN, namespace=NAMESPACE,
217 xattr.remove(item, USER_ATTR, nofollow=nofollow)
219 def test_set_get_remove(subject, use_ns):
221 lists_equal(xattr.list(item), [])
223 xattr.set(item, USER_NN, USER_VAL,
226 xattr.set(item, USER_ATTR, USER_VAL)
228 assert xattr.list(item, namespace=NAMESPACE) == [USER_NN]
230 lists_equal(xattr.list(item), [USER_ATTR])
231 lists_equal(xattr.list(item, namespace=EMPTY_NS),
234 assert xattr.get(item, USER_NN, namespace=NAMESPACE) == USER_VAL
236 assert xattr.get(item, USER_ATTR) == USER_VAL
238 assert xattr.get_all(item, namespace=NAMESPACE) == \
239 [(USER_NN, USER_VAL)]
241 tuples_equal(xattr.get_all(item),
242 [(USER_ATTR, USER_VAL)])
244 xattr.remove(item, USER_NN, namespace=NAMESPACE)
246 xattr.remove(item, USER_ATTR)
247 lists_equal(xattr.list(item), [])
248 tuples_equal(xattr.get_all(item), [])
250 def test_replace_on_missing_deprecated(subject):
252 lists_equal(xattr.listxattr(item), [])
253 with pytest.raises(EnvironmentError):
254 xattr.setxattr(item, USER_ATTR, USER_VAL, XATTR_REPLACE)
256 def test_create_on_existing_deprecated(subject):
258 lists_equal(xattr.listxattr(item), [])
259 xattr.setxattr(item, USER_ATTR, USER_VAL, 0)
260 with pytest.raises(EnvironmentError):
261 xattr.setxattr(item, USER_ATTR, USER_VAL, XATTR_CREATE)
263 def test_remove_on_missing_deprecated(any_subject):
264 """check deprecated list, set, get operations against an item"""
265 item, nofollow = any_subject
266 lists_equal(xattr.listxattr(item, nofollow), [])
267 with pytest.raises(EnvironmentError):
268 xattr.removexattr(item, USER_ATTR)
270 def test_set_get_remove_deprecated(subject):
271 """check deprecated list, set, get operations against an item"""
273 lists_equal(xattr.listxattr(item), [])
274 xattr.setxattr(item, USER_ATTR, USER_VAL, 0)
275 lists_equal(xattr.listxattr(item), [USER_ATTR])
276 assert xattr.getxattr(item, USER_ATTR) == USER_VAL
277 tuples_equal(xattr.get_all(item), [(USER_ATTR, USER_VAL)])
278 xattr.removexattr(item, USER_ATTR)
279 lists_equal(xattr.listxattr(item), [])
280 tuples_equal(xattr.get_all(item), [])
282 def test_many_ops(subject):
285 xattr.set(item, USER_ATTR, USER_VAL)
288 for i in range(MANYOPS_COUNT):
289 lists_equal(xattr.list(item), VL)
290 lists_equal(xattr.list(item, namespace=EMPTY_NS), VL)
291 assert xattr.list(item, namespace=NAMESPACE) == VN
292 for i in range(MANYOPS_COUNT):
293 assert xattr.get(item, USER_ATTR) == USER_VAL
294 assert xattr.get(item, USER_NN, namespace=NAMESPACE) == USER_VAL
295 for i in range(MANYOPS_COUNT):
296 tuples_equal(xattr.get_all(item),
297 [(USER_ATTR, USER_VAL)])
298 assert xattr.get_all(item, namespace=NAMESPACE) == \
299 [(USER_NN, USER_VAL)]
301 def test_many_ops_deprecated(subject):
302 """test many ops (deprecated functions)"""
304 xattr.setxattr(item, USER_ATTR, USER_VAL)
306 for i in range(MANYOPS_COUNT):
307 lists_equal(xattr.listxattr(item), VL)
308 for i in range(MANYOPS_COUNT):
309 assert xattr.getxattr(item, USER_ATTR) == USER_VAL
310 for i in range(MANYOPS_COUNT):
311 tuples_equal(xattr.get_all(item),
312 [(USER_ATTR, USER_VAL)])
314 def test_no_attributes_deprecated(any_subject):
315 """test no attributes (deprecated functions)"""
316 item, nofollow = any_subject
317 lists_equal(xattr.listxattr(item, True), [])
318 tuples_equal(xattr.get_all(item, True), [])
319 with pytest.raises(EnvironmentError):
320 xattr.getxattr(item, USER_ATTR, True)
322 def test_no_attributes(any_subject):
323 """test no attributes"""
324 item, nofollow = any_subject
325 lists_equal(xattr.list(item, nofollow=nofollow), [])
326 assert xattr.list(item, nofollow=nofollow,
327 namespace=NAMESPACE) == []
328 tuples_equal(xattr.get_all(item, nofollow=nofollow), [])
329 assert xattr.get_all(item, nofollow=nofollow,
330 namespace=NAMESPACE) == []
331 with pytest.raises(EnvironmentError):
332 xattr.get(item, USER_NN, nofollow=nofollow,
335 def test_binary_payload_deprecated(subject):
336 """test binary values (deprecated functions)"""
339 xattr.setxattr(item, USER_ATTR, BINVAL)
340 lists_equal(xattr.listxattr(item), [USER_ATTR])
341 assert xattr.getxattr(item, USER_ATTR) == BINVAL
342 tuples_equal(xattr.get_all(item), [(USER_ATTR, BINVAL)])
343 xattr.removexattr(item, USER_ATTR)
345 def test_binary_payload(subject):
346 """test binary values"""
349 xattr.set(item, USER_ATTR, BINVAL)
350 lists_equal(xattr.list(item), [USER_ATTR])
351 assert xattr.list(item, namespace=NAMESPACE) == [USER_NN]
352 assert xattr.get(item, USER_ATTR) == BINVAL
353 assert xattr.get(item, USER_NN, namespace=NAMESPACE) == BINVAL
354 tuples_equal(xattr.get_all(item), [(USER_ATTR, BINVAL)])
355 assert xattr.get_all(item, namespace=NAMESPACE) == [(USER_NN, BINVAL)]
356 xattr.remove(item, USER_ATTR)
358 def test_symlinks_user_fail(testdir, use_dangling):
359 _, sname = get_symlink(testdir, dangling=use_dangling)
360 with pytest.raises(IOError):
361 xattr.set(sname, USER_ATTR, USER_VAL, nofollow=True)
362 with pytest.raises(IOError):
363 xattr.set(sname, USER_NN, USER_VAL, namespace=NAMESPACE,
365 with pytest.raises(IOError):
366 xattr.setxattr(sname, USER_ATTR, USER_VAL, XATTR_CREATE, True)
368 def test_none_namespace(subject):
369 with pytest.raises(TypeError):
370 xattr.get(subject[0], USER_ATTR, namespace=None)
372 @pytest.mark.parametrize(
374 [xattr.get, xattr.list, xattr.listxattr,
375 xattr.remove, xattr.removexattr,
376 xattr.set, xattr.setxattr,
377 xattr.get, xattr.getxattr])
378 def test_wrong_call(call):
379 with pytest.raises(TypeError):
382 @pytest.mark.parametrize(
383 "call, args", [(xattr.get, [USER_ATTR]),
384 (xattr.listxattr, []),
386 (xattr.remove, [USER_ATTR]),
387 (xattr.removexattr, [USER_ATTR]),
388 (xattr.get, [USER_ATTR]),
389 (xattr.getxattr, [USER_ATTR]),
390 (xattr.set, [USER_ATTR, USER_VAL]),
391 (xattr.setxattr, [USER_ATTR, USER_VAL])])
392 def test_wrong_argument_type(call, args):
393 with pytest.raises(TypeError):
394 call(object(), *args)