1#!/usr/bin/env python3 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', '/system/bin/sh') 49 self.assertEqual(aid.identifier, 'AID_FOO_BAR') 50 self.assertEqual(aid.value, '0xFF') 51 self.assertEqual(aid.found, 'myfakefile') 52 self.assertEqual(aid.normalized_value, '255') 53 self.assertEqual(aid.friendly, 'foo_bar') 54 self.assertEqual(aid.login_shell, '/system/bin/sh') 55 56 aid = AID('AID_MEDIA_EX', '1234', 'myfakefile', '/vendor/bin/sh') 57 self.assertEqual(aid.identifier, 'AID_MEDIA_EX') 58 self.assertEqual(aid.value, '1234') 59 self.assertEqual(aid.found, 'myfakefile') 60 self.assertEqual(aid.normalized_value, '1234') 61 self.assertEqual(aid.friendly, 'mediaex') 62 self.assertEqual(aid.login_shell, '/vendor/bin/sh') 63 64 def test_aid_header_parser_good(self): 65 """Test AID Header Parser good input file""" 66 67 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 68 temp_file.write( 69 textwrap.dedent(""" 70 #define AID_FOO 1000 71 #define AID_BAR 1001 72 #define SOMETHING "something" 73 #define AID_OEM_RESERVED_START 2900 74 #define AID_OEM_RESERVED_END 2999 75 #define AID_OEM_RESERVED_1_START 7000 76 #define AID_OEM_RESERVED_1_END 8000 77 """)) 78 temp_file.flush() 79 80 parser = AIDHeaderParser(temp_file.name) 81 ranges = parser.ranges 82 aids = parser.aids 83 84 self.assertTrue((2900, 2999) in ranges["vendor"]) 85 self.assertFalse((5000, 6000) in ranges["vendor"]) 86 87 for aid in aids: 88 self.assertTrue(aid.normalized_value in ['1000', '1001']) 89 self.assertFalse(aid.normalized_value in ['1', '2', '3']) 90 91 def test_aid_header_parser_good_unordered(self): 92 """Test AID Header Parser good unordered input file""" 93 94 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 95 temp_file.write( 96 textwrap.dedent(""" 97 #define AID_FOO 1000 98 #define AID_OEM_RESERVED_1_END 8000 99 #define AID_BAR 1001 100 #define SOMETHING "something" 101 #define AID_OEM_RESERVED_END 2999 102 #define AID_OEM_RESERVED_1_START 7000 103 #define AID_OEM_RESERVED_START 2900 104 """)) 105 temp_file.flush() 106 107 parser = AIDHeaderParser(temp_file.name) 108 ranges = parser.ranges 109 aids = parser.aids 110 111 self.assertTrue((2900, 2999) in ranges["vendor"]) 112 self.assertFalse((5000, 6000) in ranges["vendor"]) 113 114 for aid in aids: 115 self.assertTrue(aid.normalized_value in ['1000', '1001']) 116 self.assertFalse(aid.normalized_value in ['1', '2', '3']) 117 118 def test_aid_header_parser_bad_aid(self): 119 """Test AID Header Parser bad aid input file""" 120 121 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 122 temp_file.write( 123 textwrap.dedent(""" 124 #define AID_FOO "bad" 125 """)) 126 temp_file.flush() 127 128 with self.assertRaises(SystemExit): 129 AIDHeaderParser(temp_file.name) 130 131 def test_aid_header_parser_bad_oem_range(self): 132 """Test AID Header Parser bad oem range input file""" 133 134 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 135 temp_file.write( 136 textwrap.dedent(""" 137 #define AID_OEM_RESERVED_START 2900 138 #define AID_OEM_RESERVED_END 1800 139 """)) 140 temp_file.flush() 141 142 with self.assertRaises(SystemExit): 143 AIDHeaderParser(temp_file.name) 144 145 def test_aid_header_parser_bad_oem_range_no_end(self): 146 """Test AID Header Parser bad oem range (no end) input file""" 147 148 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 149 temp_file.write( 150 textwrap.dedent(""" 151 #define AID_OEM_RESERVED_START 2900 152 """)) 153 temp_file.flush() 154 155 with self.assertRaises(SystemExit): 156 AIDHeaderParser(temp_file.name) 157 158 def test_aid_header_parser_bad_oem_range_no_start(self): 159 """Test AID Header Parser bad oem range (no start) input file""" 160 161 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 162 temp_file.write( 163 textwrap.dedent(""" 164 #define AID_OEM_RESERVED_END 2900 165 """)) 166 temp_file.flush() 167 168 with self.assertRaises(SystemExit): 169 AIDHeaderParser(temp_file.name) 170 171 def test_aid_header_parser_bad_oem_range_duplicated(self): 172 """Test AID Header Parser bad oem range (no start) input file""" 173 174 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 175 temp_file.write( 176 textwrap.dedent(""" 177 #define AID_OEM_RESERVED_START 2000 178 #define AID_OEM_RESERVED_END 2900 179 #define AID_OEM_RESERVED_START 3000 180 #define AID_OEM_RESERVED_END 3900 181 """)) 182 temp_file.flush() 183 184 with self.assertRaises(SystemExit): 185 AIDHeaderParser(temp_file.name) 186 187 def test_aid_header_parser_bad_oem_range_mismatch_start_end(self): 188 """Test AID Header Parser bad oem range mismatched input file""" 189 190 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 191 temp_file.write( 192 textwrap.dedent(""" 193 #define AID_OEM_RESERVED_START 2900 194 #define AID_OEM_RESERVED_2_END 2900 195 """)) 196 temp_file.flush() 197 198 with self.assertRaises(SystemExit): 199 AIDHeaderParser(temp_file.name) 200 201 def test_aid_header_parser_bad_duplicate_ranges(self): 202 """Test AID Header Parser exits cleanly on duplicate AIDs""" 203 204 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 205 temp_file.write( 206 textwrap.dedent(""" 207 #define AID_FOO 100 208 #define AID_BAR 100 209 """)) 210 temp_file.flush() 211 212 with self.assertRaises(SystemExit): 213 AIDHeaderParser(temp_file.name) 214 215 def test_aid_header_parser_no_bad_aids(self): 216 """Test AID Header Parser that it doesn't contain: 217 Ranges, ie things the end with "_START" or "_END" 218 AID_APP 219 AID_USER 220 For more details see: 221 - https://android-review.googlesource.com/#/c/313024 222 - https://android-review.googlesource.com/#/c/313169 223 """ 224 225 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 226 temp_file.write( 227 textwrap.dedent(""" 228 #define AID_APP 10000 /* TODO: switch users over to AID_APP_START */ 229 #define AID_APP_START 10000 /* first app user */ 230 #define AID_APP_END 19999 /* last app user */ 231 232 #define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ 233 #define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */ 234 235 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ 236 #define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */ 237 238 #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */ 239 #define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ 240 241 #define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */ 242 #define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */ 243 """)) 244 temp_file.flush() 245 246 parser = AIDHeaderParser(temp_file.name) 247 aids = parser.aids 248 249 bad_aids = ['_START', '_END', 'AID_APP', 'AID_USER'] 250 251 for aid in aids: 252 self.assertFalse( 253 any(bad in aid.identifier for bad in bad_aids), 254 'Not expecting keywords "%s" in aids "%s"' % 255 (str(bad_aids), str([tmp.identifier for tmp in aids]))) 256 257 def test_fs_config_file_parser_good(self): 258 """Test FSConfig Parser good input file""" 259 260 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 261 temp_file.write( 262 textwrap.dedent(""" 263 [/system/bin/file] 264 user: AID_FOO 265 group: AID_SYSTEM 266 mode: 0777 267 caps: BLOCK_SUSPEND 268 269 [/vendor/path/dir/] 270 user: AID_FOO 271 group: AID_SYSTEM 272 mode: 0777 273 caps: 0 274 275 [AID_OEM1] 276 # 5001 in base16 277 value: 0x1389 278 """)) 279 temp_file.flush() 280 281 parser = FSConfigFileParser([temp_file.name], {"oem1": [(5000, 5999)]}) 282 files = parser.files 283 dirs = parser.dirs 284 aids = parser.aids 285 286 self.assertEqual(len(files), 1) 287 self.assertEqual(len(dirs), 1) 288 self.assertEqual(len(aids), 1) 289 290 aid = aids[0] 291 fcap = files[0] 292 dcap = dirs[0] 293 294 self.assertEqual(fcap, 295 FSConfig('0777', 'AID_FOO', 'AID_SYSTEM', 296 'CAP_BLOCK_SUSPEND', 297 '/system/bin/file', temp_file.name)) 298 299 self.assertEqual(dcap, 300 FSConfig('0777', 'AID_FOO', 'AID_SYSTEM', '0', 301 '/vendor/path/dir/', temp_file.name)) 302 303 self.assertEqual(aid, AID('AID_OEM1', '0x1389', temp_file.name, '/bin/sh')) 304 305 def test_fs_config_file_parser_bad(self): 306 """Test FSConfig Parser bad input file""" 307 308 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 309 temp_file.write( 310 textwrap.dedent(""" 311 [/system/bin/file] 312 caps: BLOCK_SUSPEND 313 """)) 314 temp_file.flush() 315 316 with self.assertRaises(SystemExit): 317 FSConfigFileParser([temp_file.name], {}) 318 319 def test_fs_config_file_parser_bad_aid_range(self): 320 """Test FSConfig Parser bad aid range value input file""" 321 322 with tempfile.NamedTemporaryFile(mode='w') as temp_file: 323 temp_file.write( 324 textwrap.dedent(""" 325 [AID_OEM1] 326 value: 25 327 """)) 328 temp_file.flush() 329 330 with self.assertRaises(SystemExit): 331 FSConfigFileParser([temp_file.name], {"oem1": [(5000, 5999)]}) 332 333if __name__ == "__main__": 334 unittest.main() 335