1#!/usr/bin/env python 2# 3# Copyright (C) 2018 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""Unit tests for manifest_fixer_test.py.""" 18 19import StringIO 20import sys 21import unittest 22from xml.dom import minidom 23 24import manifest_fixer 25 26sys.dont_write_bytecode = True 27 28 29class CompareVersionGtTest(unittest.TestCase): 30 """Unit tests for compare_version_gt function.""" 31 32 def test_sdk(self): 33 """Test comparing sdk versions.""" 34 self.assertTrue(manifest_fixer.compare_version_gt('28', '27')) 35 self.assertFalse(manifest_fixer.compare_version_gt('27', '28')) 36 self.assertFalse(manifest_fixer.compare_version_gt('28', '28')) 37 38 def test_codename(self): 39 """Test comparing codenames.""" 40 self.assertTrue(manifest_fixer.compare_version_gt('Q', 'P')) 41 self.assertFalse(manifest_fixer.compare_version_gt('P', 'Q')) 42 self.assertFalse(manifest_fixer.compare_version_gt('Q', 'Q')) 43 44 def test_sdk_codename(self): 45 """Test comparing sdk versions with codenames.""" 46 self.assertTrue(manifest_fixer.compare_version_gt('Q', '28')) 47 self.assertFalse(manifest_fixer.compare_version_gt('28', 'Q')) 48 49 def test_compare_numeric(self): 50 """Test that numbers are compared in numeric and not lexicographic order.""" 51 self.assertTrue(manifest_fixer.compare_version_gt('18', '8')) 52 53 54class RaiseMinSdkVersionTest(unittest.TestCase): 55 """Unit tests for raise_min_sdk_version function.""" 56 57 def raise_min_sdk_version_test(self, input_manifest, min_sdk_version, 58 target_sdk_version, library): 59 doc = minidom.parseString(input_manifest) 60 manifest_fixer.raise_min_sdk_version(doc, min_sdk_version, 61 target_sdk_version, library) 62 output = StringIO.StringIO() 63 manifest_fixer.write_xml(output, doc) 64 return output.getvalue() 65 66 manifest_tmpl = ( 67 '<?xml version="1.0" encoding="utf-8"?>\n' 68 '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' 69 '%s' 70 '</manifest>\n') 71 72 # pylint: disable=redefined-builtin 73 def uses_sdk(self, min=None, target=None, extra=''): 74 attrs = '' 75 if min: 76 attrs += ' android:minSdkVersion="%s"' % (min) 77 if target: 78 attrs += ' android:targetSdkVersion="%s"' % (target) 79 if extra: 80 attrs += ' ' + extra 81 return ' <uses-sdk%s/>\n' % (attrs) 82 83 def test_no_uses_sdk(self): 84 """Tests inserting a uses-sdk element into a manifest.""" 85 86 manifest_input = self.manifest_tmpl % '' 87 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28') 88 output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) 89 self.assertEqual(output, expected) 90 91 def test_no_min(self): 92 """Tests inserting a minSdkVersion attribute into a uses-sdk element.""" 93 94 manifest_input = self.manifest_tmpl % ' <uses-sdk extra="foo"/>\n' 95 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28', 96 extra='extra="foo"') 97 output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) 98 self.assertEqual(output, expected) 99 100 def test_raise_min(self): 101 """Tests inserting a minSdkVersion attribute into a uses-sdk element.""" 102 103 manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') 104 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28') 105 output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) 106 self.assertEqual(output, expected) 107 108 def test_raise(self): 109 """Tests raising a minSdkVersion attribute.""" 110 111 manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') 112 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28') 113 output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) 114 self.assertEqual(output, expected) 115 116 def test_no_raise_min(self): 117 """Tests a minSdkVersion that doesn't need raising.""" 118 119 manifest_input = self.manifest_tmpl % self.uses_sdk(min='28') 120 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') 121 output = self.raise_min_sdk_version_test(manifest_input, '27', '27', False) 122 self.assertEqual(output, expected) 123 124 def test_raise_codename(self): 125 """Tests raising a minSdkVersion attribute to a codename.""" 126 127 manifest_input = self.manifest_tmpl % self.uses_sdk(min='28') 128 expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P') 129 output = self.raise_min_sdk_version_test(manifest_input, 'P', 'P', False) 130 self.assertEqual(output, expected) 131 132 def test_no_raise_codename(self): 133 """Tests a minSdkVersion codename that doesn't need raising.""" 134 135 manifest_input = self.manifest_tmpl % self.uses_sdk(min='P') 136 expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28') 137 output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) 138 self.assertEqual(output, expected) 139 140 def test_target(self): 141 """Tests an existing targetSdkVersion is preserved.""" 142 143 manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27') 144 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') 145 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) 146 self.assertEqual(output, expected) 147 148 def test_no_target(self): 149 """Tests inserting targetSdkVersion when minSdkVersion exists.""" 150 151 manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') 152 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29') 153 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) 154 self.assertEqual(output, expected) 155 156 def test_target_no_min(self): 157 """"Tests inserting targetSdkVersion when minSdkVersion exists.""" 158 159 manifest_input = self.manifest_tmpl % self.uses_sdk(target='27') 160 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') 161 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) 162 self.assertEqual(output, expected) 163 164 def test_no_target_no_min(self): 165 """Tests inserting targetSdkVersion when minSdkVersion does not exist.""" 166 167 manifest_input = self.manifest_tmpl % '' 168 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29') 169 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) 170 self.assertEqual(output, expected) 171 172 def test_library_no_target(self): 173 """Tests inserting targetSdkVersion when minSdkVersion exists.""" 174 175 manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') 176 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15') 177 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True) 178 self.assertEqual(output, expected) 179 180 def test_library_target_no_min(self): 181 """Tests inserting targetSdkVersion when minSdkVersion exists.""" 182 183 manifest_input = self.manifest_tmpl % self.uses_sdk(target='27') 184 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') 185 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True) 186 self.assertEqual(output, expected) 187 188 def test_library_no_target_no_min(self): 189 """Tests inserting targetSdkVersion when minSdkVersion does not exist.""" 190 191 manifest_input = self.manifest_tmpl % '' 192 expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15') 193 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True) 194 self.assertEqual(output, expected) 195 196 def test_extra(self): 197 """Tests that extra attributes and elements are maintained.""" 198 199 manifest_input = self.manifest_tmpl % ( 200 ' <!-- comment -->\n' 201 ' <uses-sdk android:minSdkVersion="27" extra="foo"/>\n' 202 ' <application/>\n') 203 204 # pylint: disable=line-too-long 205 expected = self.manifest_tmpl % ( 206 ' <!-- comment -->\n' 207 ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29" extra="foo"/>\n' 208 ' <application/>\n') 209 210 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) 211 212 self.assertEqual(output, expected) 213 214 def test_indent(self): 215 """Tests that an inserted element copies the existing indentation.""" 216 217 manifest_input = self.manifest_tmpl % ' <!-- comment -->\n' 218 219 # pylint: disable=line-too-long 220 expected = self.manifest_tmpl % ( 221 ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29"/>\n' 222 ' <!-- comment -->\n') 223 224 output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) 225 226 self.assertEqual(output, expected) 227 228 229class AddUsesLibrariesTest(unittest.TestCase): 230 """Unit tests for add_uses_libraries function.""" 231 232 def run_test(self, input_manifest, new_uses_libraries): 233 doc = minidom.parseString(input_manifest) 234 manifest_fixer.add_uses_libraries(doc, new_uses_libraries) 235 output = StringIO.StringIO() 236 manifest_fixer.write_xml(output, doc) 237 return output.getvalue() 238 239 manifest_tmpl = ( 240 '<?xml version="1.0" encoding="utf-8"?>\n' 241 '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' 242 ' <application>\n' 243 '%s' 244 ' </application>\n' 245 '</manifest>\n') 246 247 def uses_libraries(self, name_required_pairs): 248 ret = '' 249 for name, required in name_required_pairs: 250 ret += ( 251 ' <uses-library android:name="%s" android:required="%s"/>\n' 252 ) % (name, required) 253 254 return ret 255 256 def test_empty(self): 257 """Empty new_uses_libraries must not touch the manifest.""" 258 manifest_input = self.manifest_tmpl % self.uses_libraries([ 259 ('foo', 'true'), 260 ('bar', 'false')]) 261 expected = manifest_input 262 output = self.run_test(manifest_input, []) 263 self.assertEqual(output, expected) 264 265 def test_not_overwrite(self): 266 """new_uses_libraries must not overwrite existing tags.""" 267 manifest_input = self.manifest_tmpl % self.uses_libraries([ 268 ('foo', 'true'), 269 ('bar', 'false')]) 270 expected = manifest_input 271 output = self.run_test(manifest_input, ['foo', 'bar']) 272 self.assertEqual(output, expected) 273 274 def test_add(self): 275 """New names are added with 'required:true'.""" 276 manifest_input = self.manifest_tmpl % self.uses_libraries([ 277 ('foo', 'true'), 278 ('bar', 'false')]) 279 expected = self.manifest_tmpl % self.uses_libraries([ 280 ('foo', 'true'), 281 ('bar', 'false'), 282 ('baz', 'true'), 283 ('qux', 'true')]) 284 output = self.run_test(manifest_input, ['bar', 'baz', 'qux']) 285 self.assertEqual(output, expected) 286 287 def test_no_application(self): 288 """When there is no <application> tag, the tag is added.""" 289 manifest_input = ( 290 '<?xml version="1.0" encoding="utf-8"?>\n' 291 '<manifest xmlns:android=' 292 '"http://schemas.android.com/apk/res/android">\n' 293 '</manifest>\n') 294 expected = self.manifest_tmpl % self.uses_libraries([ 295 ('foo', 'true'), 296 ('bar', 'true')]) 297 output = self.run_test(manifest_input, ['foo', 'bar']) 298 self.assertEqual(output, expected) 299 300 def test_empty_application(self): 301 """Even when here is an empty <application/> tag, the libs are added.""" 302 manifest_input = ( 303 '<?xml version="1.0" encoding="utf-8"?>\n' 304 '<manifest xmlns:android=' 305 '"http://schemas.android.com/apk/res/android">\n' 306 ' <application/>\n' 307 '</manifest>\n') 308 expected = self.manifest_tmpl % self.uses_libraries([ 309 ('foo', 'true'), 310 ('bar', 'true')]) 311 output = self.run_test(manifest_input, ['foo', 'bar']) 312 self.assertEqual(output, expected) 313 314 315class AddUsesNonSdkApiTest(unittest.TestCase): 316 """Unit tests for add_uses_libraries function.""" 317 318 def run_test(self, input_manifest): 319 doc = minidom.parseString(input_manifest) 320 manifest_fixer.add_uses_non_sdk_api(doc) 321 output = StringIO.StringIO() 322 manifest_fixer.write_xml(output, doc) 323 return output.getvalue() 324 325 manifest_tmpl = ( 326 '<?xml version="1.0" encoding="utf-8"?>\n' 327 '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' 328 ' <application%s/>\n' 329 '</manifest>\n') 330 331 def uses_non_sdk_api(self, value): 332 return ' android:usesNonSdkApi="true"' if value else '' 333 334 def test_set_true(self): 335 """Empty new_uses_libraries must not touch the manifest.""" 336 manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(False) 337 expected = self.manifest_tmpl % self.uses_non_sdk_api(True) 338 output = self.run_test(manifest_input) 339 self.assertEqual(output, expected) 340 341 def test_already_set(self): 342 """new_uses_libraries must not overwrite existing tags.""" 343 manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(True) 344 expected = manifest_input 345 output = self.run_test(manifest_input) 346 self.assertEqual(output, expected) 347 348 349class UseEmbeddedDexTest(unittest.TestCase): 350 """Unit tests for add_use_embedded_dex function.""" 351 352 def run_test(self, input_manifest): 353 doc = minidom.parseString(input_manifest) 354 manifest_fixer.add_use_embedded_dex(doc) 355 output = StringIO.StringIO() 356 manifest_fixer.write_xml(output, doc) 357 return output.getvalue() 358 359 manifest_tmpl = ( 360 '<?xml version="1.0" encoding="utf-8"?>\n' 361 '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' 362 ' <application%s/>\n' 363 '</manifest>\n') 364 365 def use_embedded_dex(self, value): 366 return ' android:useEmbeddedDex="%s"' % value 367 368 def test_manifest_with_undeclared_preference(self): 369 manifest_input = self.manifest_tmpl % '' 370 expected = self.manifest_tmpl % self.use_embedded_dex('true') 371 output = self.run_test(manifest_input) 372 self.assertEqual(output, expected) 373 374 def test_manifest_with_use_embedded_dex(self): 375 manifest_input = self.manifest_tmpl % self.use_embedded_dex('true') 376 expected = manifest_input 377 output = self.run_test(manifest_input) 378 self.assertEqual(output, expected) 379 380 def test_manifest_with_not_use_embedded_dex(self): 381 manifest_input = self.manifest_tmpl % self.use_embedded_dex('false') 382 self.assertRaises(RuntimeError, self.run_test, manifest_input) 383 384 385class AddExtractNativeLibsTest(unittest.TestCase): 386 """Unit tests for add_extract_native_libs function.""" 387 388 def run_test(self, input_manifest, value): 389 doc = minidom.parseString(input_manifest) 390 manifest_fixer.add_extract_native_libs(doc, value) 391 output = StringIO.StringIO() 392 manifest_fixer.write_xml(output, doc) 393 return output.getvalue() 394 395 manifest_tmpl = ( 396 '<?xml version="1.0" encoding="utf-8"?>\n' 397 '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' 398 ' <application%s/>\n' 399 '</manifest>\n') 400 401 def extract_native_libs(self, value): 402 return ' android:extractNativeLibs="%s"' % value 403 404 def test_set_true(self): 405 manifest_input = self.manifest_tmpl % '' 406 expected = self.manifest_tmpl % self.extract_native_libs('true') 407 output = self.run_test(manifest_input, True) 408 self.assertEqual(output, expected) 409 410 def test_set_false(self): 411 manifest_input = self.manifest_tmpl % '' 412 expected = self.manifest_tmpl % self.extract_native_libs('false') 413 output = self.run_test(manifest_input, False) 414 self.assertEqual(output, expected) 415 416 def test_match(self): 417 manifest_input = self.manifest_tmpl % self.extract_native_libs('true') 418 expected = manifest_input 419 output = self.run_test(manifest_input, True) 420 self.assertEqual(output, expected) 421 422 def test_conflict(self): 423 manifest_input = self.manifest_tmpl % self.extract_native_libs('true') 424 self.assertRaises(RuntimeError, self.run_test, manifest_input, False) 425 426 427if __name__ == '__main__': 428 unittest.main() 429