blob: d54c4121e4f3f05cf26ff01bc4f9821031de8c87 [file] [log] [blame]
Cole Fauste03153b2023-01-26 17:29:13 -08001#!/usr/bin/env python3
Dan Shiefb892d2017-12-06 15:57:31 -08002#
3# Copyright (C) 2017 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"""A tool to generate TradeFed test config file.
18"""
19
Jingwen Chen4bccadd2023-09-19 06:34:16 +000020import argparse
Jingwen Chenf3406e62023-09-18 07:19:11 +000021import re
Dan Shiefb892d2017-12-06 15:57:31 -080022import os
23import shutil
24import sys
25from xml.dom.minidom import parse
26
27ATTRIBUTE_LABEL = 'android:label'
28ATTRIBUTE_RUNNER = 'android:name'
Dan Shi9a501682017-12-22 13:32:48 -080029ATTRIBUTE_PACKAGE = 'package'
Dan Shiefb892d2017-12-06 15:57:31 -080030
31PLACEHOLDER_LABEL = '{LABEL}'
easoncylee94258702020-04-30 15:01:30 +080032PLACEHOLDER_EXTRA_CONFIGS = '{EXTRA_CONFIGS}'
Dan Shiefb892d2017-12-06 15:57:31 -080033PLACEHOLDER_MODULE = '{MODULE}'
34PLACEHOLDER_PACKAGE = '{PACKAGE}'
35PLACEHOLDER_RUNNER = '{RUNNER}'
36PLACEHOLDER_TEST_TYPE = '{TEST_TYPE}'
Dan Shi613b6432024-09-26 17:45:06 +000037PLACEHOLDER_EXTRA_TEST_RUNNER_CONFIGS = '{EXTRA_TEST_RUNNER_CONFIGS}'
Dan Shiefb892d2017-12-06 15:57:31 -080038
39
40def main(argv):
41 """Entry point of auto_gen_test_config.
42
43 Args:
44 argv: A list of arguments.
45 Returns:
46 0 if no error, otherwise 1.
47 """
easoncylee94258702020-04-30 15:01:30 +080048
Jingwen Chen4bccadd2023-09-19 06:34:16 +000049 parser = argparse.ArgumentParser()
50 parser.add_argument(
51 "target_config",
52 help="Path to the generated output config.")
53 parser.add_argument(
54 "android_manifest",
55 help="Path to AndroidManifest.xml or output of 'aapt2 dump xmltree' with .xmltree extension.")
56 parser.add_argument(
57 "empty_config",
58 help="Path to the empty config template.")
59 parser.add_argument(
60 "instrumentation_test_config_template",
61 help="Path to the instrumentation test config template.")
62 parser.add_argument("--extra-configs", default="")
Dan Shi613b6432024-09-26 17:45:06 +000063 parser.add_argument("--extra-test-runner-configs", default="")
Jingwen Chen4bccadd2023-09-19 06:34:16 +000064 args = parser.parse_args(argv)
65
66 target_config = args.target_config
67 android_manifest = args.android_manifest
68 empty_config = args.empty_config
69 instrumentation_test_config_template = args.instrumentation_test_config_template
70 extra_configs = '\n'.join(args.extra_configs.split('\\n'))
Dan Shi613b6432024-09-26 17:45:06 +000071 extra_test_runner_configs = '\n'.join(args.extra_test_runner_configs.split('\\n'))
Dan Shiefb892d2017-12-06 15:57:31 -080072
Dan Shiefb892d2017-12-06 15:57:31 -080073 module = os.path.splitext(os.path.basename(target_config))[0]
Jingwen Chenf3406e62023-09-18 07:19:11 +000074
75 # If the AndroidManifest.xml is not available, but the APK is, this tool also
76 # accepts the output of `aapt2 dump xmltree <apk> AndroidManifest.xml` written
77 # into a file. This is a custom structured aapt2 output - not raw XML!
78 if android_manifest.endswith(".xmltree"):
Dan Shiefb892d2017-12-06 15:57:31 -080079 label = module
Jingwen Chenf3406e62023-09-18 07:19:11 +000080 with open(android_manifest, encoding="utf-8") as manifest:
81 # e.g. A: package="android.test.example.helloworld" (Raw: "android.test.example.helloworld")
82 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
83 pattern = re.compile(r"\(Raw:\s\"(.*)\"\)$")
84 curr_element = None
Jingwen Chen4bccadd2023-09-19 06:34:16 +000085 for line in manifest:
Jingwen Chenf3406e62023-09-18 07:19:11 +000086 curr_line = line.strip()
87 if curr_line.startswith("E:"):
88 # e.g. "E: instrumentation (line=9)"
89 # ^^^^^^^^^^^^^^^
90 curr_element = curr_line.split(" ")[1]
91 if curr_element == "instrumentation":
92 if ATTRIBUTE_RUNNER in curr_line:
93 runner = re.findall(pattern, curr_line)[0]
94 if ATTRIBUTE_LABEL in curr_line:
95 label = re.findall(pattern, curr_line)[0]
96 if curr_element == "manifest":
97 if ATTRIBUTE_PACKAGE in curr_line:
98 package = re.findall(pattern, curr_line)[0]
99
100 if not (runner and label and package):
101 # Failed to locate instrumentation or manifest element in AndroidManifest.
102 # file. Empty test config file will be created.
103 shutil.copyfile(empty_config, target_config)
104 return 0
105
106 else:
107 # If the AndroidManifest.xml file is directly available, read it as an XML file.
108 manifest = parse(android_manifest)
109 instrumentation_elements = manifest.getElementsByTagName('instrumentation')
110 manifest_elements = manifest.getElementsByTagName('manifest')
111 if len(instrumentation_elements) != 1 or len(manifest_elements) != 1:
112 # Failed to locate instrumentation or manifest element in AndroidManifest.
113 # file. Empty test config file will be created.
114 shutil.copyfile(empty_config, target_config)
115 return 0
116
117 instrumentation = instrumentation_elements[0]
118 manifest = manifest_elements[0]
119 if ATTRIBUTE_LABEL in instrumentation.attributes:
120 label = instrumentation.attributes[ATTRIBUTE_LABEL].value
121 else:
122 label = module
123 runner = instrumentation.attributes[ATTRIBUTE_RUNNER].value
124 package = manifest.attributes[ATTRIBUTE_PACKAGE].value
125
Dan Shi96068b72018-02-21 11:31:06 -0800126 test_type = ('InstrumentationTest'
Jingwen Chenf3406e62023-09-18 07:19:11 +0000127 if runner.endswith('.InstrumentationTestRunner')
128 else 'AndroidJUnitTest')
Dan Shiefb892d2017-12-06 15:57:31 -0800129
130 with open(instrumentation_test_config_template) as template:
131 config = template.read()
132 config = config.replace(PLACEHOLDER_LABEL, label)
133 config = config.replace(PLACEHOLDER_MODULE, module)
134 config = config.replace(PLACEHOLDER_PACKAGE, package)
135 config = config.replace(PLACEHOLDER_TEST_TYPE, test_type)
easoncylee94258702020-04-30 15:01:30 +0800136 config = config.replace(PLACEHOLDER_EXTRA_CONFIGS, extra_configs)
Dan Shi613b6432024-09-26 17:45:06 +0000137 config = config.replace(PLACEHOLDER_EXTRA_TEST_RUNNER_CONFIGS, extra_test_runner_configs)
Dan Shiefb892d2017-12-06 15:57:31 -0800138 config = config.replace(PLACEHOLDER_RUNNER, runner)
139 with open(target_config, 'w') as config_file:
140 config_file.write(config)
141 return 0
142
143if __name__ == '__main__':
144 sys.exit(main(sys.argv[1:]))