1#!/usr/bin/env python
2"""Unit test suite for the fs_config_genertor.py tool."""
3
4import tempfile
5import textwrap
6import unittest
7
8from fs_config_generator import AID
9from fs_config_generator import AIDHeaderParser
10from fs_config_generator import FSConfigFileParser
11from fs_config_generator import FSConfig
12from fs_config_generator import Utils
13
14
15# Disable protected access so we can test class internal
16# methods. Also, disable invalid-name as some of the
17# class method names are over length.
18# pylint: disable=protected-access,invalid-name
19class Tests(unittest.TestCase):
20    """Test class for unit tests"""
21
22    def test_is_overlap(self):
23        """Test overlap detection helper"""
24
25        self.assertTrue(AIDHeaderParser._is_overlap((0, 1), (1, 2)))
26
27        self.assertTrue(AIDHeaderParser._is_overlap((0, 100), (90, 200)))
28
29        self.assertTrue(AIDHeaderParser._is_overlap((20, 50), (1, 101)))
30
31        self.assertFalse(AIDHeaderParser._is_overlap((0, 100), (101, 200)))
32
33        self.assertFalse(AIDHeaderParser._is_overlap((-10, 0), (10, 20)))
34
35    def test_in_any_range(self):
36        """Test if value in range"""
37
38        self.assertFalse(Utils.in_any_range(50, [(100, 200), (1, 2), (1, 1)]))
39        self.assertFalse(Utils.in_any_range(250, [(100, 200), (1, 2), (1, 1)]))
40
41        self.assertTrue(Utils.in_any_range(100, [(100, 200), (1, 2), (1, 1)]))
42        self.assertTrue(Utils.in_any_range(200, [(100, 200), (1, 2), (1, 1)]))
43        self.assertTrue(Utils.in_any_range(150, [(100, 200)]))
44
45    def test_aid(self):
46        """Test AID class constructor"""
47
48        aid = AID('AID_FOO_BAR', '0xFF', 'myfakefile')
49        self.assertEquals(aid.identifier, 'AID_FOO_BAR')
50        self.assertEquals(aid.value, '0xFF')
51        self.assertEquals(aid.found, 'myfakefile')
52        self.assertEquals(aid.normalized_value, '255')
53        self.assertEquals(aid.friendly, 'foo_bar')
54
55        aid = AID('AID_MEDIA_EX', '1234', 'myfakefile')
56        self.assertEquals(aid.identifier, 'AID_MEDIA_EX')
57        self.assertEquals(aid.value, '1234')
58        self.assertEquals(aid.found, 'myfakefile')
59        self.assertEquals(aid.normalized_value, '1234')
60        self.assertEquals(aid.friendly, 'mediaex')
61
62    def test_aid_header_parser_good(self):
63        """Test AID Header Parser good input file"""
64
65        with tempfile.NamedTemporaryFile() as temp_file:
66            temp_file.write(
67                textwrap.dedent("""
68                #define AID_FOO 1000
69                #define AID_BAR 1001
70                #define SOMETHING "something"
71                #define AID_OEM_RESERVED_START 2900
72                #define AID_OEM_RESERVED_END   2999
73                #define AID_OEM_RESERVED_1_START  7000
74                #define AID_OEM_RESERVED_1_END    8000
75            """))
76            temp_file.flush()
77
78            parser = AIDHeaderParser(temp_file.name)
79            oem_ranges = parser.oem_ranges
80            aids = parser.aids
81
82            self.assertTrue((2900, 2999) in oem_ranges)
83            self.assertFalse((5000, 6000) in oem_ranges)
84
85            for aid in aids:
86                self.assertTrue(aid.normalized_value in ['1000', '1001'])
87                self.assertFalse(aid.normalized_value in ['1', '2', '3'])
88
89    def test_aid_header_parser_good_unordered(self):
90        """Test AID Header Parser good unordered input file"""
91
92        with tempfile.NamedTemporaryFile() as temp_file:
93            temp_file.write(
94                textwrap.dedent("""
95                #define AID_FOO 1000
96                #define AID_OEM_RESERVED_1_END    8000
97                #define AID_BAR 1001
98                #define SOMETHING "something"
99                #define AID_OEM_RESERVED_END   2999
100                #define AID_OEM_RESERVED_1_START  7000
101                #define AID_OEM_RESERVED_START 2900
102            """))
103            temp_file.flush()
104
105            parser = AIDHeaderParser(temp_file.name)
106            oem_ranges = parser.oem_ranges
107            aids = parser.aids
108
109            self.assertTrue((2900, 2999) in oem_ranges)
110            self.assertFalse((5000, 6000) in oem_ranges)
111
112            for aid in aids:
113                self.assertTrue(aid.normalized_value in ['1000', '1001'])
114                self.assertFalse(aid.normalized_value in ['1', '2', '3'])
115
116    def test_aid_header_parser_bad_aid(self):
117        """Test AID Header Parser bad aid input file"""
118
119        with tempfile.NamedTemporaryFile() as temp_file:
120            temp_file.write(
121                textwrap.dedent("""
122                #define AID_FOO "bad"
123            """))
124            temp_file.flush()
125
126            with self.assertRaises(SystemExit):
127                AIDHeaderParser(temp_file.name)
128
129    def test_aid_header_parser_bad_oem_range(self):
130        """Test AID Header Parser bad oem range input file"""
131
132        with tempfile.NamedTemporaryFile() as temp_file:
133            temp_file.write(
134                textwrap.dedent("""
135                #define AID_OEM_RESERVED_START 2900
136                #define AID_OEM_RESERVED_END   1800
137            """))
138            temp_file.flush()
139
140            with self.assertRaises(SystemExit):
141                AIDHeaderParser(temp_file.name)
142
143    def test_aid_header_parser_bad_oem_range_no_end(self):
144        """Test AID Header Parser bad oem range (no end) input file"""
145
146        with tempfile.NamedTemporaryFile() as temp_file:
147            temp_file.write(
148                textwrap.dedent("""
149                #define AID_OEM_RESERVED_START 2900
150            """))
151            temp_file.flush()
152
153            with self.assertRaises(SystemExit):
154                AIDHeaderParser(temp_file.name)
155
156    def test_aid_header_parser_bad_oem_range_no_start(self):
157        """Test AID Header Parser bad oem range (no start) input file"""
158
159        with tempfile.NamedTemporaryFile() as temp_file:
160            temp_file.write(
161                textwrap.dedent("""
162                #define AID_OEM_RESERVED_END 2900
163            """))
164            temp_file.flush()
165
166            with self.assertRaises(SystemExit):
167                AIDHeaderParser(temp_file.name)
168
169    def test_aid_header_parser_bad_oem_range_mismatch_start_end(self):
170        """Test AID Header Parser bad oem range mismatched input file"""
171
172        with tempfile.NamedTemporaryFile() as temp_file:
173            temp_file.write(
174                textwrap.dedent("""
175                #define AID_OEM_RESERVED_START 2900
176                #define AID_OEM_RESERVED_2_END 2900
177            """))
178            temp_file.flush()
179
180            with self.assertRaises(SystemExit):
181                AIDHeaderParser(temp_file.name)
182
183    def test_aid_header_parser_bad_duplicate_ranges(self):
184        """Test AID Header Parser exits cleanly on duplicate AIDs"""
185
186        with tempfile.NamedTemporaryFile() as temp_file:
187            temp_file.write(
188                textwrap.dedent("""
189                #define AID_FOO 100
190                #define AID_BAR 100
191            """))
192            temp_file.flush()
193
194            with self.assertRaises(SystemExit):
195                AIDHeaderParser(temp_file.name)
196
197    def test_aid_header_parser_no_bad_aids(self):
198        """Test AID Header Parser that it doesn't contain:
199        Ranges, ie things the end with "_START" or "_END"
200        AID_APP
201        AID_USER
202        For more details see:
203          - https://android-review.googlesource.com/#/c/313024
204          - https://android-review.googlesource.com/#/c/313169
205        """
206
207        with tempfile.NamedTemporaryFile() as temp_file:
208            temp_file.write(
209                textwrap.dedent("""
210                #define AID_APP              10000 /* TODO: switch users over to AID_APP_START */
211                #define AID_APP_START        10000 /* first app user */
212                #define AID_APP_END          19999 /* last app user */
213
214                #define AID_CACHE_GID_START  20000 /* start of gids for apps to mark cached data */
215                #define AID_CACHE_GID_END    29999 /* end of gids for apps to mark cached data */
216
217                #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
218                #define AID_SHARED_GID_END   59999 /* end of gids for apps in each user to share */
219
220                #define AID_ISOLATED_START   99000 /* start of uids for fully isolated sandboxed processes */
221                #define AID_ISOLATED_END     99999 /* end of uids for fully isolated sandboxed processes */
222
223                #define AID_USER            100000 /* TODO: switch users over to AID_USER_OFFSET */
224                #define AID_USER_OFFSET     100000 /* offset for uid ranges for each user */
225            """))
226            temp_file.flush()
227
228            parser = AIDHeaderParser(temp_file.name)
229            aids = parser.aids
230
231            bad_aids = ['_START', '_END', 'AID_APP', 'AID_USER']
232
233            for aid in aids:
234                self.assertFalse(
235                    any(bad in aid.identifier for bad in bad_aids),
236                    'Not expecting keywords "%s" in aids "%s"' %
237                    (str(bad_aids), str([tmp.identifier for tmp in aids])))
238
239    def test_fs_config_file_parser_good(self):
240        """Test FSConfig Parser good input file"""
241
242        with tempfile.NamedTemporaryFile() as temp_file:
243            temp_file.write(
244                textwrap.dedent("""
245                [/system/bin/file]
246                user: AID_FOO
247                group: AID_SYSTEM
248                mode: 0777
249                caps: BLOCK_SUSPEND
250
251                [/vendor/path/dir/]
252                user: AID_FOO
253                group: AID_SYSTEM
254                mode: 0777
255                caps: 0
256
257                [AID_OEM1]
258                # 5001 in base16
259                value: 0x1389
260            """))
261            temp_file.flush()
262
263            parser = FSConfigFileParser([temp_file.name], [(5000, 5999)])
264            files = parser.files
265            dirs = parser.dirs
266            aids = parser.aids
267
268            self.assertEquals(len(files), 1)
269            self.assertEquals(len(dirs), 1)
270            self.assertEquals(len(aids), 1)
271
272            aid = aids[0]
273            fcap = files[0]
274            dcap = dirs[0]
275
276            self.assertEqual(fcap,
277                             FSConfig('0777', 'AID_FOO', 'AID_SYSTEM',
278                                      '(1ULL << CAP_BLOCK_SUSPEND)',
279                                      '/system/bin/file', temp_file.name))
280
281            self.assertEqual(dcap,
282                             FSConfig('0777', 'AID_FOO', 'AID_SYSTEM', '(0)',
283                                      '/vendor/path/dir/', temp_file.name))
284
285            self.assertEqual(aid, AID('AID_OEM1', '0x1389', temp_file.name))
286
287    def test_fs_config_file_parser_bad(self):
288        """Test FSConfig Parser bad input file"""
289
290        with tempfile.NamedTemporaryFile() as temp_file:
291            temp_file.write(
292                textwrap.dedent("""
293                [/system/bin/file]
294                caps: BLOCK_SUSPEND
295            """))
296            temp_file.flush()
297
298            with self.assertRaises(SystemExit):
299                FSConfigFileParser([temp_file.name], [(5000, 5999)])
300
301    def test_fs_config_file_parser_bad_aid_range(self):
302        """Test FSConfig Parser bad aid range value input file"""
303
304        with tempfile.NamedTemporaryFile() as temp_file:
305            temp_file.write(
306                textwrap.dedent("""
307                [AID_OEM1]
308                value: 25
309            """))
310            temp_file.flush()
311
312            with self.assertRaises(SystemExit):
313                FSConfigFileParser([temp_file.name], [(5000, 5999)])
314