blob: 0bf47c6ec0332fc1f3a2b7833e272d460a8fa69a [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 Chenf3406e62023-09-18 07:19:11 +000020import re
Dan Shiefb892d2017-12-06 15:57:31 -080021import os
22import shutil
23import sys
24from xml.dom.minidom import parse
25
26ATTRIBUTE_LABEL = 'android:label'
27ATTRIBUTE_RUNNER = 'android:name'
Dan Shi9a501682017-12-22 13:32:48 -080028ATTRIBUTE_PACKAGE = 'package'
Dan Shiefb892d2017-12-06 15:57:31 -080029
30PLACEHOLDER_LABEL = '{LABEL}'
easoncylee94258702020-04-30 15:01:30 +080031PLACEHOLDER_EXTRA_CONFIGS = '{EXTRA_CONFIGS}'
Dan Shiefb892d2017-12-06 15:57:31 -080032PLACEHOLDER_MODULE = '{MODULE}'
33PLACEHOLDER_PACKAGE = '{PACKAGE}'
34PLACEHOLDER_RUNNER = '{RUNNER}'
35PLACEHOLDER_TEST_TYPE = '{TEST_TYPE}'
36
37
38def main(argv):
39 """Entry point of auto_gen_test_config.
40
41 Args:
42 argv: A list of arguments.
43 Returns:
44 0 if no error, otherwise 1.
45 """
easoncylee94258702020-04-30 15:01:30 +080046 if len(argv) != 4 and len(argv) != 6:
Dan Shiefb892d2017-12-06 15:57:31 -080047 sys.stderr.write(
Jingwen Chenf3406e62023-09-18 07:19:11 +000048 f'Invalid arguments: {argv}. The script requires 4 arguments for file paths: '
49 'target_config, android_manifest (or the xmltree dump), empty_config, '
50 'instrumentation_test_config_template, '
easoncylee94258702020-04-30 15:01:30 +080051 'and 2 optional arguments for extra configs: '
52 '--extra-configs \'EXTRA_CONFIGS\'.\n')
Dan Shiefb892d2017-12-06 15:57:31 -080053 return 1
easoncylee94258702020-04-30 15:01:30 +080054
Dan Shiefb892d2017-12-06 15:57:31 -080055 target_config = argv[0]
56 android_manifest = argv[1]
57 empty_config = argv[2]
58 instrumentation_test_config_template = argv[3]
easoncylee94258702020-04-30 15:01:30 +080059 extra_configs = '\n'.join(argv[5].split('\\n')) if len(argv) == 6 else ''
Dan Shiefb892d2017-12-06 15:57:31 -080060
Dan Shiefb892d2017-12-06 15:57:31 -080061 module = os.path.splitext(os.path.basename(target_config))[0]
Jingwen Chenf3406e62023-09-18 07:19:11 +000062
63 # If the AndroidManifest.xml is not available, but the APK is, this tool also
64 # accepts the output of `aapt2 dump xmltree <apk> AndroidManifest.xml` written
65 # into a file. This is a custom structured aapt2 output - not raw XML!
66 if android_manifest.endswith(".xmltree"):
Dan Shiefb892d2017-12-06 15:57:31 -080067 label = module
Jingwen Chenf3406e62023-09-18 07:19:11 +000068 with open(android_manifest, encoding="utf-8") as manifest:
69 # e.g. A: package="android.test.example.helloworld" (Raw: "android.test.example.helloworld")
70 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
71 pattern = re.compile(r"\(Raw:\s\"(.*)\"\)$")
72 curr_element = None
73 for line in manifest.readlines():
74 curr_line = line.strip()
75 if curr_line.startswith("E:"):
76 # e.g. "E: instrumentation (line=9)"
77 # ^^^^^^^^^^^^^^^
78 curr_element = curr_line.split(" ")[1]
79 if curr_element == "instrumentation":
80 if ATTRIBUTE_RUNNER in curr_line:
81 runner = re.findall(pattern, curr_line)[0]
82 if ATTRIBUTE_LABEL in curr_line:
83 label = re.findall(pattern, curr_line)[0]
84 if curr_element == "manifest":
85 if ATTRIBUTE_PACKAGE in curr_line:
86 package = re.findall(pattern, curr_line)[0]
87
88 if not (runner and label and package):
89 # Failed to locate instrumentation or manifest element in AndroidManifest.
90 # file. Empty test config file will be created.
91 shutil.copyfile(empty_config, target_config)
92 return 0
93
94 else:
95 # If the AndroidManifest.xml file is directly available, read it as an XML file.
96 manifest = parse(android_manifest)
97 instrumentation_elements = manifest.getElementsByTagName('instrumentation')
98 manifest_elements = manifest.getElementsByTagName('manifest')
99 if len(instrumentation_elements) != 1 or len(manifest_elements) != 1:
100 # Failed to locate instrumentation or manifest element in AndroidManifest.
101 # file. Empty test config file will be created.
102 shutil.copyfile(empty_config, target_config)
103 return 0
104
105 instrumentation = instrumentation_elements[0]
106 manifest = manifest_elements[0]
107 if ATTRIBUTE_LABEL in instrumentation.attributes:
108 label = instrumentation.attributes[ATTRIBUTE_LABEL].value
109 else:
110 label = module
111 runner = instrumentation.attributes[ATTRIBUTE_RUNNER].value
112 package = manifest.attributes[ATTRIBUTE_PACKAGE].value
113
Dan Shi96068b72018-02-21 11:31:06 -0800114 test_type = ('InstrumentationTest'
Jingwen Chenf3406e62023-09-18 07:19:11 +0000115 if runner.endswith('.InstrumentationTestRunner')
116 else 'AndroidJUnitTest')
Dan Shiefb892d2017-12-06 15:57:31 -0800117
118 with open(instrumentation_test_config_template) as template:
119 config = template.read()
120 config = config.replace(PLACEHOLDER_LABEL, label)
121 config = config.replace(PLACEHOLDER_MODULE, module)
122 config = config.replace(PLACEHOLDER_PACKAGE, package)
123 config = config.replace(PLACEHOLDER_TEST_TYPE, test_type)
easoncylee94258702020-04-30 15:01:30 +0800124 config = config.replace(PLACEHOLDER_EXTRA_CONFIGS, extra_configs)
Dan Shiefb892d2017-12-06 15:57:31 -0800125 config = config.replace(PLACEHOLDER_RUNNER, runner)
126 with open(target_config, 'w') as config_file:
127 config_file.write(config)
128 return 0
129
130if __name__ == '__main__':
131 sys.exit(main(sys.argv[1:]))