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', '/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() 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 oem_ranges = parser.oem_ranges 82 aids = parser.aids 83 84 self.assertTrue((2900, 2999) in oem_ranges) 85 self.assertFalse((5000, 6000) in oem_ranges) 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() 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 oem_ranges = parser.oem_ranges 109 aids = parser.aids 110 111 self.assertTrue((2900, 2999) in oem_ranges) 112 self.assertFalse((5000, 6000) in oem_ranges) 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() 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() 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() 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() 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_mismatch_start_end(self): 172 """Test AID Header Parser bad oem range mismatched input file""" 173 174 with tempfile.NamedTemporaryFile() as temp_file: 175 temp_file.write( 176 textwrap.dedent(""" 177 #define AID_OEM_RESERVED_START 2900 178 #define AID_OEM_RESERVED_2_END 2900 179 """)) 180 temp_file.flush() 181 182 with self.assertRaises(SystemExit): 183 AIDHeaderParser(temp_file.name) 184 185 def test_aid_header_parser_bad_duplicate_ranges(self): 186 """Test AID Header Parser exits cleanly on duplicate AIDs""" 187 188 with tempfile.NamedTemporaryFile() as temp_file: 189 temp_file.write( 190 textwrap.dedent(""" 191 #define AID_FOO 100 192 #define AID_BAR 100 193 """)) 194 temp_file.flush() 195 196 with self.assertRaises(SystemExit): 197 AIDHeaderParser(temp_file.name) 198 199 def test_aid_header_parser_no_bad_aids(self): 200 """Test AID Header Parser that it doesn't contain: 201 Ranges, ie things the end with "_START" or "_END" 202 AID_APP 203 AID_USER 204 For more details see: 205 - https://android-review.googlesource.com/#/c/313024 206 - https://android-review.googlesource.com/#/c/313169 207 """ 208 209 with tempfile.NamedTemporaryFile() as temp_file: 210 temp_file.write( 211 textwrap.dedent(""" 212 #define AID_APP 10000 /* TODO: switch users over to AID_APP_START */ 213 #define AID_APP_START 10000 /* first app user */ 214 #define AID_APP_END 19999 /* last app user */ 215 216 #define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ 217 #define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */ 218 219 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ 220 #define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */ 221 222 #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */ 223 #define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ 224 225 #define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */ 226 #define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */ 227 """)) 228 temp_file.flush() 229 230 parser = AIDHeaderParser(temp_file.name) 231 aids = parser.aids 232 233 bad_aids = ['_START', '_END', 'AID_APP', 'AID_USER'] 234 235 for aid in aids: 236 self.assertFalse( 237 any(bad in aid.identifier for bad in bad_aids), 238 'Not expecting keywords "%s" in aids "%s"' % 239 (str(bad_aids), str([tmp.identifier for tmp in aids]))) 240 241 def test_fs_config_file_parser_good(self): 242 """Test FSConfig Parser good input file""" 243 244 with tempfile.NamedTemporaryFile() as temp_file: 245 temp_file.write( 246 textwrap.dedent(""" 247 [/system/bin/file] 248 user: AID_FOO 249 group: AID_SYSTEM 250 mode: 0777 251 caps: BLOCK_SUSPEND 252 253 [/vendor/path/dir/] 254 user: AID_FOO 255 group: AID_SYSTEM 256 mode: 0777 257 caps: 0 258 259 [AID_OEM1] 260 # 5001 in base16 261 value: 0x1389 262 """)) 263 temp_file.flush() 264 265 parser = FSConfigFileParser([temp_file.name], [(5000, 5999)]) 266 files = parser.files 267 dirs = parser.dirs 268 aids = parser.aids 269 270 self.assertEqual(len(files), 1) 271 self.assertEqual(len(dirs), 1) 272 self.assertEqual(len(aids), 1) 273 274 aid = aids[0] 275 fcap = files[0] 276 dcap = dirs[0] 277 278 self.assertEqual(fcap, 279 FSConfig('0777', 'AID_FOO', 'AID_SYSTEM', 280 'CAP_BLOCK_SUSPEND', 281 '/system/bin/file', temp_file.name)) 282 283 self.assertEqual(dcap, 284 FSConfig('0777', 'AID_FOO', 'AID_SYSTEM', '0', 285 '/vendor/path/dir/', temp_file.name)) 286 287 self.assertEqual(aid, AID('AID_OEM1', '0x1389', temp_file.name, '/vendor/bin/sh')) 288 289 def test_fs_config_file_parser_bad(self): 290 """Test FSConfig Parser bad input file""" 291 292 with tempfile.NamedTemporaryFile() as temp_file: 293 temp_file.write( 294 textwrap.dedent(""" 295 [/system/bin/file] 296 caps: BLOCK_SUSPEND 297 """)) 298 temp_file.flush() 299 300 with self.assertRaises(SystemExit): 301 FSConfigFileParser([temp_file.name], [(5000, 5999)]) 302 303 def test_fs_config_file_parser_bad_aid_range(self): 304 """Test FSConfig Parser bad aid range value input file""" 305 306 with tempfile.NamedTemporaryFile() as temp_file: 307 temp_file.write( 308 textwrap.dedent(""" 309 [AID_OEM1] 310 value: 25 311 """)) 312 temp_file.flush() 313 314 with self.assertRaises(SystemExit): 315 FSConfigFileParser([temp_file.name], [(5000, 5999)]) 316