Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84b5bb91 authored by Jingwen Chen's avatar Jingwen Chen Committed by Gerrit Code Review
Browse files

Merge "Make auto_gen_test_config available to Bazel." into main

parents 5922b630 f3406e64
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -82,3 +82,17 @@ python_binary_host {
      }
    }
}

python_test_host {
    name: "auto_gen_test_config_test",
    main: "auto_gen_test_config_test.py",
    srcs: [
        "auto_gen_test_config.py",
        "auto_gen_test_config_test.py",
    ],
    auto_gen_config: true,
    test_suites: ["general-tests"],
    test_options: {
        unit_test: true,
    },
}
+7 −0
Original line number Diff line number Diff line
@@ -26,3 +26,10 @@ py_binary(
    python_version = "PY3",
    visibility = ["//visibility:public"],
)

py_binary(
    name = "auto_gen_test_config",
    srcs = ["auto_gen_test_config.py"],
    python_version = "PY3",
    visibility = ["//visibility:public"],
)
+57 −21
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
"""A tool to generate TradeFed test config file.
"""

import re
import os
import shutil
import sys
@@ -44,9 +45,9 @@ def main(argv):
  """
  if len(argv) != 4 and len(argv) != 6:
    sys.stderr.write(
        'Invalid arguments. The script requires 4 arguments for file paths: '
        'target_config android_manifest empty_config '
        'instrumentation_test_config_template '
        f'Invalid arguments: {argv}. The script requires 4 arguments for file paths: '
        'target_config, android_manifest (or the xmltree dump), empty_config, '
        'instrumentation_test_config_template, '
        'and 2 optional arguments for extra configs: '
        '--extra-configs \'EXTRA_CONFIGS\'.\n')
    return 1
@@ -57,6 +58,41 @@ def main(argv):
  instrumentation_test_config_template = argv[3]
  extra_configs = '\n'.join(argv[5].split('\\n')) if len(argv) == 6 else ''

  module = os.path.splitext(os.path.basename(target_config))[0]

  # If the AndroidManifest.xml is not available, but the APK is, this tool also
  # accepts the output of `aapt2 dump xmltree <apk> AndroidManifest.xml` written
  # into a file. This is a custom structured aapt2 output - not raw XML!
  if android_manifest.endswith(".xmltree"):
    label = module
    with open(android_manifest, encoding="utf-8") as manifest:
      # e.g. A: package="android.test.example.helloworld" (Raw: "android.test.example.helloworld")
      #                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      pattern = re.compile(r"\(Raw:\s\"(.*)\"\)$")
      curr_element = None
      for line in manifest.readlines():
        curr_line = line.strip()
        if curr_line.startswith("E:"):
          # e.g. "E: instrumentation (line=9)"
          #          ^^^^^^^^^^^^^^^
          curr_element = curr_line.split(" ")[1]
        if curr_element == "instrumentation":
          if ATTRIBUTE_RUNNER in curr_line:
            runner =  re.findall(pattern, curr_line)[0]
          if ATTRIBUTE_LABEL in curr_line:
            label = re.findall(pattern, curr_line)[0]
        if curr_element == "manifest":
          if ATTRIBUTE_PACKAGE in curr_line:
            package = re.findall(pattern, curr_line)[0]

    if not (runner and label and package):
      # Failed to locate instrumentation or manifest element in AndroidManifest.
      # file. Empty test config file will be created.
      shutil.copyfile(empty_config, target_config)
      return 0

  else:
    # If the AndroidManifest.xml file is directly available, read it as an XML file.
    manifest = parse(android_manifest)
    instrumentation_elements = manifest.getElementsByTagName('instrumentation')
    manifest_elements = manifest.getElementsByTagName('manifest')
@@ -66,7 +102,6 @@ def main(argv):
      shutil.copyfile(empty_config, target_config)
      return 0

  module = os.path.splitext(os.path.basename(target_config))[0]
    instrumentation = instrumentation_elements[0]
    manifest = manifest_elements[0]
    if ATTRIBUTE_LABEL in instrumentation.attributes:
@@ -75,6 +110,7 @@ def main(argv):
      label = module
    runner = instrumentation.attributes[ATTRIBUTE_RUNNER].value
    package = manifest.attributes[ATTRIBUTE_PACKAGE].value

  test_type = ('InstrumentationTest'
              if runner.endswith('.InstrumentationTestRunner')
              else 'AndroidJUnitTest')
+130 −18
Original line number Diff line number Diff line
@@ -30,6 +30,24 @@ MANIFEST_INVALID = """<?xml version="1.0" encoding="utf-8"?>
</manifest>
"""

XMLTREE_JUNIT_TEST = """N: android=http://schemas.android.com/apk/res/android (line=2)
  E: manifest (line=2)
    A: package="com.android.my.tests.x" (Raw: "com.android.my.tests.x")
      E: instrumentation (line=9)
        A: http://schemas.android.com/apk/res/android:label(0x01010001)="TestModule" (Raw: "TestModule")
        A: http://schemas.android.com/apk/res/android:name(0x01010003)="androidx.test.runner.AndroidJUnitRunner" (Raw: "androidx.test.runner.AndroidJUnitRunner")
        A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)="com.android.my.tests" (Raw: "com.android.my.tests")
