eb425402f4fd9a3302b706f3f4136b52424cb680
[rrq/fuse_xattrs.git] / test / tests.py
1 #!/usr/bin/env python3
2
3
4 # fuse_xattrs - Add xattrs support using sidecar files
5 #
6 # Copyright (C) 2016  Felipe Barriga Richards <felipe {at} felipebarriga.cl>
7 #
8 # This program can be distributed under the terms of the GNU GPL.
9 # See the file COPYING.
10
11
12 import unittest
13 import xattr
14 from pathlib import Path
15 import os
16
17 # TODO
18 # - listxattr: list too long
19 # - sidecar file permissions
20 # - corrupt metadata files
21
22
23 class TestXAttrs(unittest.TestCase):
24     def setUp(self):
25         self.randomFile = "./mount/tmp/foo.txt"
26         self.randomFileSidecar = "./mount/tmp/foo.txt.xattr"
27         if os.path.isfile(self.randomFile):
28             os.remove(self.randomFile)
29         Path(self.randomFile).touch()
30
31         if os.path.isfile(self.randomFileSidecar):
32             os.remove(self.randomFileSidecar)
33
34     def tearDown(self):
35         os.remove(self.randomFile)
36
37     def test_xattr_set(self):
38         xattr.setxattr(self.randomFile, "user.foo", bytes("bar", "utf-8"))
39
40     def test_xattr_set_name_max_length(self):
41         enc = "utf-8"
42         key = "user." + "x" * 250
43         value = "x"
44         self.assertEqual(len(key), 255)
45         xattr.setxattr(self.randomFile, key, bytes(value, enc))
46
47         key = "user." + "x" * 251
48         self.assertEqual(len(key), 256)
49         with self.assertRaises(OSError) as ex:
50             xattr.setxattr(self.randomFile, key, bytes(value, enc))
51         self.assertEqual(ex.exception.errno, 34)
52         self.assertEqual(ex.exception.strerror, "Numerical result out of range")
53
54     @unittest.expectedFailure
55     def test_xattr_set_value_max_size(self):
56         enc = "utf-8"
57         key = "user.foo"
58         value = "x" * (64 * 1024 + 1)  # we want max 64KiB of data
59         with self.assertRaises(OSError) as ex:
60             xattr.setxattr(self.randomFile, key, bytes(value, enc))
61
62         # on btrfs we get "no space left on device"
63         self.assertEqual(ex.exception.errno, 28)
64         self.assertEqual(ex.exception.strerror, "No space left on device")
65
66         # on fuse_xattr we get "Argument list too long"
67         # the error is thrown by fuse, not by fuse_xattr code
68
69     def test_xattr_set_namespaces(self):
70         with self.assertRaises(OSError) as ex:
71             xattr.setxattr(self.randomFile, "system.foo", bytes("bar", "utf-8"))
72         self.assertEqual(ex.exception.errno, 95)
73         self.assertEqual(ex.exception.strerror, "Operation not supported")
74
75         with self.assertRaises(OSError) as ex:
76             xattr.setxattr(self.randomFile, "trust.foo", bytes("bar", "utf-8"))
77         self.assertEqual(ex.exception.errno, 95)
78         self.assertEqual(ex.exception.strerror, "Operation not supported")
79
80         with self.assertRaises(OSError) as ex:
81             xattr.setxattr(self.randomFile, "foo.foo", bytes("bar", "utf-8"))
82         self.assertEqual(ex.exception.errno, 95)
83         self.assertEqual(ex.exception.strerror, "Operation not supported")
84
85         with self.assertRaises(PermissionError) as ex:
86             xattr.setxattr(self.randomFile, "security.foo", bytes("bar", "utf-8"))
87         self.assertEqual(ex.exception.errno, 1)
88         self.assertEqual(ex.exception.strerror, "Operation not permitted")
89
90     def test_xattr_get_non_existent(self):
91         key = "user.foo"
92         with self.assertRaises(OSError) as ex:
93             xattr.getxattr(self.randomFile, key)
94         self.assertEqual(ex.exception.errno, 61)
95         self.assertEqual(ex.exception.strerror, "No data available")
96
97     def test_xattr_get(self):
98         enc = "utf-8"
99         key = "user.foo"
100         value = "bar"
101         xattr.setxattr(self.randomFile, key, bytes(value, enc))
102         read_value = xattr.getxattr(self.randomFile, key)
103         self.assertEqual(value, read_value.decode(enc))
104
105     def test_xattr_set_override(self):
106         enc = "utf-8"
107         key = "user.foo"
108         value1 = "bar"
109         value2 = "rab"
110         xattr.setxattr(self.randomFile, key, bytes(value1, enc))
111         xattr.setxattr(self.randomFile, key, bytes(value2, enc))
112         read_value = xattr.getxattr(self.randomFile, key)
113         self.assertEqual(value2, read_value.decode(enc))
114
115     def test_xattr_set_create(self):
116         enc = "utf-8"
117         key = "user.foo"
118         value1 = "bar"
119         value2 = "rab"
120         xattr.setxattr(self.randomFile, key, bytes(value1, enc), xattr.XATTR_CREATE)
121         with self.assertRaises(FileExistsError) as ex:
122             xattr.setxattr(self.randomFile, key, bytes(value2, enc), xattr.XATTR_CREATE)
123         self.assertEqual(ex.exception.errno, 17)
124         self.assertEqual(ex.exception.strerror, "File exists")
125
126         read_value = xattr.getxattr(self.randomFile, key)
127         self.assertEqual(value1, read_value.decode(enc))
128
129     def test_xattr_set_replace(self):
130         enc = "utf-8"
131         key = "user.foo"
132         value = "bar"
133         with self.assertRaises(OSError) as ex:
134             xattr.setxattr(self.randomFile, key, bytes(value, enc), xattr.XATTR_REPLACE)
135         self.assertEqual(ex.exception.errno, 61)
136         self.assertEqual(ex.exception.strerror, "No data available")
137
138         with self.assertRaises(OSError) as ex:
139             xattr.getxattr(self.randomFile, key)
140         self.assertEqual(ex.exception.errno, 61)
141         self.assertEqual(ex.exception.strerror, "No data available")
142
143     def test_xattr_list_empty(self):
144         attrs = xattr.listxattr(self.randomFile)
145         self.assertEqual(len(attrs), 0)
146
147     def test_xattr_list(self):
148         enc = "utf-8"
149         key1 = "user.foo"
150         key2 = "user.foo2"
151         key3 = "user.foo3"
152         value = "bar"
153
154         # set 3 keys
155         xattr.setxattr(self.randomFile, key1, bytes(value, enc))
156         xattr.setxattr(self.randomFile, key2, bytes(value, enc))
157         xattr.setxattr(self.randomFile, key3, bytes(value, enc))
158
159         # get and check the list
160         attrs = xattr.listxattr(self.randomFile)
161
162         self.assertEqual(len(attrs), 3)
163         self.assertTrue(key1 in attrs)
164         self.assertTrue(key2 in attrs)
165         self.assertTrue(key3 in attrs)
166
167     def test_xattr_unicode(self):
168         enc = "utf-8"
169         key = "user.fooシüßてЙĘ𝄠✠"
170         value = "bar"
171
172         # set
173         xattr.setxattr(self.randomFile, key, bytes(value, enc))
174
175         # list
176         attrs = xattr.listxattr(self.randomFile)
177         self.assertEqual(len(attrs), 1)
178         self.assertTrue(key in attrs)
179
180         # read
181         read_value = xattr.getxattr(self.randomFile, key)
182         self.assertEqual(value, read_value.decode(enc))
183
184         # remove
185         xattr.removexattr(self.randomFile, key)
186
187     def test_xattr_remove(self):
188         enc = "utf-8"
189         key = "user.foo"
190         value = "bar"
191
192         # set
193         xattr.setxattr(self.randomFile, key, bytes(value, enc))
194
195         # remove
196         xattr.removexattr(self.randomFile, key)
197
198         # list should be empty
199         attrs = xattr.listxattr(self.randomFile)
200         self.assertEqual(len(attrs), 0)
201
202         # should fail when trying to read it
203         with self.assertRaises(OSError) as ex:
204             xattr.getxattr(self.randomFile, key)
205         self.assertEqual(ex.exception.errno, 61)
206         self.assertEqual(ex.exception.strerror, "No data available")
207
208         # removing twice should fail
209         with self.assertRaises(OSError) as ex:
210             xattr.getxattr(self.randomFile, key)
211         self.assertEqual(ex.exception.errno, 61)
212         self.assertEqual(ex.exception.strerror, "No data available")
213
214
215 if __name__ == '__main__':
216     unittest.main()