Add testOnly attribute to AndroidManifest file of apex_test

If the build file contains the apex_test module, add the
testOnly attribute to the application element of the
corresponding AndroidManifest file and set its value to true.
If the testOnly attribute is already present and has value
false, then do nothing.

Tests added in manifest_fixer_test.py to check if the updated
AndroidManifest file has the testOnly attribute set to true or not.

Bug: 213310150
Test: atest --host manifest_fixer_test
Test: m nothing
Test: manually checked the AndroidManifest file generated
Change-Id: I36247dbe0261c342d451a4422c314fd8fe0c2369
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index d80a617..2d3103b 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -65,6 +65,9 @@
   parser.add_argument('--has-no-code', dest='has_no_code', action='store_true',
                       help=('adds hasCode="false" attribute to application. Ignored if application elem '
                             'already has a hasCode attribute.'))
+  parser.add_argument('--test-only', dest='test_only', action='store_true',
+                      help=('adds testOnly="true" attribute to application. Assign true value if application elem '
+                            'already has a testOnly attribute.'))
   parser.add_argument('input', help='input AndroidManifest.xml file')
   parser.add_argument('output', help='output AndroidManifest.xml file')
   return parser.parse_args()
@@ -318,6 +321,26 @@
   attr.value = 'false'
   application.setAttributeNode(attr)
 
+def set_test_only_flag_to_true(doc):
+  manifest = parse_manifest(doc)
+  elems = get_children_with_tag(manifest, 'application')
+  application = elems[0] if len(elems) == 1 else None
+  if len(elems) > 1:
+    raise RuntimeError('found multiple <application> tags')
+  elif not elems:
+    application = doc.createElement('application')
+    indent = get_indent(manifest.firstChild, 1)
+    first = manifest.firstChild
+    manifest.insertBefore(doc.createTextNode(indent), first)
+    manifest.insertBefore(application, first)
+
+  attr = application.getAttributeNodeNS(android_ns, 'testOnly')
+  if attr is not None:
+    # Do nothing If the application already has a testOnly attribute.
+    return
+  attr = doc.createAttributeNS(android_ns, 'android:testOnly')
+  attr.value = 'true'
+  application.setAttributeNode(attr)
 
 def main():
   """Program entry point."""
@@ -349,6 +372,9 @@
     if args.has_no_code:
       set_has_code_to_false(doc)
 
+    if args.test_only:
+      set_test_only_flag_to_true(doc)
+
     if args.extract_native_libs is not None:
       add_extract_native_libs(doc, args.extract_native_libs)
 
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index f6fcaaf..199b279 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -521,12 +521,55 @@
     self.assert_xml_equal(output, manifest_input)
 
   def test_has_application_has_code_true(self):
-    """ Do nothing if there's already an application elemeent even if its
+    """ Do nothing if there's already an application element even if its
      hasCode attribute is true. """
     manifest_input = self.manifest_tmpl % '    <application android:hasCode="true"/>\n'
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
 
+class AddTestOnlyApplicationTest(unittest.TestCase):
+  """Unit tests for set_test_only_flag_to_true function."""
+
+  def assert_xml_equal(self, output, expected):
+    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+  def run_test(self, input_manifest):
+    doc = minidom.parseString(input_manifest)
+    manifest_fixer.set_test_only_flag_to_true(doc)
+    output = io.StringIO()
+    manifest_fixer.write_xml(output, doc)
+    return output.getvalue()
+
+  manifest_tmpl = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+      '%s'
+      '</manifest>\n')
+
+  def test_no_application(self):
+    manifest_input = self.manifest_tmpl % ''
+    expected = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
+  def test_has_application_no_test_only(self):
+    manifest_input = self.manifest_tmpl % '    <application/>\n'
+    expected = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
+  def test_has_application_test_only_true(self):
+    """ If there's already an application element."""
+    manifest_input = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, manifest_input)
+
+  def test_has_application_test_only_false(self):
+    """ If there's already an application element with the testOnly attribute as false."""
+    manifest_input = self.manifest_tmpl % '    <application android:testOnly="false"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, manifest_input)
+
 if __name__ == '__main__':
   unittest.main(verbosity=2)