21700: Install Bundler system-wide in Rails postinst
[arvados.git] / services / fuse / tests / test_inodes.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 import arvados_fuse
6 import unittest
7 import llfuse
8 import logging
9
10 from unittest import mock
11
12 class InodeTests(unittest.TestCase):
13
14     # The following tests call next(inodes._counter) because inode 1
15     # (the root directory) gets special treatment.
16
17     def test_inodes_basic(self):
18         cache = arvados_fuse.InodeCache(1000, 4)
19         inodes = arvados_fuse.Inodes(cache)
20         next(inodes._counter)
21
22         # Check that ent1 gets added to inodes
23         ent1 = mock.MagicMock()
24         ent1.in_use.return_value = False
25         ent1.has_ref.return_value = False
26         ent1.persisted.return_value = True
27         ent1.objsize.return_value = 500
28         inodes.add_entry(ent1)
29         self.assertIn(ent1.inode, inodes)
30         self.assertIs(inodes[ent1.inode], ent1)
31         self.assertEqual(500, cache.total())
32
33     def test_inodes_not_persisted(self):
34         cache = arvados_fuse.InodeCache(1000, 4)
35         inodes = arvados_fuse.Inodes(cache)
36         next(inodes._counter)
37
38         ent1 = mock.MagicMock()
39         ent1.in_use.return_value = False
40         ent1.has_ref.return_value = False
41         ent1.persisted.return_value = True
42         ent1.objsize.return_value = 500
43         inodes.add_entry(ent1)
44
45         # ent2 is not persisted, so it doesn't
46         # affect the cache total
47         ent2 = mock.MagicMock()
48         ent2.in_use.return_value = False
49         ent2.has_ref.return_value = False
50         ent2.persisted.return_value = False
51         ent2.objsize.return_value = 600
52         inodes.add_entry(ent2)
53         self.assertEqual(500, cache.total())
54
55     def test_inode_cleared(self):
56         cache = arvados_fuse.InodeCache(1000, 4)
57         inodes = arvados_fuse.Inodes(cache)
58         next(inodes._counter)
59
60         # Check that ent1 gets added to inodes
61         ent1 = mock.MagicMock()
62         ent1.in_use.return_value = False
63         ent1.has_ref.return_value = False
64         ent1.persisted.return_value = True
65         ent1.objsize.return_value = 500
66         inodes.add_entry(ent1)
67
68         # ent3 is persisted, adding it should cause ent1 to get cleared
69         ent3 = mock.MagicMock()
70         ent3.in_use.return_value = False
71         ent3.has_ref.return_value = False
72         ent3.persisted.return_value = True
73         ent3.objsize.return_value = 600
74
75         self.assertFalse(ent1.clear.called)
76         inodes.add_entry(ent3)
77
78         # Won't clear anything because min_entries = 4
79         self.assertEqual(2, len(cache._cache_entries))
80         self.assertFalse(ent1.clear.called)
81         self.assertEqual(1100, cache.total())
82
83         # Change min_entries
84         cache.min_entries = 1
85         ent1.parent_inode = None
86         inodes.cap_cache()
87         inodes.wait_remove_queue_empty()
88         self.assertEqual(600, cache.total())
89         self.assertTrue(ent1.clear.called)
90
91         # Touching ent1 should cause ent3 to get cleared
92         ent3.parent_inode = None
93         self.assertFalse(ent3.clear.called)
94         inodes.inode_cache.update_cache_size(ent1)
95         inodes.touch(ent1)
96         inodes.wait_remove_queue_empty()
97         self.assertTrue(ent3.clear.called)
98         self.assertEqual(500, cache.total())
99
100     def test_clear_in_use(self):
101         cache = arvados_fuse.InodeCache(1000, 4)
102         inodes = arvados_fuse.Inodes(cache)
103         next(inodes._counter)
104
105         ent1 = mock.MagicMock()
106         ent1.in_use.return_value = True
107         ent1.has_ref.return_value = False
108         ent1.persisted.return_value = True
109         ent1.objsize.return_value = 500
110         inodes.add_entry(ent1)
111
112         ent3 = mock.MagicMock()
113         ent3.in_use.return_value = False
114         ent3.has_ref.return_value = True
115         ent3.persisted.return_value = True
116         ent3.objsize.return_value = 600
117         inodes.add_entry(ent3)
118
119         cache.min_entries = 1
120
121         # ent1, ent3 in use, has ref, can't be cleared
122         ent1.clear.called = False
123         ent3.clear.called = False
124         self.assertFalse(ent1.clear.called)
125         self.assertFalse(ent3.clear.called)
126         inodes.touch(ent3)
127         inodes.wait_remove_queue_empty()
128         self.assertFalse(ent1.clear.called)
129         self.assertFalse(ent3.clear.called)
130         # kernel invalidate gets called anyway
131         self.assertTrue(ent3.kernel_invalidate.called)
132         self.assertEqual(1100, cache.total())
133
134         # ent1 still in use, ent3 doesn't have ref,
135         # so ent3 gets cleared
136         ent3.has_ref.return_value = False
137         ent1.clear.called = False
138         ent3.clear.called = False
139         ent3.parent_inode = None
140         inodes.touch(ent3)
141         inodes.wait_remove_queue_empty()
142         self.assertFalse(ent1.clear.called)
143         self.assertTrue(ent3.clear.called)
144         self.assertEqual(500, cache.total())
145
146     def test_delete(self):
147         cache = arvados_fuse.InodeCache(1000, 0)
148         inodes = arvados_fuse.Inodes(cache)
149         next(inodes._counter)
150
151         ent1 = mock.MagicMock()
152         ent1.in_use.return_value = False
153         ent1.has_ref.return_value = False
154         ent1.persisted.return_value = True
155         ent1.objsize.return_value = 500
156         inodes.add_entry(ent1)
157
158         ent3 = mock.MagicMock()
159         ent3.in_use.return_value = False
160         ent3.has_ref.return_value = False
161         ent3.persisted.return_value = True
162         ent3.objsize.return_value = 600
163
164         # Delete ent1
165         self.assertEqual(500, cache.total())
166         ent1.ref_count = 0
167         with llfuse.lock:
168             inodes.del_entry(ent1)
169         inodes.wait_remove_queue_empty()
170         self.assertEqual(0, cache.total())
171
172         inodes.add_entry(ent3)
173         inodes.wait_remove_queue_empty()
174         self.assertEqual(600, cache.total())