def check_needed_commands_available() -> None: if not os.path.exists(settings.ANDROID_HOME): cause = f"Declared ANDROID_HOME points to a missing directory: {settings.ANDROID_HOME}" logger.log_progress(cause) raise Exception(cause) if not is_command_available(adb.adb_cmd_prefix): cause = f"Command \'adb\' was not found in the path {adb.adb_cmd_prefix}" logger.log_progress(cause) raise Exception(cause) avd_manager_path = f"{settings.ANDROID_HOME}tools/bin/avdmanager" if os.path.exists(avd_manager_path): features.provide('avd_manager_path', avd_manager_path) else: cause = f"Command \'avdmanager\' was not found in the path {avd_manager_path}" logger.log_progress(cause) raise Exception(cause) if not is_command_available("ant"): cause = "Command 'ant' needed but not found." logger.log_progress(cause) raise Exception(cause) if not is_command_available("java"): cause = "Command 'java' needed but not found." logger.log_progress(cause) raise Exception(cause) if not is_command_available("javac"): cause = "Command 'javac' needed but not found." logger.log_progress(cause) raise Exception(cause) if args.coverage == "ella" and not is_command_available("socat"): cause = "Command 'socat' needed for ELLA coverage but not found." logger.log_progress(cause) raise Exception(cause) output, errors, result_code = run_cmd("java -version") first_line = errors.split('\n')[0].strip() version = first_line.split(' ')[2] if not version[1:-1].startswith("1.8"): cause = f"Found java with version {version}, but 1.8 is needed." logger.log_progress(cause) raise Exception(cause) output, errors, result_code = run_cmd("javac -version") version = errors.split(' ')[1].strip() if not version.startswith("1.8"): cause = f"Found javac with version {version}, but 1.8 is needed." logger.log_progress(cause) raise Exception(cause)
def prepare_app_for_instrumentation(self) -> Tuple[str, str]: # copy sources to instrumented subjects folder app_name = os.path.basename(self.app_path) instrumented_source_path = self.instrumented_subjects_path + app_name os.system(f"rm -rf {instrumented_source_path}") os.system(f"mkdir -p {self.instrumented_subjects_path}") os.system(f"cp -Lr {self.app_path} {instrumented_source_path}") # get AndroidManifest path manifest_path = self.get_manifest_path(instrumented_source_path) # get package name package_name = self.get_package_name(manifest_path) # copy emma source source_root = self.get_source_root(manifest_path, package_name) os.system(f"cp -r {self.emma_instrument_path} {source_root}") # modify emma source emma_instrument_dest_path: str = f"{source_root}EmmaInstrument" for target in os.listdir(emma_instrument_dest_path): if target.endswith(".java"): self.alter_emma_file(f"{emma_instrument_dest_path}/{target}", package_name) # get & alter based on main activity based on "android.intent.action.MAIN" main_activity = self.get_main_activity(manifest_path) if main_activity.startswith("."): main_activity = package_name + main_activity elif not main_activity.startswith(package_name): main_activity = package_name + "." + main_activity features.provide('main_activity', main_activity) # update main activity in InstrumentedActivity.java self.alter_InstrumentedActivity( f"{emma_instrument_dest_path}/InstrumentedActivity.java", main_activity) # update AndroidManifest.xml self.alter_AndroidManifest(manifest_path, package_name) self.alter_MainActivity(manifest_path, main_activity) # update project os.chdir(instrumented_source_path) # TODO: replace for a command that doesn't depend on old android-sdk-linux os.system( f"{settings.WORKING_DIR}monkey/android-sdk-linux/tools/android update project --path . " f"--target {settings.ANDROID_TARGET} --subprojects{logger.redirect_string()}" ) return instrumented_source_path, package_name
def instrument(self) -> None: self.app_path: str = RequiredFeature('app_path').request() self.result_dir: str = RequiredFeature('result_dir').request() self.instrumented_subjects_path: str = RequiredFeature('instrumented_subjects_path').request() self.emma_instrument_path: str = RequiredFeature('emma_instrument_path').request() # first, check if we should assume apps are already instrumented assume_subjects_instrumented = RequiredFeature('assume_subjects_instrumented').request() if assume_subjects_instrumented: features.provide('instrumented_app_path', self.app_path) output, errors, result_code = run_cmd(f"aapt dump badging {self.app_path} | grep package:\\ name") package_name = output.split("package: name=\'")[1].split("\'")[0] features.provide('package_name', package_name) self.prepare_files_for_coverage() return logger.log_progress(f"\nInstrumenting app: {os.path.basename(self.app_path)}") # copy sources and instrument application instrumented_app_path, package_name = self.prepare_app_for_instrumentation() features.provide('package_name', package_name) features.provide('instrumented_app_path', instrumented_app_path) self.instrument_gradle_file(instrumented_app_path, package_name) result_code = os.system(f"./gradlew assembleDebug 2>&1 >{self.result_dir}/build.log") if result_code != 0: raise Exception("Unable run assembleDebug") os.chdir(settings.WORKING_DIR) self.prepare_files_for_coverage()
def run(strategy_name: str, app_paths: List[str]) -> None: compress = RequiredFeature('compress').request() continue_on_subject_failure = RequiredFeature( 'continue_on_subject_failure').request() for i in range(0, len(app_paths)): features.provide('app_path', app_paths[i]) success = run_one_app(strategy_name) if not success and not continue_on_subject_failure: break if compress: compress_results(strategy_name)
def prepare_result_dir(app_name: str, repetition: int, strategy_with_runner_name: str) -> str: repetition_folder = str(repetition) algorithm_folder = strategy_with_runner_name # check if the user wants to overwrite repetition_folder because it's evaluating scripts evaluate_scripts_repetition_number = RequiredFeature( 'evaluate_scripts_repetition_number').request() if evaluate_scripts_repetition_number is not None: repetition_folder = str(evaluate_scripts_repetition_number) evaluate_scripts_algorithm_name = RequiredFeature( 'evaluate_scripts_algorithm_name').request() if evaluate_scripts_algorithm_name is not None: algorithm_folder = str(evaluate_scripts_algorithm_name) # build result_dir path result_dir = f"{settings.WORKING_DIR}results/{algorithm_folder}/{app_name}/{repetition_folder}" skip_subject_if_logbook_in_results = RequiredFeature( 'skip_subject_if_logbook_in_results').request() if skip_subject_if_logbook_in_results: if os.path.exists(f"{result_dir}/logbook.pickle"): raise Exception( f"Skipping run for {algorithm_folder}/{app_name}/{repetition_folder} since there is " f"already a results folder with a valid logbook file inside it." ) # clean and create result_dir os.system(f"rm -rf {result_dir}/*{logger.redirect_string()}") result_code = os.system(f"mkdir -p {result_dir}") if result_code != 0: raise Exception("Unable to create result dir") os.system(f"mkdir -p {result_dir}/intermediate") os.system(f"mkdir -p {result_dir}/coverage") os.system(f"mkdir -p {result_dir}/crashes") features.provide('result_dir', result_dir) adb.adb_logs_dir = result_dir return result_dir
def __init__(self) -> None: self.device_manager = RequiredFeature('device_manager').request() self.strategy = RequiredFeature('strategy').request() self.test_suite_evaluator = RequiredFeature('test_suite_evaluator').request() self.test_runner = RequiredFeature('test_runner').request() self.population_generator = RequiredFeature('population_generator').request() self.toolbox = RequiredFeature('toolbox').request() self.result_dir = RequiredFeature('result_dir').request() self.budget_manager = RequiredFeature('budget_manager').request() self.test_runner.register_crossover_operator(self.toolbox) self.test_runner.register_mutation_operator(self.toolbox) self.test_suite_evaluator.register_selection_operator(self.toolbox) self.apk_preparer = ApkPreparer() features.provide('apk_preparer', self.apk_preparer) self.history = RequiredFeature('history').request() self.toolbox.decorate("mate", self.history.decorator) self.toolbox.decorate("mutate", self.history.decorator)
def provide_compiled_package_name(self, build_gradle_path, package_name) -> None: """ This method takes care of parsing the "debug" build type config, in search of applicationIdSuffix properties that might change the package name once installed in the emulator. If the application has an ETG config file, it will use that instead. :param build_gradle_path: :param package_name: :return: """ etg_config_path = "etg.config" if os.path.isfile(etg_config_path): etg_config = ETGConfig(etg_config_path) features.provide('compiled_package_name', etg_config.compiled_package_name()) return suffix_found = False in_stream = open(build_gradle_path) for index, line in enumerate(in_stream): if line.find("applicationIdSuffix = \"") != -1: suffix: str = line.split("applicationIdSuffix = \"")[1] suffix = suffix.strip("\"\n") suffix_found = True features.provide('compiled_package_name', f"{package_name}{suffix}") break in_stream.close() if not suffix_found: # assume same compiled package name as the one declard in AndroidManifest.xml file features.provide('compiled_package_name', package_name)
def get_subject_paths(arguments: argparse.Namespace) -> List[str]: features.provide('assume_subjects_instrumented', arguments.assume_subjects_instrumented) subject_path = arguments.subject_path if subject_path is not None: absolute_path = os.path.abspath(subject_path) absolute_path = absolute_path.rstrip('/') features.provide('subjects_path', [absolute_path]) return [absolute_path] else: subjects_path = arguments.subjects_path.rstrip('/') + '/' features.provide('subjects_path', subjects_path) app_paths = [] if arguments.assume_subjects_instrumented: output, errors, result_code = run_cmd( f"find -L {subjects_path} -name \"*.apk\"") for line in output.strip().split('\n'): app_paths.append( line.rstrip('/')) # remove trailing forward slash else: output, errors, result_code = run_cmd( f"ls -1 \"{subjects_path}\"*") for relative_path in output.strip().split('\n'): # convert relative path to absolute path absolute_path = os.path.abspath(relative_path) absolute_path = absolute_path.rstrip('/') if os.path.isdir(absolute_path): app_paths.append(absolute_path) elif os.path.isfile(absolute_path): if absolute_path.endswith(".apk"): if "hydrate" not in relative_path: # hydrate app doesn't compile yet, so don't bother app_paths.append( absolute_path) # remove trailing forward slash else: logger.log_progress( f"Ignoring non-APK file {absolute_path} in subjects path" ) else: # special file (e.g., socket) logger.log_progress( f"Ignoring special file {absolute_path} in subjects path" ) continue if arguments.randomize_subjects: random.shuffle(app_paths) if arguments.limit_subjects_number != -1: app_paths = app_paths[0:arguments.limit_subjects_number] # return list(filter(lambda p: 'com.zhiliaoapp.musically' in p, app_paths)) return app_paths
def get_apk_path(self) -> None: self.apk_path = None if self.instrumented_app_path.endswith(".apk"): self.apk_path = self.instrumented_app_path else: # now find its name output, errors, result_code = run_cmd( f"find -L {self.instrumented_app_path} -name \"*.apk\" | grep -v androidTest | grep -v unaligned" ) apk_paths = [] for file_path in output.split("\n"): if file_path != "": apk_paths.append(file_path) if len(apk_paths) == 0: raise Exception( f"No APKs found inside folder {self.instrumented_app_path} after build." ) if len(apk_paths) > 1: # try to filter APKs based on ETG.config file (might not be present) etg_config_path = f"{self.instrumented_app_path}/etg.config" if os.path.isfile(etg_config_path): etg_config = ETGConfig(etg_config_path) # try to filter by build type build_type = etg_config.build_type() apk_paths = list( filter(lambda path: f"/{build_type}/" in path, apk_paths)) # try to filter by product flavors product_flavors = etg_config.product_flavors() if len(product_flavors) > 0: product_flavors_combined = '' for index, flavor in enumerate(product_flavors): if index == 0: product_flavors_combined += flavor.lower() else: product_flavors_combined += flavor.capitalize() apk_paths = list( filter( lambda path: f"/{product_flavors_combined}/" in path, apk_paths)) if len(apk_paths) == 0: raise Exception( f"Evolutiz was unable to determine which APK inside folder " f"{self.instrumented_app_path} should it use, since neither of them satisfy the " f"combined product flavor provided: {product_flavors_combined} in the ETG config " f"file") else: # TODO: provide more info about ETG config files raise Exception( f"There are several APKs found inside folder {self.instrumented_app_path} after " f"build. Evolutiz was unable to determine which one should it use. " f"You can help it by providing an ETG config file at the root of the app's folder." ) self.apk_path = apk_paths[0] features.provide('apk_path', self.apk_path) assert self.apk_path is not None
def instrument(self) -> None: self.app_path: str = RequiredFeature('app_path').request() self.result_dir: str = RequiredFeature('result_dir').request() self.instrumented_subjects_path: str = RequiredFeature( 'instrumented_subjects_path').request() if not self.app_path.endswith(".apk"): cause = "ELLA instrumentation only works with APKs.\n" \ f"Provided app path was: {self.app_path}" logger.log_progress(cause) raise Exception(cause) apk_filename = os.path.basename(self.app_path) logger.log_progress(f"\nInstrumenting app: {apk_filename}") # delete any previous content in ELLA's output folder run_cmd(f"rm -rf {self.ella_original_output_folder_path}/*") # delete any previous content generated by ELLA in /tmp folder run_cmd(f"rm /tmp/ella* /tmp/inputclasses*") # prepare the ella-customized jars output, errors, result_code = run_cmd( f"python gen-ella-wrappers.py", cwd=self.ella_original_folder_path, timeout=120) if result_code != 0: raise Exception("Unable generate ELLA wrappers") output, errors, result_code = run_cmd( f"ant clean build", cwd=self.ella_original_folder_path) if result_code != 0: raise Exception("Unable compile ELLA JARs") # perform the instrumentation of the APK output, errors, result_code = run_cmd( f"./ella.sh i {self.app_path}", cwd=self.ella_original_folder_path, timeout=120) if result_code != 0: raise Exception( f"Unable instrument {self.app_path} APK using ELLA") os.chdir(settings.WORKING_DIR) # find instrumented APK in ELLA's output folder output, errors, result_code = run_cmd( f"find {self.get_current_apk_output_folder()} -type f -name \"instrumented.apk\"" ) instrumented_app_path = output.rstrip('\n') features.provide('instrumented_app_path', instrumented_app_path) # get package and Main activity name directly from the original APK output, errors, result_code = run_cmd( f"aapt dump badging {self.app_path} | grep package:\\ name") package_name = output.split("package: name=\'")[1].split("\'")[0] features.provide('package_name', package_name) features.provide('compiled_package_name', package_name) output, errors, result_code = run_cmd( f"aapt dump badging {self.app_path} | grep launchable-activity | cut -d' ' -f2 | cut -d\"'\" -f 2" ) main_activity_name = output.rstrip('\n') features.provide('main_activity', main_activity_name)
def register_app_instrumentator(self): features.provide('app_instrumentator', JacocoAppInstrumentator)
def register_minimum_api(self) -> None: self.minimum_api = 19 features.provide('minimum_api', self.minimum_api)
def prepare_files_for_coverage(self): instrumented_app_path = RequiredFeature('instrumented_app_path').request() self.jacoco_coverage_class_files_path = f"{settings.WORKING_DIR}{instrumented_app_path}/classes" features.provide('jacoco_coverage_class_files_path', self.jacoco_coverage_class_files_path) etg_config_path = f"{instrumented_app_path}/etg.config" if not os.path.isfile(etg_config_path): raise Exception("Unable to find ETG config for JaCoCo coverage fetcher") etg_config = ETGConfig(etg_config_path) package_name_path = '/'.join(etg_config.package_name().split('.')) app_folder_path = etg_config.get_application_folder_path() # do we have a .jar output, errors, result_code = run_cmd(f"find {app_folder_path} -name \"*.jar\" -type f | grep app_classes") app_classes_jar_path = output.strip("\n") if app_classes_jar_path != "": # unzip this jar so it is taken into account in next find command unzip_folder = f"{os.path.dirname(app_classes_jar_path)}/unzipped" run_cmd(f"rm -r {unzip_folder}") run_cmd(f"unzip {app_classes_jar_path} -d {unzip_folder}") # find out all .class files output, errors, result_code = run_cmd(f"find {app_folder_path} -name \"*.class\" -type f") class_files = output.split("\n") # figure out build variant build_variant = "debug" build_variant_path = build_variant product_flavors = etg_config.product_flavors() if len(product_flavors) > 0: product_flavors_combined = "" product_flavors_combined_path = "" for i, flavor in enumerate(product_flavors): if i == 0: product_flavors_combined += flavor.lower() else: product_flavors_combined += flavor.capitalize() product_flavors_combined_path += "/" product_flavors_combined_path += flavor.lower() build_variant = product_flavors_combined + etg_config.build_type().capitalize() build_variant_path = product_flavors_combined_path + "/" + etg_config.build_type() + "/" if package_name_path.endswith(product_flavors_combined): package_name_path = package_name_path.split("/" + product_flavors_combined)[0] filtered_class_files = [] for class_file in class_files: if (build_variant in class_file or build_variant_path in class_file) \ and not "AndroidTest" in class_file \ and not "androidTest" in class_file \ and not "UnitTest" in class_file \ and not "R$" in class_file \ and not "R.class" in class_file \ and not "BuildConfig.class" in class_file \ and not "/EmmaInstrument/" in class_file \ and not "/jacoco_instrumented_classes/" in class_file \ and not "/jacoco/" in class_file \ and not "/transforms/" in class_file \ and not "/kapt3/" in class_file: filtered_class_files.append(class_file) run_cmd(f"mkdir -p {self.jacoco_coverage_class_files_path}") # Proceed to find the root folder where package name start for each class file. For example: # 'subjects/com.a42crash.iarcuschin.a42crash/app/build/tmp/kotlin-classes/debug/com/a42crash/iarcuschin/a42crash/MainActivity.class' # -> We should copy from the /debug/ folder onwards # 'subjects/com.a42crash.iarcuschin.a42crash/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/a42crash/iarcuschin/a42crash/MainActivity_ViewBinding.class' # -> We should copy from the /classes/ folder onwards class_folders = [] for class_file in filtered_class_files: should_inspect = True for folder in class_folders: if class_file.startswith(folder): should_inspect = False break if not should_inspect: # we already have the folder for this class file continue aux = Path(class_file).parent class_file_folder = str(aux.absolute()) while not class_file_folder.endswith("subjects"): if class_file_folder.endswith(package_name_path): class_folder = class_file_folder.split(package_name_path)[0] if not class_folder in class_folders: class_folders.append(class_folder) break else: aux = aux.parent class_file_folder = str(aux.absolute()) if "/" == class_file_folder: break # copy class folders with their directory structure run_cmd(f"mkdir -p {self.jacoco_coverage_class_files_path}/{package_name_path}") for class_folder in class_folders: run_cmd("rsync -a --prune-empty-dirs --exclude=\"*EmmaInstrument*/\" " + "--exclude=\"*AndroidTest*/\" --exclude=\"*UnitTest*/\" --exclude=\"*kapt3*/\" " + "--exclude=\"*jacoco_instrumented_classes*/\" --exclude=\"R\\$*.class\" " + "--exclude=\"*jacoco*/\" --exclude=\"*androidTest*/\" --exclude=\"*transforms*/\" " + "--exclude=\"BuildConfig.class\" --exclude=\"R.class\" --include=\"*.class\" " + "--include=\"*/\" --exclude=\"*\" " + f"{class_folder}/{package_name_path}/ {self.jacoco_coverage_class_files_path}/{package_name_path}")
def provide_features() -> None: # define subjects features.provide('instrumented_subjects_path', args.instrumented_subjects_path) features.provide('continue_on_subject_failure', args.continue_on_subject_failure) features.provide('continue_on_repetition_failure', args.continue_on_repetition_failure) # define budget and repetitions features.provide('repetitions', args.repetitions) features.provide('repetitions_offset', args.repetitions_offset) features.provide( 'budget_manager', BudgetManager(time_budget=args.time_budget, evaluations_budget=args.evaluations_budget)) # define devices configuration features.provide('emulators_number', args.emulators_number) features.provide('real_devices_number', args.real_devices_number) features.provide('avd_series', args.avd_series) features.provide('avd_manager', AvdManager()) features.provide('strategy', possible_strategies[args.strategy]) features.provide('test_suite_evaluator', possible_test_suite_evaluators[args.evaluator]) # define test runner test_runner = possible_test_runners[args.test_runner] features.provide('test_runner', test_runner) test_runner.register_minimum_api() # define coverage fetcher app instrumentator coverage_fetcher = possible_coverage_fetchers[args.coverage] features.provide('coverage_fetcher', coverage_fetcher) coverage_fetcher.register_app_instrumentator() features.provide('emma_instrument_path', args.emma_instrument_path) # define individual and population generators features.provide('individual_generator', possible_individual_generators[args.individual_generator]) features.provide('population_generator', PopulationGenerator) # define extras features.provide('verbose_level', args.verbose) features.provide('write_logbook', args.write_logbook) features.provide('write_history', args.write_history) features.provide('write_hall_of_fame', args.write_hall_of_fame) features.provide('compress', args.compress) # singletons toolbox = Toolbox() toolbox.register("selectBest", tools.selBest) features.provide('toolbox', toolbox) features.provide('device_manager', DeviceManager()) features.provide('evaluate_scripts_folder_path', args.evaluate_scripts_folder_path) features.provide('evaluate_scripts_repetition_number', args.evaluate_scripts_repetition_number) features.provide('evaluate_scripts_algorithm_name', args.evaluate_scripts_algorithm_name) features.provide('skip_subject_if_logbook_in_results', args.skip_subject_if_logbook_in_results)
def instrument(self) -> None: self.app_path: str = RequiredFeature('app_path').request() self.result_dir: str = RequiredFeature('result_dir').request() self.instrumented_subjects_path: str = RequiredFeature( 'instrumented_subjects_path').request() self.emma_instrument_path: str = RequiredFeature( 'emma_instrument_path').request() # first, check if we should assume apps are already instrumented assume_subjects_instrumented = RequiredFeature( 'assume_subjects_instrumented').request() if assume_subjects_instrumented: features.provide('instrumented_app_path', self.app_path) # copy emma generated file result_code = os.system( f"cp bin/coverage.em {self.result_dir}/{logger.redirect_string()}" ) if result_code != 0: raise Exception("Unable to copy coverage.em file") output, errors, result_code = run_cmd( f"aapt dump badging {self.app_path} | grep package:\\ name") package_name = output.split("package: name=\'")[1].split("\'")[0] features.provide('package_name', package_name) features.provide('compiled_package_name', package_name) return logger.log_progress( f"\nInstrumenting app: {os.path.basename(self.app_path)}") # copy sources and instrument application instrumented_app_path, package_name = self.prepare_app_for_instrumentation( ) # compile with emma data result_code = os.system( f"ant clean emma debug 2>&1 >{self.result_dir}/build.log") if result_code != 0: raise Exception("Unable run ant clean emma debug") # copy emma generated file result_code = os.system( f"cp bin/coverage.em {self.result_dir}/{logger.redirect_string()}") if result_code != 0: raise Exception("Unable to copy coverage.em file") os.chdir(settings.WORKING_DIR) features.provide('package_name', package_name) features.provide('instrumented_app_path', instrumented_app_path) # assume same compiled package name as the one declard in AndroidManifest.xml file features.provide('compiled_package_name', package_name)
def register_app_instrumentator(self): features.provide('app_instrumentator', EllaAppInstrumentator())
def run_one_app(strategy_with_runner_name: str) -> bool: app_path = RequiredFeature('app_path').request() repetitions = RequiredFeature('repetitions').request() repetitions_offset = RequiredFeature('repetitions_offset').request() budget_manager = RequiredFeature('budget_manager').request() test_suite_evaluator = RequiredFeature('test_suite_evaluator').request() verbose_level = RequiredFeature('verbose_level').request() continue_on_repetition_failure = RequiredFeature( 'continue_on_repetition_failure').request() app_name = os.path.basename(app_path) try: there_was_a_failed_repetition = False for repetition in range(repetitions_offset, repetitions): try: os.chdir(settings.WORKING_DIR) logbook = tools.Logbook() logbook.header = ['gen'] features.provide('logbook', logbook) history = tools.History() features.provide('history', history) hall_of_fame = test_suite_evaluator.new_hall_of_fame() features.provide('hall_of_fame', hall_of_fame) result_dir = prepare_result_dir(app_name, repetition, strategy_with_runner_name) get_emulators_running(result_dir) test_generator = Evolutiz() budget_manager.start_budget() logger.log_progress( f"\n-----> Starting repetition: {str(repetition)} for app: {app_name}, " f"initial timestamp is: {str(budget_manager.start_time)}") test_generator.run() logger.log_progress(f"\nEvolutiz finished for app: {app_name}") time_budget_used = budget_manager.get_time_budget_used() if time_budget_used is not None: logger.log_progress( f"\nTime budget used: {time_budget_used:.2f} seconds\n" ) evaluations_budget_used = budget_manager.get_evaluations_budget_used( ) if evaluations_budget_used is not None: logger.log_progress( f"\nEvaluations budget used: {evaluations_budget_used:d}\n" ) # wait for all MultipleQueueConsumerThread to terminate wait_for_working_threas_to_finish() except Exception as e: logger.log_progress( f"\nThere was an error running repetition {repetition} on app: {app_name}" ) if verbose_level > 0: logger.log_progress(f"\n{str(e)}") if verbose_level > 1: logger.log_progress(f"\n{traceback.format_exc()}") traceback.print_exc() there_was_a_failed_repetition = True if not continue_on_repetition_failure: # there was a problem during current repetition, halt further executions of this subject raise e # otherwise, keep running the remaining repetitions return not there_was_a_failed_repetition except Exception as e: logger.log_progress( f"\nThere was an error running evolutiz on app: {app_name}") if verbose_level > 0: logger.log_progress(f"\n{str(e)}") if verbose_level > 1: logger.log_progress(f"\n{traceback.format_exc()}") traceback.print_exc() return False