twister: move main to twisterlib/twister_main.py

This allows for ability to import main to be used
within west

Signed-off-by: Al Semjonovs <asemjonovs@google.com>
This commit is contained in:
Al Semjonovs 2022-11-17 14:05:12 -07:00 committed by Marti Bolivar
commit d09fd2259f
2 changed files with 240 additions and 217 deletions

View file

@ -0,0 +1,238 @@
# vim: set syntax=python ts=4 :
#
# Copyright (c) 2022 Google
# SPDX-License-Identifier: Apache-2.0
import colorama
import logging
import os
import shutil
import sys
import time
from colorama import Fore
from twisterlib.testplan import TestPlan
from twisterlib.reports import Reporting
from twisterlib.hardwaremap import HardwareMap
from twisterlib.coverage import run_coverage
from twisterlib.runner import TwisterRunner
from twisterlib.environment import TwisterEnv
from twisterlib.package import Artifacts
logger = logging.getLogger("twister")
logger.setLevel(logging.DEBUG)
def setup_logging(outdir, log_file, verbose, timestamps):
# create file handler which logs even debug messages
if log_file:
fh = logging.FileHandler(log_file)
else:
fh = logging.FileHandler(os.path.join(outdir, "twister.log"))
fh.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
if verbose > 1:
ch.setLevel(logging.DEBUG)
else:
ch.setLevel(logging.INFO)
# create formatter and add it to the handlers
if timestamps:
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
else:
formatter = logging.Formatter("%(levelname)-7s - %(message)s")
formatter_file = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
ch.setFormatter(formatter)
fh.setFormatter(formatter_file)
# add the handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)
def init_color(colorama_strip):
colorama.init(strip=colorama_strip)
def main(options):
start_time = time.time()
# Configure color output
color_strip = False if options.force_color else None
colorama.init(strip=color_strip)
init_color(colorama_strip=color_strip)
previous_results = None
# Cleanup
if options.no_clean or options.only_failed or options.test_only:
if os.path.exists(options.outdir):
print("Keeping artifacts untouched")
elif options.last_metrics:
ls = os.path.join(options.outdir, "twister.json")
if os.path.exists(ls):
with open(ls, "r") as fp:
previous_results = fp.read()
else:
sys.exit(f"Can't compare metrics with non existing file {ls}")
elif os.path.exists(options.outdir):
if options.clobber_output:
print("Deleting output directory {}".format(options.outdir))
shutil.rmtree(options.outdir)
else:
for i in range(1, 100):
new_out = options.outdir + ".{}".format(i)
if not os.path.exists(new_out):
print("Renaming output directory to {}".format(new_out))
shutil.move(options.outdir, new_out)
break
previous_results_file = None
os.makedirs(options.outdir, exist_ok=True)
if options.last_metrics and previous_results:
previous_results_file = os.path.join(options.outdir, "baseline.json")
with open(previous_results_file, "w") as fp:
fp.write(previous_results)
VERBOSE = options.verbose
setup_logging(options.outdir, options.log_file, VERBOSE, options.timestamps)
env = TwisterEnv(options)
env.discover()
hwm = HardwareMap(env)
ret = hwm.discover()
if ret == 0:
return 0
env.hwm = hwm
tplan = TestPlan(env)
try:
tplan.discover()
except RuntimeError as e:
logger.error(f"{e}")
return 1
if tplan.report() == 0:
return 0
try:
tplan.load()
except RuntimeError as e:
logger.error(f"{e}")
return 1
if options.list_tests and options.platform:
tplan.report_platform_tests(options.platform)
return 0
if VERBOSE > 1:
# if we are using command line platform filter, no need to list every
# other platform as excluded, we know that already.
# Show only the discards that apply to the selected platforms on the
# command line
for i in tplan.instances.values():
if i.status == "filtered":
if options.platform and i.platform.name not in options.platform:
continue
logger.debug(
"{:<25} {:<50} {}SKIPPED{}: {}".format(
i.platform.name,
i.testsuite.name,
Fore.YELLOW,
Fore.RESET,
i.reason,
)
)
if options.report_excluded:
tplan.report_excluded_tests()
return 0
report = Reporting(tplan, env)
plan_file = os.path.join(options.outdir, "testplan.json")
if not os.path.exists(plan_file):
report.json_report(plan_file)
if options.save_tests:
report.json_report(options.save_tests)
return 0
if options.device_testing and not options.build_only:
print("\nDevice testing on:")
hwm.dump(filtered=tplan.selected_platforms)
print("")
if options.dry_run:
duration = time.time() - start_time
logger.info("Completed in %d seconds" % (duration))
return 0
if options.short_build_path:
tplan.create_build_dir_links()
runner = TwisterRunner(tplan.instances, tplan.testsuites, env)
runner.duts = hwm.duts
runner.run()
# figure out which report to use for size comparison
report_to_use = None
if options.compare_report:
report_to_use = options.compare_report
elif options.last_metrics:
report_to_use = previous_results_file
report.footprint_reports(
report_to_use,
options.show_footprint,
options.all_deltas,
options.footprint_threshold,
options.last_metrics,
)
duration = time.time() - start_time
runner.results.summary()
report.summary(runner.results, options.disable_unrecognized_section_test, duration)
if options.coverage:
if not options.build_only:
run_coverage(tplan, options)
else:
logger.info("Skipping coverage report generation due to --build-only.")
if options.device_testing and not options.build_only:
hwm.summary(tplan.selected_platforms)
report.save_reports(
options.report_name,
options.report_suffix,
options.report_dir,
options.no_update,
options.platform_reports,
)
if options.package_artifacts:
artifacts = Artifacts(env)
artifacts.package()
logger.info("Run completed")
if (
runner.results.failed
or runner.results.error
or (tplan.warnings and options.warnings_as_errors)
):
return 1
return 0

