• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2024 Huawei Device Co., Ltd.
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
17from glob import glob
18import argparse
19import copy
20import json
21import os
22import stat
23import subprocess
24import shutil
25
26JAVA_CASES = 'java_cases'
27DEX_FILE = 'classes.dex'
28DEX_SIZE_DATA = 'dex_size.dat'
29SOURCE_SIZE_DATA = 'source_size.dat'
30R8_CLASS = 'com.android.tools.r8.R8'
31PG_CONF = 'pg.cfg'
32
33
34def is_file(parser, arg):
35    if not os.path.isfile(arg):
36        parser.error("The file '%s' does not exist" % arg)
37    return os.path.abspath(arg)
38
39def is_dir(parser, arg):
40    if not os.path.isdir(arg):
41        parser.error("The dir '%s' does not exist" % arg)
42    return os.path.abspath(arg)
43
44def check_timeout(value):
45    ivalue = int(value)
46    if ivalue <= 0:
47        raise argparse.ArgumentTypeError("%s is an invalid timeout value" % value)
48    return ivalue
49
50
51def get_args():
52    description = "Execute Java test cases and generate a data file with the bytecode file size of the test cases."
53    parser = argparse.ArgumentParser(description=description)
54    parser.add_argument('--java-bin-path', dest='java_bin_path', type=lambda arg : is_dir(parser, arg),
55                        help='Path to java root', required=True)
56    parser.add_argument('--r8-path', dest='r8_path', type=lambda arg : is_file(parser, arg),
57                        help='Path to the executable program r8', required=True)
58    parser.add_argument('--rt-path', dest='rt_path', type=lambda arg : is_file(parser, arg),
59                        help='Path to the jre/lib/rt.jar', required=True)
60    parser.add_argument('--timeout', type=check_timeout, dest='timeout', default=180,
61                        help='Time limits for use case execution (In seconds)')
62    return parser.parse_args()
63
64
65class JavaR8Runner:
66    def __init__(self, args):
67        self.args = args
68        self.test_root = os.path.dirname(os.path.abspath(__file__))
69        self.output = {}
70        self.source_output = {}
71        self.case_list = []
72        self.cmd_list = [args.java_bin_path, args.r8_path, args.rt_path] # Contain [java_bin_path, r8_path, rt_path]
73
74    @staticmethod
75    def get_case_name(case_path):
76        filename = case_path.split('/')[-1]
77        case_name = filename[:filename.rfind('.')]
78        return case_name
79
80    def add_case(self, case_path, extension):
81        if not os.path.isabs(case_path):
82            case_path = os.path.join(self.test_root, case_path)
83        abs_case_path = os.path.abspath(case_path)
84        if abs_case_path not in self.case_list and abs_case_path.endswith(extension):
85            self.case_list.append(case_path)
86
87    def add_directory(self, directory, extension):
88        if not os.path.isabs(directory):
89            directory = os.path.join(self.test_root, directory)
90        glob_expression = os.path.join(os.path.abspath(directory), "**/*%s" % (extension))
91        cases = glob(glob_expression, recursive=True)
92        for case in cases:
93            self.add_case(case, extension)
94
95    def save_output_to_file(self):
96        dex_size_data = os.path.join(self.test_root, DEX_SIZE_DATA)
97        source_size_data = os.path.join(self.test_root, SOURCE_SIZE_DATA)
98        flags = os.O_RDWR | os.O_CREAT
99        mode = stat.S_IWUSR | stat.S_IRUSR
100        with os.fdopen(os.open(dex_size_data, flags, mode), 'w') as f:
101            f.truncate()
102            json.dump(self.output, f)
103        with os.fdopen(os.open(source_size_data, flags, mode), 'w') as f:
104            f.truncate()
105            json.dump(self.source_output, f)
106
107    def run(self):
108        self.case_list.sort()
109        [java_bin_path, r8_path, rt_path] = copy.deepcopy(self.cmd_list)
110        javac_path = '/'.join([java_bin_path, 'javac'])
111        jar_path = '/'.join([java_bin_path, 'jar'])
112        java_path = '/'.join([java_bin_path, 'java'])
113        cmd_jar2dex_base = [java_path, '-cp', r8_path, R8_CLASS, '--release', '--pg-conf', PG_CONF, '--lib', rt_path]
114        for file_path in self.case_list:
115            child_path = file_path[:file_path.rfind('.')]
116            cmd_java2class = [javac_path, file_path, '-d', child_path]
117            case_name = self.get_case_name(file_path)
118            jar_name = ''.join([child_path, '/', case_name, '.jar'])
119            cmd_class2jar = [jar_path ,'cvf', jar_name, '-C', child_path, '.']
120            cmd_jar2dex = copy.copy(cmd_jar2dex_base)
121            cmd_jar2dex.extend(['--output', child_path, jar_name])
122            try:
123                subprocess.run(cmd_java2class, timeout=self.args.timeout)
124                subprocess.run(cmd_class2jar, timeout=self.args.timeout)
125                subprocess.run(cmd_jar2dex, timeout=self.args.timeout)
126
127            except subprocess.TimeoutExpired:
128                print(f'[WARNING]: Timeout! {file_path}')
129            except Exception as e:
130                print(f"[ERROR]: {e}")
131            finally:
132                dex_file = os.path.join(child_path, DEX_FILE)
133                dex_file_size = 0
134                if os.path.exists(dex_file):
135                    dex_file_size = os.path.getsize(dex_file)
136                shutil.rmtree(child_path)
137                self.output[self.get_case_name(file_path)] = dex_file_size
138                self.source_output[self.get_case_name(file_path)] = os.path.getsize(file_path)
139                print(f'[INFO]: FINISH: {file_path}!')
140
141        self.save_output_to_file()
142
143
144def main():
145    args = get_args()
146    java_runner = JavaR8Runner(args)
147    java_runner.add_directory(JAVA_CASES, '.java')
148    java_runner.run()
149
150
151if __name__ == "__main__":
152    main()