"""

XMLTREE_INSTRUMENTATION_TEST = """N: android=http://schemas.android.com/apk/res/android (line=2)
  E: manifest (line=2)
    A: package="com.android.my.tests.x" (Raw: "com.android.my.tests.x")
      E: instrumentation (line=9)
        A: http://schemas.android.com/apk/res/android:label(0x01010001)="TestModule" (Raw: "TestModule")
        A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.test.InstrumentationTestRunner" (Raw: "android.test.InstrumentationTestRunner")
        A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)="com.android.my.tests" (Raw: "com.android.my.tests")
"""

MANIFEST_JUNIT_TEST = """<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.android.my.tests.x">
@@ -45,12 +63,12 @@ MANIFEST_INSTRUMENTATION_TEST = """<?xml version="1.0" encoding="utf-8"?>
    <instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="com.android.my.tests"
        android:label="My Tests" />
        android:label="TestModule" />
</manifest>
"""

EXPECTED_JUNIT_TEST_CONFIG = """<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
<!-- Copyright (C) 2023 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -66,19 +84,23 @@ EXPECTED_JUNIT_TEST_CONFIG = """<?xml version="1.0" encoding="utf-8"?>
-->
<!-- This test config file is auto-generated. -->
<configuration description="Runs TestModule.">
    <option name="test-suite-tag" value="apct" />
    <option name="test-suite-tag" value="apct-instrumentation" />

    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
        <option name="cleanup-apks" value="true" />
        <option name="test-file-name" value="TestModule.apk" />
    </target_preparer>

    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
        <option name="package" value="com.android.my.tests.x" />
        {EXTRA_TEST_RUNNER_CONFIGS}<option name="package" value="com.android.my.tests.x" />
        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
    </test>
</configuration>
"""

EXPECTED_INSTRUMENTATION_TEST_CONFIG = """<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
<!-- Copyright (C) 2023 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -93,23 +115,74 @@ EXPECTED_INSTRUMENTATION_TEST_CONFIG = """<?xml version="1.0" encoding="utf-8"?>
     limitations under the License.
-->
<!-- This test config file is auto-generated. -->
<configuration description="Runs My Tests.">
<configuration description="Runs TestModule.">
    <option name="test-suite-tag" value="apct" />
    <option name="test-suite-tag" value="apct-instrumentation" />

    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
        <option name="cleanup-apks" value="true" />
        <option name="test-file-name" value="TestModule.apk" />
    </target_preparer>

    <test class="com.android.tradefed.testtype.InstrumentationTest" >
        <option name="package" value="com.android.my.tests.x" />
        {EXTRA_TEST_RUNNER_CONFIGS}<option name="package" value="com.android.my.tests.x" />
        <option name="runner" value="android.test.InstrumentationTestRunner" />
    </test>
</configuration>
"""

TOOLS_DIR = os.path.dirname(os.path.dirname(__file__))
EMPTY_TEST_CONFIG = os.path.join(
    TOOLS_DIR, '..', 'core', 'empty_test_config.xml')
INSTRUMENTATION_TEST_CONFIG_TEMPLATE = os.path.join(
    TOOLS_DIR, '..', 'core', 'instrumentation_test_config_template.xml')
EMPTY_TEST_CONFIG_CONTENT = """<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<!-- No AndroidTest.xml was provided and the manifest does not include
     instrumentation, hence this apk is not instrumentable.
-->
<configuration description="Empty Configuration" />
"""

