blob: 8ee599a1ec843381b632939920fd224d42f2c221 [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}'
37
38
39def main(argv):
40 """Entry point of auto_gen_test_config.
41
42 Args:
43 argv: A list of arguments.
44 Returns:
45 0 if no error, otherwise 1.
46 """
easoncylee94258702020-04-30 15:01:30 +080047
Jingwen Chen4bccadd2023-09-19 06:34:16 +000048 parser = argparse.ArgumentParser()
49 parser.add_argument(
50 "target_config",
51 help="Path to the generated output config.")
52 parser.add_argument(
53 "android_manifest",
54 help="Path to AndroidManifest.xml or output of 'aapt2 dump xmltree' with .xmltree extension.")
55 parser.add_argument(
56 "empty_config",
57 help="Path to the empty config template.")
58 parser.add_argument(
59 "instrumentation_test_config_template",
60 help="Path to the instrumentation test config template.")
61 parser.add_argument("--extra-configs", default="")
62 args = parser.parse_args(argv)
63
64 target_config = args.target_config
65 android_manifest = args.android_manifest
66 empty_config = args.empty_config
67 instrumentation_test_config_template = args.instrumentation_test_config_template
68 extra_configs = '\n'.join(args.extra_configs.split('\\n'))
Dan Shiefb892d2017-12-06 15:57:31 -080069
Dan Shiefb892d2017-12-06 15:57:31 -080070 module = os.path.splitext(os.path.basename(target_config))[0]
Jingwen Chenf3406e62023-09-18 07:19:11 +000071
72 # If the AndroidManifest.xml is not available, but the APK is, this tool also
73 # accepts the output of `aapt2 dump xmltree <apk> AndroidManifest.xml` written
74 # into a file. This is a custom structured aapt2 output - not raw XML!
75 if android_manifest.endswith(".xmltree"):
Dan Shiefb892d2017-12-06 15:57:31 -080076 label = module
Jingwen Chenf3406e62023-09-18 07:19:11 +000077 with open(android_manifest, encoding="utf-8") as manifest:
78 # e.g. A: package="android.test.example.helloworld" (Raw: "android.test.example.helloworld")
79 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
80 pattern = re.compile(r"\(Raw:\s\"(.*)\"\)$")
81 curr_element = None
Jingwen Chen4bccadd2023-09-19 06:34:16 +000082 for line in manifest:
Jingwen Chenf3406e62023-09-18 07:19:11 +000083 curr_line = line.strip()
84 if curr_line.startswith("E:"):
85 # e.g. "E: instrumentation (line=9)"
86 # ^^^^^^^^^^^^^^^
87 curr_element = curr_line.split(" ")[1]
88 if curr_element == "instrumentation":
89 if ATTRIBUTE_RUNNER in curr_line:
90 runner = re.findall(pattern, curr_line)[0]
91 if ATTRIBUTE_LABEL in curr_line:
92 label = re.findall(pattern, curr_line)[0]
93 if curr_element == "manifest":
94 if ATTRIBUTE_PACKAGE in curr_line:
95 package = re.findall(pattern, curr_line)[0]
96
97 if not (runner and label and package):
98 # Failed to locate instrumentation or manifest element in AndroidManifest.
99 # file. Empty test config file will be created.
100 shutil.copyfile(empty_config, target_config)
101 return 0
102
103 else:
104 # If the AndroidManifest.xml file is directly available, read it as an XML file.
105 manifest = parse(android_manifest)
106 instrumentation_elements = manifest.getElementsByTagName('instrumentation')
107 manifest_elements = manifest.getElementsByTagName('manifest')
108 if len(instrumentation_elements) != 1 or len(manifest_elements) != 1:
109 # Failed to locate instrumentation or manifest element in AndroidManifest.
110 # file. Empty test config file will be created.
111 shutil.copyfile(empty_config, target_config)
112 return 0
113
114 instrumentation = instrumentation_elements[0]
115 manifest = manifest_elements[0]
116 if ATTRIBUTE_LABEL in instrumentation.attributes:
117 label = instrumentation.attributes[ATTRIBUTE_LABEL].value
118 else:
119 label = module
120 runner = instrumentation.attributes[ATTRIBUTE_RUNNER].value
121 package = manifest.attributes[ATTRIBUTE_PACKAGE].value
122
Dan Shi96068b72018-02-21 11:31:06 -0800123 test_type = ('InstrumentationTest'
Jingwen Chenf3406e62023-09-18 07:19:11 +0000124 if runner.endswith('.InstrumentationTestRunner')
125 else 'AndroidJUnitTest')
Dan Shiefb892d2017-12-06 15:57:31 -0800126
127 with open(instrumentation_test_config_template) as template:
128 config = template.read()
129 config = config.replace(PLACEHOLDER_LABEL, label)
130 config = config.replace(PLACEHOLDER_MODULE, module)
131 config = config.replace(PLACEHOLDER_PACKAGE, package)
132 config = config.replace(PLACEHOLDER_TEST_TYPE, test_type)
easoncylee94258702020-04-30 15:01:30 +0800133 config = config.replace(PLACEHOLDER_EXTRA_CONFIGS, extra_configs)
Dan Shiefb892d2017-12-06 15:57:31 -0800134 config = config.replace(PLACEHOLDER_RUNNER, runner)
135 with open(target_config, 'w') as config_file:
136 config_file.write(config)
137 return 0
138
139if __name__ == '__main__':
140 sys.exit(main(sys.argv[1:]))