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 24 25JAVA_CASES = 'java_cases' 26DEX_FILE = 'classes.dex' 27DEX_SIZE_DATA = 'dex_size.dat' 28 29 30def is_file(parser, arg): 31 if not os.path.isfile(arg): 32 parser.error("The file '%s' does not exist" % arg) 33 return os.path.abspath(arg) 34 35 36def check_timeout(value): 37 ivalue = int(value) 38 if ivalue <= 0: 39 raise argparse.ArgumentTypeError("%s is an invalid timeout value" % value) 40 return ivalue 41 42 43def get_args(): 44 description = "Execute Java test cases and generate a data file with the bytecode file size of the test cases." 45 parser = argparse.ArgumentParser(description=description) 46 parser.add_argument('--javac-path', dest='javac_path', type=lambda arg : is_file(parser, arg), 47 help='Path to javac compiler', required=True) 48 parser.add_argument('--d8-path', dest='d8_path', type=lambda arg : is_file(parser, arg), 49 help='Path to the executable program d8', required=True) 50 parser.add_argument('--timeout', type=check_timeout, dest='timeout', default=180, 51 help='Time limits for use case execution (In seconds)') 52 return parser.parse_args() 53 54 55class JavaD8Runner: 56 def __init__(self, args): 57 self.args = args 58 self.test_root = os.path.dirname(os.path.abspath(__file__)) 59 self.output = {} 60 self.case_list = [] 61 self.cmd_list = [[args.javac_path], [args.d8_path]] # Contain [cmd_java2class, cmd_class2dex] 62 63 @staticmethod 64 def get_case_name(case_path): 65 filename = case_path.split('/')[-1] 66 case_name = filename[:filename.rfind('.')] 67 return case_name 68 69 def add_case(self, case_path, extension): 70 if not os.path.isabs(case_path): 71 case_path = os.path.join(self.test_root, case_path) 72 abs_case_path = os.path.abspath(case_path) 73 if abs_case_path not in self.case_list and abs_case_path.endswith(extension): 74 self.case_list.append(case_path) 75 76 def add_directory(self, directory, extension): 77 if not os.path.isabs(directory): 78 directory = os.path.join(self.test_root, directory) 79 glob_expression = os.path.join(os.path.abspath(directory), "**/*%s" % (extension)) 80 cases = glob(glob_expression, recursive=True) 81 for case in cases: 82 self.add_case(case, extension) 83 84 def save_output_to_file(self): 85 dex_size_data = os.path.join(self.test_root, DEX_SIZE_DATA) 86 flags = os.O_RDWR | os.O_CREAT 87 mode = stat.S_IWUSR | stat.S_IRUSR 88 with os.fdopen(os.open(dex_size_data, flags, mode), 'w') as f: 89 f.truncate() 90 json.dump(self.output, f) 91 92 def run(self): 93 self.case_list.sort() 94 for file_path in self.case_list: 95 [cmd_java2class, cmd_class2dex] = copy.deepcopy(self.cmd_list) 96 cmd_java2class.extend([file_path]) 97 class_files = [] 98 case_root = os.path.dirname(file_path) 99 try: 100 subprocess.run(cmd_java2class, timeout=self.args.timeout) 101 102 for file in os.listdir(case_root): 103 if file.endswith('.class'): 104 class_files.append(os.path.join(case_root, file)) 105 106 cmd_class2dex.extend(class_files) 107 cmd_class2dex.extend(['--output', case_root]) 108 109 subprocess.run(cmd_class2dex, timeout=self.args.timeout) 110 111 except subprocess.TimeoutExpired: 112 print(f'[WARNING]: Timeout! {file_path}') 113 except Exception as e: 114 print(f"[ERROR]: {e}") 115 finally: 116 for class_file in class_files: 117 if os.path.exists(class_file): 118 os.remove(class_file) 119 120 dex_file = os.path.join(case_root, DEX_FILE) 121 dex_file_size = 0 122 if os.path.exists(dex_file): 123 dex_file_size = os.path.getsize(dex_file) 124 os.remove(dex_file) 125 self.output[self.get_case_name(file_path)] = dex_file_size 126 print(f'[INFO]: FINISH: {file_path}!') 127 128 self.save_output_to_file() 129 130 131def main(): 132 args = get_args() 133 java_runner = JavaD8Runner(args) 134 java_runner.add_directory(JAVA_CASES, '.java') 135 java_runner.run() 136 137 138if __name__ == "__main__": 139 main()