INSTRUMENTATION_TEST_CONFIG_TEMPLATE_CONTENT = """<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<!-- This test config file is auto-generated. -->
<configuration description="Runs {LABEL}.">
    <option name="test-suite-tag" value="apct" />
    <option name="test-suite-tag" value="apct-instrumentation" />
{EXTRA_CONFIGS}
    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
        <option name="cleanup-apks" value="true" />
        <option name="test-file-name" value="{MODULE}.apk" />
    </target_preparer>

    <test class="com.android.tradefed.testtype.{TEST_TYPE}" >
        {EXTRA_TEST_RUNNER_CONFIGS}<option name="package" value="{PACKAGE}" />
        <option name="runner" value="{RUNNER}" />
    </test>
</configuration>
"""


class AutoGenTestConfigUnittests(unittest.TestCase):
@@ -120,6 +193,16 @@ class AutoGenTestConfigUnittests(unittest.TestCase):
    self.test_dir = tempfile.mkdtemp()
    self.config_file = os.path.join(self.test_dir, TEST_MODULE + '.config')
    self.manifest_file = os.path.join(self.test_dir, 'AndroidManifest.xml')
    self.xmltree_file = os.path.join(self.test_dir, TEST_MODULE + '.xmltree')
    self.empty_test_config_file = os.path.join(self.test_dir, 'empty.config')
    self.instrumentation_test_config_template_file = os.path.join(
        self.test_dir, 'instrumentation.config')

    with open(self.empty_test_config_file, 'w') as f:
      f.write(EMPTY_TEST_CONFIG_CONTENT)

    with open(self.instrumentation_test_config_template_file, 'w') as f:
      f.write(INSTRUMENTATION_TEST_CONFIG_TEMPLATE_CONTENT)

  def tearDown(self):
    """Cleanup the test directory."""
@@ -133,11 +216,11 @@ class AutoGenTestConfigUnittests(unittest.TestCase):

    argv = [self.config_file,
            self.manifest_file,
            EMPTY_TEST_CONFIG,
            INSTRUMENTATION_TEST_CONFIG_TEMPLATE]
            self.empty_test_config_file,
            self.instrumentation_test_config_template_file]
    auto_gen_test_config.main(argv)
    with open(self.config_file) as config_file:
      with open(EMPTY_TEST_CONFIG) as empty_config:
      with open(self.empty_test_config_file) as empty_config:
        self.assertEqual(config_file.read(), empty_config.read())

  def testCreateJUnitTestConfig(self):
@@ -148,8 +231,8 @@ class AutoGenTestConfigUnittests(unittest.TestCase):

    argv = [self.config_file,
            self.manifest_file,
            EMPTY_TEST_CONFIG,
            INSTRUMENTATION_TEST_CONFIG_TEMPLATE]
            self.empty_test_config_file,
            self.instrumentation_test_config_template_file]
    auto_gen_test_config.main(argv)
    with open(self.config_file) as config_file:
      self.assertEqual(config_file.read(), EXPECTED_JUNIT_TEST_CONFIG)
@@ -162,8 +245,37 @@ class AutoGenTestConfigUnittests(unittest.TestCase):

    argv = [self.config_file,
            self.manifest_file,
            EMPTY_TEST_CONFIG,
            INSTRUMENTATION_TEST_CONFIG_TEMPLATE]
            self.empty_test_config_file,
            self.instrumentation_test_config_template_file]
    auto_gen_test_config.main(argv)
    with open(self.config_file) as config_file:
      self.assertEqual(
          config_file.read(), EXPECTED_INSTRUMENTATION_TEST_CONFIG)

  def testCreateJUnitTestConfigWithXMLTree(self):
    """Test creating test config for AndroidJUnitTest.
    """
    with open(self.xmltree_file, 'w') as f:
      f.write(XMLTREE_JUNIT_TEST)

    argv = [self.config_file,
            self.xmltree_file,
            self.empty_test_config_file,
            self.instrumentation_test_config_template_file]
    auto_gen_test_config.main(argv)
    with open(self.config_file) as config_file:
      self.assertEqual(config_file.read(), EXPECTED_JUNIT_TEST_CONFIG)

  def testCreateInstrumentationTestConfigWithXMLTree(self):
    """Test creating test config for InstrumentationTest.
    """
    with open(self.xmltree_file, 'w') as f:
      f.write(XMLTREE_INSTRUMENTATION_TEST)

    argv = [self.config_file,
            self.xmltree_file,
            self.empty_test_config_file,
            self.instrumentation_test_config_template_file]
    auto_gen_test_config.main(argv)
    with open(self.config_file) as config_file:
      self.assertEqual(