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.py."""
18
19import io
20import sys
21import unittest
22from xml.dom import minidom
23import xml.etree.ElementTree as ET
24
25import manifest_fixer
26
27sys.dont_write_bytecode = True
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 = io.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 assert_xml_equal(self, output, expected):
84    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
85
86  def test_no_uses_sdk(self):
87    """Tests inserting a uses-sdk element into a manifest."""
88
89    manifest_input = self.manifest_tmpl % ''
90    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
91    output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
92    self.assert_xml_equal(output, expected)
93
94  def test_no_min(self):
95    """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
96
97    manifest_input = self.manifest_tmpl % '    <uses-sdk extra="foo"/>\n'
98    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28',
99                                                  extra='extra="foo"')
100    output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
101    self.assert_xml_equal(output, expected)
102
103  def test_raise_min(self):
104    """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
105
106    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
107    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
108    output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
109    self.assert_xml_equal(output, expected)
110
111  def test_raise(self):
112    """Tests raising a minSdkVersion attribute."""
113
114    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
115    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
116    output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
117    self.assert_xml_equal(output, expected)
118
119  def test_no_raise_min(self):
120    """Tests a minSdkVersion that doesn't need raising."""
121
122    manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
123    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
124    output = self.raise_min_sdk_version_test(manifest_input, '27', '27', False)
125    self.assert_xml_equal(output, expected)
126
127  def test_raise_codename(self):
128    """Tests raising a minSdkVersion attribute to a codename."""
129
130    manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
131    expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P')
132    output = self.raise_min_sdk_version_test(manifest_input, 'P', 'P', False)
133    self.assert_xml_equal(output, expected)
134
135  def test_no_raise_codename(self):
136    """Tests a minSdkVersion codename that doesn't need raising."""
137
138    manifest_input = self.manifest_tmpl % self.uses_sdk(min='P')
139    expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28')
140    output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
141    self.assert_xml_equal(output, expected)
142
143  def test_target(self):
144    """Tests an existing targetSdkVersion is preserved."""
145
146    manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27')
147    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
148    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
149    self.assert_xml_equal(output, expected)
150
151  def test_no_target(self):
152    """Tests inserting targetSdkVersion when minSdkVersion exists."""
153
154    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
155    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
156    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
157    self.assert_xml_equal(output, expected)
158
159  def test_target_no_min(self):
160    """"Tests inserting targetSdkVersion when minSdkVersion exists."""
161
162    manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
163    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
164    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
165    self.assert_xml_equal(output, expected)
166
167  def test_no_target_no_min(self):
168    """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
169
170    manifest_input = self.manifest_tmpl % ''
171    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
172    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
173    self.assert_xml_equal(output, expected)
174
175  def test_library_no_target(self):
176    """Tests inserting targetSdkVersion when minSdkVersion exists."""
177
178    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
179    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
180    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
181    self.assert_xml_equal(output, expected)
182
183  def test_library_target_no_min(self):
184    """Tests inserting targetSdkVersion when minSdkVersion exists."""
185
186    manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
187    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
188    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
189    self.assert_xml_equal(output, expected)
190
191  def test_library_no_target_no_min(self):
192    """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
193
194    manifest_input = self.manifest_tmpl % ''
195    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
196    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
197    self.assert_xml_equal(output, expected)
198
199  def test_extra(self):
200    """Tests that extra attributes and elements are maintained."""
201
202    manifest_input = self.manifest_tmpl % (
203        '    <!-- comment -->\n'
204        '    <uses-sdk android:minSdkVersion="27" extra="foo"/>\n'
205        '    <application/>\n')
206
207    # pylint: disable=line-too-long
208    expected = self.manifest_tmpl % (
209        '    <!-- comment -->\n'
210        '    <uses-sdk android:minSdkVersion="28" extra="foo" android:targetSdkVersion="29"/>\n'
211        '    <application/>\n')
212
213    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
214
215    self.assert_xml_equal(output, expected)
216
217  def test_indent(self):
218    """Tests that an inserted element copies the existing indentation."""
219
220    manifest_input = self.manifest_tmpl % '  <!-- comment -->\n'
221
222    # pylint: disable=line-too-long
223    expected = self.manifest_tmpl % (
224        '  <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29"/>\n'
225        '  <!-- comment -->\n')
226
227    output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
228
229    self.assert_xml_equal(output, expected)
230
231
232class AddLoggingParentTest(unittest.TestCase):
233  """Unit tests for add_logging_parent function."""
234
235  def assert_xml_equal(self, output, expected):
236    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
237
238  def add_logging_parent_test(self, input_manifest, logging_parent=None):
239    doc = minidom.parseString(input_manifest)
240    if logging_parent:
241      manifest_fixer.add_logging_parent(doc, logging_parent)
242    output = io.StringIO()
243    manifest_fixer.write_xml(output, doc)
244    return output.getvalue()
245
246  manifest_tmpl = (
247      '<?xml version="1.0" encoding="utf-8"?>\n'
248      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
249      '%s'
250      '</manifest>\n')
251
252  def uses_logging_parent(self, logging_parent=None):
253    attrs = ''
254    if logging_parent:
255      meta_text = ('<meta-data android:name="android.content.pm.LOGGING_PARENT" '
256                   'android:value="%s"/>\n') % (logging_parent)
257      attrs += '    <application>\n        %s    </application>\n' % (meta_text)
258
259    return attrs
260
261  def test_no_logging_parent(self):
262    """Tests manifest_fixer with no logging_parent."""
263    manifest_input = self.manifest_tmpl % ''
264    expected = self.manifest_tmpl % self.uses_logging_parent()
265    output = self.add_logging_parent_test(manifest_input)
266    self.assert_xml_equal(output, expected)
267
268  def test_logging_parent(self):
269    """Tests manifest_fixer with no logging_parent."""
270    manifest_input = self.manifest_tmpl % ''
271    expected = self.manifest_tmpl % self.uses_logging_parent('FOO')
272    output = self.add_logging_parent_test(manifest_input, 'FOO')
273    self.assert_xml_equal(output, expected)
274
275
276class AddUsesLibrariesTest(unittest.TestCase):
277  """Unit tests for add_uses_libraries function."""
278
279  def assert_xml_equal(self, output, expected):
280    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
281
282  def run_test(self, input_manifest, new_uses_libraries):
283    doc = minidom.parseString(input_manifest)
284    manifest_fixer.add_uses_libraries(doc, new_uses_libraries, True)
285    output = io.StringIO()
286    manifest_fixer.write_xml(output, doc)
287    return output.getvalue()
288
289  manifest_tmpl = (
290      '<?xml version="1.0" encoding="utf-8"?>\n'
291      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
292      '    <application>\n'
293      '%s'
294      '    </application>\n'
295      '</manifest>\n')
296
297  def uses_libraries(self, name_required_pairs):
298    ret = ''
299    for name, required in name_required_pairs:
300      ret += (
301          '        <uses-library android:name="%s" android:required="%s"/>\n'
302      ) % (name, required)
303
304    return ret
305
306  def test_empty(self):
307    """Empty new_uses_libraries must not touch the manifest."""
308    manifest_input = self.manifest_tmpl % self.uses_libraries([
309        ('foo', 'true'),
310        ('bar', 'false')])
311    expected = manifest_input
312    output = self.run_test(manifest_input, [])
313    self.assert_xml_equal(output, expected)
314
315  def test_not_overwrite(self):
316    """new_uses_libraries must not overwrite existing tags."""
317    manifest_input = self.manifest_tmpl % self.uses_libraries([
318        ('foo', 'true'),
319        ('bar', 'false')])
320    expected = manifest_input
321    output = self.run_test(manifest_input, ['foo', 'bar'])
322    self.assert_xml_equal(output, expected)
323
324  def test_add(self):
325    """New names are added with 'required:true'."""
326    manifest_input = self.manifest_tmpl % self.uses_libraries([
327        ('foo', 'true'),
328        ('bar', 'false')])
329    expected = self.manifest_tmpl % self.uses_libraries([
330        ('foo', 'true'),
331        ('bar', 'false'),
332        ('baz', 'true'),
333        ('qux', 'true')])
334    output = self.run_test(manifest_input, ['bar', 'baz', 'qux'])
335    self.assert_xml_equal(output, expected)
336
337  def test_no_application(self):
338    """When there is no <application> tag, the tag is added."""
339    manifest_input = (
340        '<?xml version="1.0" encoding="utf-8"?>\n'
341        '<manifest xmlns:android='
342        '"http://schemas.android.com/apk/res/android">\n'
343        '</manifest>\n')
344    expected = self.manifest_tmpl % self.uses_libraries([
345        ('foo', 'true'),
346        ('bar', 'true')])
347    output = self.run_test(manifest_input, ['foo', 'bar'])
348    self.assert_xml_equal(output, expected)
349
350  def test_empty_application(self):
351    """Even when here is an empty <application/> tag, the libs are added."""
352    manifest_input = (
353        '<?xml version="1.0" encoding="utf-8"?>\n'
354        '<manifest xmlns:android='
355        '"http://schemas.android.com/apk/res/android">\n'
356        '    <application/>\n'
357        '</manifest>\n')
358    expected = self.manifest_tmpl % self.uses_libraries([
359        ('foo', 'true'),
360        ('bar', 'true')])
361    output = self.run_test(manifest_input, ['foo', 'bar'])
362    self.assert_xml_equal(output, expected)
363
364
365class AddUsesNonSdkApiTest(unittest.TestCase):
366  """Unit tests for add_uses_libraries function."""
367
368  def assert_xml_equal(self, output, expected):
369    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
370
371  def run_test(self, input_manifest):
372    doc = minidom.parseString(input_manifest)
373    manifest_fixer.add_uses_non_sdk_api(doc)
374    output = io.StringIO()
375    manifest_fixer.write_xml(output, doc)
376    return output.getvalue()
377
378  manifest_tmpl = (
379      '<?xml version="1.0" encoding="utf-8"?>\n'
380      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
381      '    <application%s/>\n'
382      '</manifest>\n')
383
384  def uses_non_sdk_api(self, value):
385    return ' android:usesNonSdkApi="true"' if value else ''
386
387  def test_set_true(self):
388    """Empty new_uses_libraries must not touch the manifest."""
389    manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(False)
390    expected = self.manifest_tmpl % self.uses_non_sdk_api(True)
391    output = self.run_test(manifest_input)
392    self.assert_xml_equal(output, expected)
393
394  def test_already_set(self):
395    """new_uses_libraries must not overwrite existing tags."""
396    manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(True)
397    expected = manifest_input
398    output = self.run_test(manifest_input)
399    self.assert_xml_equal(output, expected)
400
401
402class UseEmbeddedDexTest(unittest.TestCase):
403  """Unit tests for add_use_embedded_dex function."""
404
405  def assert_xml_equal(self, output, expected):
406    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
407
408  def run_test(self, input_manifest):
409    doc = minidom.parseString(input_manifest)
410    manifest_fixer.add_use_embedded_dex(doc)
411    output = io.StringIO()
412    manifest_fixer.write_xml(output, doc)
413    return output.getvalue()
414
415  manifest_tmpl = (
416      '<?xml version="1.0" encoding="utf-8"?>\n'
417      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
418      '    <application%s/>\n'
419      '</manifest>\n')
420
421  def use_embedded_dex(self, value):
422    return ' android:useEmbeddedDex="%s"' % value
423
424  def test_manifest_with_undeclared_preference(self):
425    manifest_input = self.manifest_tmpl % ''
426    expected = self.manifest_tmpl % self.use_embedded_dex('true')
427    output = self.run_test(manifest_input)
428    self.assert_xml_equal(output, expected)
429
430  def test_manifest_with_use_embedded_dex(self):
431    manifest_input = self.manifest_tmpl % self.use_embedded_dex('true')
432    expected = manifest_input
433    output = self.run_test(manifest_input)
434    self.assert_xml_equal(output, expected)
435
436  def test_manifest_with_not_use_embedded_dex(self):
437    manifest_input = self.manifest_tmpl % self.use_embedded_dex('false')
438    self.assertRaises(RuntimeError, self.run_test, manifest_input)
439
440
441class AddExtractNativeLibsTest(unittest.TestCase):
442  """Unit tests for add_extract_native_libs function."""
443
444  def assert_xml_equal(self, output, expected):
445    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
446
447  def run_test(self, input_manifest, value):
448    doc = minidom.parseString(input_manifest)
449    manifest_fixer.add_extract_native_libs(doc, value)
450    output = io.StringIO()
451    manifest_fixer.write_xml(output, doc)
452    return output.getvalue()
453
454  manifest_tmpl = (
455      '<?xml version="1.0" encoding="utf-8"?>\n'
456      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
457      '    <application%s/>\n'
458      '</manifest>\n')
459
460  def extract_native_libs(self, value):
461    return ' android:extractNativeLibs="%s"' % value
462
463  def test_set_true(self):
464    manifest_input = self.manifest_tmpl % ''
465    expected = self.manifest_tmpl % self.extract_native_libs('true')
466    output = self.run_test(manifest_input, True)
467    self.assert_xml_equal(output, expected)
468
469  def test_set_false(self):
470    manifest_input = self.manifest_tmpl % ''
471    expected = self.manifest_tmpl % self.extract_native_libs('false')
472    output = self.run_test(manifest_input, False)
473    self.assert_xml_equal(output, expected)
474
475  def test_match(self):
476    manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
477    expected = manifest_input
478    output = self.run_test(manifest_input, True)
479    self.assert_xml_equal(output, expected)
480
481  def test_conflict(self):
482    manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
483    self.assertRaises(RuntimeError, self.run_test, manifest_input, False)
484
485
486class AddNoCodeApplicationTest(unittest.TestCase):
487  """Unit tests for set_has_code_to_false function."""
488
489  def assert_xml_equal(self, output, expected):
490    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
491
492  def run_test(self, input_manifest):
493    doc = minidom.parseString(input_manifest)
494    manifest_fixer.set_has_code_to_false(doc)
495    output = io.StringIO()
496    manifest_fixer.write_xml(output, doc)
497    return output.getvalue()
498
499  manifest_tmpl = (
500      '<?xml version="1.0" encoding="utf-8"?>\n'
501      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
502      '%s'
503      '</manifest>\n')
504
505  def test_no_application(self):
506    manifest_input = self.manifest_tmpl % ''
507    expected = self.manifest_tmpl % '    <application android:hasCode="false"/>\n'
508    output = self.run_test(manifest_input)
509    self.assert_xml_equal(output, expected)
510
511  def test_has_application_no_has_code(self):
512    manifest_input = self.manifest_tmpl % '    <application/>\n'
513    expected = self.manifest_tmpl % '    <application android:hasCode="false"/>\n'
514    output = self.run_test(manifest_input)
515    self.assert_xml_equal(output, expected)
516
517  def test_has_application_has_code_false(self):
518    """ Do nothing if there's already an application elemeent. """
519    manifest_input = self.manifest_tmpl % '    <application android:hasCode="false"/>\n'
520    output = self.run_test(manifest_input)
521    self.assert_xml_equal(output, manifest_input)
522
523  def test_has_application_has_code_true(self):
524    """ Do nothing if there's already an application element even if its
525     hasCode attribute is true. """
526    manifest_input = self.manifest_tmpl % '    <application android:hasCode="true"/>\n'
527    output = self.run_test(manifest_input)
528    self.assert_xml_equal(output, manifest_input)
529
530
531class AddTestOnlyApplicationTest(unittest.TestCase):
532  """Unit tests for set_test_only_flag_to_true function."""
533
534  def assert_xml_equal(self, output, expected):
535    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
536
537  def run_test(self, input_manifest):
538    doc = minidom.parseString(input_manifest)
539    manifest_fixer.set_test_only_flag_to_true(doc)
540    output = io.StringIO()
541    manifest_fixer.write_xml(output, doc)
542    return output.getvalue()
543
544  manifest_tmpl = (
545      '<?xml version="1.0" encoding="utf-8"?>\n'
546      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
547      '%s'
548      '</manifest>\n')
549
550  def test_no_application(self):
551    manifest_input = self.manifest_tmpl % ''
552    expected = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
553    output = self.run_test(manifest_input)
554    self.assert_xml_equal(output, expected)
555
556  def test_has_application_no_test_only(self):
557    manifest_input = self.manifest_tmpl % '    <application/>\n'
558    expected = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
559    output = self.run_test(manifest_input)
560    self.assert_xml_equal(output, expected)
561
562  def test_has_application_test_only_true(self):
563    """ If there's already an application element."""
564    manifest_input = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
565    output = self.run_test(manifest_input)
566    self.assert_xml_equal(output, manifest_input)
567
568  def test_has_application_test_only_false(self):
569    """ If there's already an application element with the testOnly attribute as false."""
570    manifest_input = self.manifest_tmpl % '    <application android:testOnly="false"/>\n'
571    output = self.run_test(manifest_input)
572    self.assert_xml_equal(output, manifest_input)
573
574
575class SetMaxSdkVersionTest(unittest.TestCase):
576  """Unit tests for set_max_sdk_version function."""
577
578  def assert_xml_equal(self, output, expected):
579    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
580
581  def run_test(self, input_manifest, max_sdk_version):
582    doc = minidom.parseString(input_manifest)
583    manifest_fixer.set_max_sdk_version(doc, max_sdk_version)
584    output = io.StringIO()
585    manifest_fixer.write_xml(output, doc)
586    return output.getvalue()
587
588  manifest_tmpl = (
589      '<?xml version="1.0" encoding="utf-8"?>\n'
590      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
591      '%s'
592      '</manifest>\n')
593
594  def permission(self, max=None):
595    if max is None:
596      return '   <permission/>'
597    return '    <permission android:maxSdkVersion="%s"/>\n' % max
598
599  def uses_permission(self, max=None):
600    if max is None:
601      return '   <uses-permission/>'
602    return '    <uses-permission android:maxSdkVersion="%s"/>\n' % max
603
604  def test_permission_no_max_sdk_version(self):
605    """Tests if permission has no maxSdkVersion attribute"""
606    manifest_input = self.manifest_tmpl % self.permission()
607    expected = self.manifest_tmpl % self.permission()
608    output = self.run_test(manifest_input, '9000')
609    self.assert_xml_equal(output, expected)
610
611  def test_permission_max_sdk_version_changed(self):
612    """Tests if permission maxSdkVersion attribute is set to current"""
613    manifest_input = self.manifest_tmpl % self.permission('current')
614    expected = self.manifest_tmpl % self.permission(9000)
615    output = self.run_test(manifest_input, '9000')
616    self.assert_xml_equal(output, expected)
617
618  def test_permission_max_sdk_version_not_changed(self):
619    """Tests if permission maxSdkVersion attribute is not set to current"""
620    manifest_input = self.manifest_tmpl % self.permission(30)
621    expected = self.manifest_tmpl % self.permission(30)
622    output = self.run_test(manifest_input, '9000')
623    self.assert_xml_equal(output, expected)
624
625  def test_uses_permission_no_max_sdk_version(self):
626    """Tests if uses-permission has no maxSdkVersion attribute"""
627    manifest_input = self.manifest_tmpl % self.uses_permission()
628    expected = self.manifest_tmpl % self.uses_permission()
629    output = self.run_test(manifest_input, '9000')
630    self.assert_xml_equal(output, expected)
631
632  def test_uses_permission_max_sdk_version_changed(self):
633    """Tests if uses-permission maxSdkVersion attribute is set to current"""
634    manifest_input = self.manifest_tmpl % self.uses_permission('current')
635    expected = self.manifest_tmpl % self.uses_permission(9000)
636    output = self.run_test(manifest_input, '9000')
637    self.assert_xml_equal(output, expected)
638
639  def test_uses_permission_max_sdk_version_not_changed(self):
640    """Tests if uses-permission maxSdkVersion attribute is not set to current"""
641    manifest_input = self.manifest_tmpl % self.uses_permission(30)
642    expected = self.manifest_tmpl % self.uses_permission(30)
643    output = self.run_test(manifest_input, '9000')
644    self.assert_xml_equal(output, expected)
645
646class OverrideDefaultVersionTest(unittest.TestCase):
647  """Unit tests for override_default_version function."""
648
649  def assert_xml_equal(self, output, expected):
650    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
651
652  def run_test(self, input_manifest, version):
653    doc = minidom.parseString(input_manifest)
654    manifest_fixer.override_placeholder_version(doc, version)
655    output = io.StringIO()
656    manifest_fixer.write_xml(output, doc)
657    return output.getvalue()
658
659  manifest_tmpl = (
660      '<?xml version="1.0" encoding="utf-8"?>\n'
661      '<manifest xmlns:android="http://schemas.android.com/apk/res/android" '
662      'android:versionCode="%s">\n'
663      '</manifest>\n')
664
665  def test_doesnt_override_existing_version(self):
666    """Tests that an existing version is not overridden"""
667    manifest_input = self.manifest_tmpl % '12345'
668    expected = manifest_input
669    output = self.run_test(manifest_input, '67890')
670    self.assert_xml_equal(output, expected)
671
672  def test_overrides_default_version(self):
673    """Tests that a default version is overridden"""
674    manifest_input = self.manifest_tmpl % '0'
675    expected = self.manifest_tmpl % '67890'
676    output = self.run_test(manifest_input, '67890')
677    self.assert_xml_equal(output, expected)
678
679
680if __name__ == '__main__':
681  unittest.main(verbosity=2)
682