1#!/usr/bin/env python3
2
3# Step to run:
4# 1. run . build/envsetup.sh; lunch ...
5# 2. Run this script from inside a terminal in sysui studio, as it needs JAVA_HOME to be setup.
6
7import argparse, itertools, os, re, subprocess
8from collections.abc import Generator
9from modules_config import Config, ModuleConfig, TestModuleMapping
10
11def runGradleCommand(outputFile: str, cfg: Config, mc: ModuleConfig) -> int:
12    gradlew_dir: String = os.path.join(cfg.build_top, mc.gradlew_location)
13    with open(os.path.expanduser(outputFile), "w") as of:
14        ret = subprocess.run([os.path.join(gradlew_dir, "gradlew"), f"{mc.gradlew_target}"],
15                             stdout=of, stderr=of, cwd=gradlew_dir)
16        return ret.returncode
17
18def check_java():
19    expected_versions = ['17.0', '1.7', '21.0']
20    java_home = os.environ.get('JAVA_HOME')
21    if not java_home:
22        print('JAVA_HOME is not set, exiting ...')
23        exit (-2)
24    java_binary = os.path.join(java_home, 'bin/java')
25    print('Checking version for java binary:', java_binary)
26    out = subprocess.check_output([java_binary, '-version'], stderr=subprocess.STDOUT).decode('utf-8')
27    version = re.search(' version \"(\d+\.\d+).*\"', out).groups()[0]
28    if not version in expected_versions:
29        print('Your java version, version', version, ' does not match any of the expected versions', expected_versions)
30        exit (-3)
31
32def walkDir(dirname: str) -> Generator[str, None, None]:
33    print("Processing dir:", dirname)
34    for root, _, files in os.walk(dirname, topdown=False):
35        for name in files:
36            if not (name.endswith('.java') or name.endswith('.kt')):
37                continue
38            yield os.path.join(root, name)
39
40def processFile(filename: str, cfg: Config, mc: ModuleConfig) -> None:
41    print("Attempting roboify on: " , filename)
42
43    # Move to multivalentTests
44    new_filename = filename.replace("/tests/", "/tests/multivalentTests/")
45    dirname = os.path.dirname(new_filename)
46    basename = os.path.basename(new_filename)
47    outputFile = f"{cfg.output_dir}/{basename}.err"
48    os.makedirs(dirname, exist_ok=True)
49    os.rename(filename, new_filename)
50
51    # Attempt run
52    exitValue = runGradleCommand(outputFile, cfg, mc)
53
54    # Log and restore state if needed
55    print("Test run ", "succeeded" if exitValue == 0  else "failed", " with exitValue: ", exitValue)
56    if exitValue != 0 :
57        os.rename(new_filename, filename)
58
59    return exitValue == 0
60
61def init():
62    parser = argparse.ArgumentParser(
63        description='This script attempts to convert device based unit tests to deviceless.')
64    parser.add_argument('-m', '--module_pattern', required=True)
65    check_java()
66    return parser.parse_args()
67
68def process(cfg: Config, successful: list[str], failed: list[str]):
69    for mc in cfg.module_configs:
70        for d in mc.dirs:
71            for f in walkDir(os.path.join(cfg.build_top, d)):
72                if any(excl in f for excl in mc.excludes):
73                    print("Ignore file:", f)
74                    continue
75                ret = processFile(f, cfg, mc)
76                (successful if ret else failed).append(f)
77
78def main():
79    args = init()
80    successful: list[str] = []
81    failed: list[str] = []
82
83    module_configs = []
84
85    for k, mc in TestModuleMapping.items():
86        if not (args.module_pattern in k):
87            continue
88        print(f"Processing module:{k}, module_config:{mc}")
89        module_configs.append(mc)
90
91    if not module_configs:
92        print(f"Could not match any modules that match: [{args.module_pattern}], exiting ...")
93        exit(-5)
94
95    cfg = Config(module_configs)
96    if not cfg.build_top:
97        print("ANDROID_BUILD_TOP env variable is not set, plase run lunch before invoking this script")
98        exit(-1)
99
100    process(cfg, successful, failed)
101
102    print(f"Successes:\n{successful[:3]} ...")
103    print(f"Failures:\n{failed}")
104    return 0
105
106if __name__ == "__main__":
107    ret = main()
108    exit(ret)
109