View file

@ -171,11 +171,6 @@ Most everyday users will run with no arguments.
import os
import sys
import logging
import time
import shutil
import colorama
from colorama import Fore
from pathlib import Path
@ -195,218 +190,8 @@ if not ZEPHYR_BASE:
sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/pylib/twister/"))
sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/pylib/build_helpers"))
from twisterlib.testplan import TestPlan
from twisterlib.environment import TwisterEnv, add_parse_arguments, parse_arguments
from twisterlib.reports import Reporting
from twisterlib.hardwaremap import HardwareMap
from twisterlib.coverage import run_coverage
from twisterlib.runner import TwisterRunner
from twisterlib.package import Artifacts
logger = logging.getLogger('twister')
logger.setLevel(logging.DEBUG)
def setup_logging(outdir, log_file, verbose, timestamps):
# create file handler which logs even debug messages
if log_file:
fh = logging.FileHandler(log_file)
else:
fh = logging.FileHandler(os.path.join(outdir, "twister.log"))
fh.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
if verbose > 1:
ch.setLevel(logging.DEBUG)
else:
ch.setLevel(logging.INFO)
# create formatter and add it to the handlers
if timestamps:
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
else:
formatter = logging.Formatter('%(levelname)-7s - %(message)s')
formatter_file = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
fh.setFormatter(formatter_file)
# add the handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)
def init_color(colorama_strip):
colorama.init(strip=colorama_strip)
def main(options):
start_time = time.time()
# Configure color output
color_strip = False if options.force_color else None
colorama.init(strip=color_strip)
init_color(colorama_strip=color_strip)
previous_results = None
# Cleanup
if options.no_clean or options.only_failed or options.test_only:
if os.path.exists(options.outdir):
print("Keeping artifacts untouched")
elif options.last_metrics:
ls = os.path.join(options.outdir, "twister.json")
if os.path.exists(ls):
with open(ls, "r") as fp:
previous_results = fp.read()
else:
sys.exit(f"Can't compare metrics with non existing file {ls}")
elif os.path.exists(options.outdir):
if options.clobber_output:
print("Deleting output directory {}".format(options.outdir))
shutil.rmtree(options.outdir)
else:
for i in range(1, 100):
new_out = options.outdir + ".{}".format(i)
if not os.path.exists(new_out):
print("Renaming output directory to {}".format(new_out))
shutil.move(options.outdir, new_out)
break
previous_results_file = None
os.makedirs(options.outdir, exist_ok=True)
if options.last_metrics and previous_results:
previous_results_file = os.path.join(options.outdir, "baseline.json")
with open(previous_results_file, "w") as fp:
fp.write(previous_results)
VERBOSE = options.verbose
setup_logging(options.outdir, options.log_file, VERBOSE, options.timestamps)
env = TwisterEnv(options)
env.discover()
hwm = HardwareMap(env)
ret = hwm.discover()
if ret == 0:
return 0
env.hwm = hwm
tplan = TestPlan(env)
try:
tplan.discover()
except RuntimeError as e:
logger.error(f"{e}")
return 1
if tplan.report() == 0:
return 0
try:
tplan.load()
except RuntimeError as e:
logger.error(f"{e}")
return 1
if options.list_tests and options.platform:
tplan.report_platform_tests(options.platform)
return 0
if VERBOSE > 1:
# if we are using command line platform filter, no need to list every
# other platform as excluded, we know that already.
# Show only the discards that apply to the selected platforms on the
# command line
for i in tplan.instances.values():
if i.status == "filtered":
if options.platform and i.platform.name not in options.platform:
continue
logger.debug(
"{:<25} {:<50} {}SKIPPED{}: {}".format(
i.platform.name,
i.testsuite.name,
Fore.YELLOW,
Fore.RESET,
i.reason))
if options.report_excluded:
tplan.report_excluded_tests()
return 0
report = Reporting(tplan, env)
plan_file = os.path.join(options.outdir, "testplan.json")
if not os.path.exists(plan_file):
report.json_report(plan_file)
if options.save_tests:
report.json_report(options.save_tests)
return 0
if options.device_testing and not options.build_only:
print("\nDevice testing on:")
hwm.dump(filtered=tplan.selected_platforms)
print("")
if options.dry_run:
duration = time.time() - start_time
logger.info("Completed in %d seconds" % (duration))
return 0
if options.short_build_path:
tplan.create_build_dir_links()
runner = TwisterRunner(tplan.instances, tplan.testsuites, env)
runner.duts = hwm.duts
runner.run()
# figure out which report to use for size comparison
report_to_use = None
if options.compare_report:
report_to_use = options.compare_report
elif options.last_metrics:
report_to_use = previous_results_file
report.footprint_reports(report_to_use,
options.show_footprint,
options.all_deltas,
options.footprint_threshold,
options.last_metrics)
duration = time.time() - start_time
runner.results.summary()
report.summary(runner.results, options.disable_unrecognized_section_test, duration)
if options.coverage:
if not options.build_only:
run_coverage(tplan, options)
else:
logger.info("Skipping coverage report generation due to --build-only.")
if options.device_testing and not options.build_only:
hwm.summary(tplan.selected_platforms)
report.save_reports(options.report_name,
options.report_suffix,
options.report_dir,
options.no_update,
options.platform_reports
)
if options.package_artifacts:
artifacts = Artifacts(env)
artifacts.package()
logger.info("Run completed")
if runner.results.failed or runner.results.error or (tplan.warnings and options.warnings_as_errors):
return 1
return 0
from twisterlib.environment import add_parse_arguments, parse_arguments
from twisterlib.twister_main import main
if __name__ == "__main__":
ret = 0