Merge branch 'TIF/Jenkins' into TIF/Jenkins_Test

This commit is contained in:
jonawals
2021-06-08 17:00:16 +01:00
3 changed files with 168 additions and 95 deletions
+127 -39
View File
@@ -23,33 +23,40 @@ def is_child_path(parent_path, child_path):
child_path = os.path.abspath(child_path)
return os.path.commonpath([os.path.abspath(parent_path)]) == os.path.commonpath([os.path.abspath(parent_path), os.path.abspath(child_path)])
# Enumerations for test sequence types
class SequenceType(Enum):
# Regular sequence as-per the tiaf regular sequence
REGULAR = 1
# TIA sequence as-per the tiaf read-only impact analysis sequence
TEST_IMPACT_ANALYSIS = 2
# Seed sequence as-per the tiaf seed sequence
SEED = 3
class TestImpact:
def __init__(self, config_file, dst_commit):
def __init__(self, config_file, pipeline, dst_commit):
self.__pipeline = pipeline
self.__parse_config_file(config_file)
self.__init_repo(dst_commit)
self.__generate_change_list()
if self.__use_test_impact_analysis and not self.__is_pipeline_of_truth:
self.__generate_change_list()
# Parse the configuration file and retrieve the data needed for launching the test impact analysis runtime
def __parse_config_file(self, config_file):
print(f"Attempting to parse configuration file '{config_file}'...")
with open(config_file, "r") as config_data:
config = json.load(config_data)
# Repository
self.__repo_dir = config["repo"]["root"]
# Jenkins
self.__use_test_impact_analysis = config["jenkins"]["use_test_impact_analysis"]
self.__pipeline_of_truth = config["jenkins"]["pipeline_of_truth"]
print(f"Pipeline of truth: '{self.__pipeline_of_truth}'.")
print(f"This pipeline: '{self.__pipeline}'.")
if self.__pipeline in self.__pipeline_of_truth:
self.__is_pipeline_of_truth = True
else:
self.__is_pipeline_of_truth = False
print(f"Is pipeline of truth: '{self.__is_pipeline_of_truth}'.")
# TIAF binary
self.__tiaf_bin = config["repo"]["tiaf_bin"]
if not os.path.isfile(self.__tiaf_bin):
if self.__use_test_impact_analysis and not os.path.isfile(self.__tiaf_bin):
raise FileNotFoundError("Could not find tiaf binary")
# Workspaces
self.__active_workspace = config["workspace"]["active"]["root"]
self.__historic_workspace = config["workspace"]["historic"]["root"]
self.__temp_workspace = config["workspace"]["temp"]["root"]
# Last commit hash
last_commit_hash_path_rel = config["workspace"]["historic"]["relative_paths"]["last_run_hash_file"]
self.__last_commit_hash_path = os.path.join(self.__historic_workspace, last_commit_hash_path_rel)
print("The configuration file was parsed successfully.")
@@ -71,7 +78,7 @@ class TestImpact:
def __read_last_run_hash(self):
self.__has_src_commit = False
if os.path.isfile(self.__last_commit_hash_path):
print(f"Previous commit hash found at '{self.__last_commit_hash_path}'")
print(f"Previous commit hash found at '{self.__last_commit_hash_path}'.")
with open(self.__last_commit_hash_path) as file:
self.__src_commit = file.read()
self.__has_src_commit = True
@@ -144,43 +151,79 @@ class TestImpact:
return
# Runs the specified test sequence
def run(self, sequence_type, safe_mode, test_timeout, global_timeout):
def run(self, suite, safe_mode, test_timeout, global_timeout):
args = []
print("Please note: test impact analysis sequences will be run in read-only mode (seed sequences are unaffected).")
if sequence_type == SequenceType.REGULAR:
print("Sequence type: regular.")
args.append("--sequence=regular")
args.append("--fpolicy=abort")
elif sequence_type == SequenceType.SEED:
print("Sequence type: seed.")
args.append("--sequence=seed")
args.append("--fpolicy=continue")
elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS:
print("Sequence type: test impact analysis (no write).")
args.append("--fpolicy=abort")
if self.__has_change_list:
args.append(f"-changelist={self.__change_list_path}")
args.append("--sequence=tianowrite")
else:
print(f"No change list was generated, falling back to a regular sequence.")
print("Sequence type: Regular.")
args.append("--sequence=regular")
else:
raise ValueError(sequence_type)
# Suite
args.append(f"--suite={suite}")
print(f"Test suite is set to '{suite}'.")
# Timeouts
if test_timeout != None:
args.append(f"--ttimeout={test_timeout}")
print(f"Test target timeout is set to {test_timeout} seconds.")
if global_timeout != None:
args.append(f"--gtimeout={global_timeout}")
print(f"Global sequence timeout is set to {test_timeout} seconds.")
# If test impact analysis is enabled:
# -> Pipleine of truth will perform a seed that will continue until the sequence is complete regardless of test failues
# -> Non pipline of truth will attempt to perform an impact analysis sequence and exit early upon the fist test failure
# If test impact analysis is disabled:
# -> Pipleine of truth will perform a regular sequence that will continue until the sequence is complete regardless of test failues
# -> Non pipline of truth will perform a regular sequence and exit early upon the fist test failure
if self.__use_test_impact_analysis:
print("Test impact analysis ie enabled.")
# Pipeline of truth sequence
if self.__is_pipeline_of_truth:
# Sequence type
args.append("--sequence=seed")
print("Sequence type is set to 'seed'.")
# Test failure policy
args.append("--fpolicy=continue")
print("Test failure policy is set to 'continue'.")
# Non pipeline of truth sequence
else:
if self.__has_change_list:
# Change list
args.append(f"--changelist={self.__change_list_path}")
print(f"Change list is set to '{self.__change_list_path}'.")
# Sequence type
args.append("--sequence=tianowrite")
print("Sequence type is set to 'tianowrite'.")
# Safe mode
if safe_mode:
args.append("--safemode=on")
print("Safe mode set to 'on'.")
else:
args.append("--safemode=off")
print("Safe mode set to 'off'.")
else:
args.append("--sequence=regular")
print("Sequence type is set to 'regular'.")
# Test failure policy
args.append("--fpolicy=abort")
print("Test failure policy is set to 'abort'.")
else:
print("Test impact analysis ie disabled.")
# Sequence type
args.append("--sequence=regular")
print("Sequence type is set to 'seed'.")
# Pipeline of truth sequence
if self.__is_pipeline_of_truth:
# Test failure policy
args.append("--fpolicy=continue")
print("Test failure policy is set to 'continue'.")
# Non pipeline of truth sequence
else:
# Test failure policy
args.append("--fpolicy=abort")
print("Test failure policy is set to 'abort'.")
print("Args: ", end='')
print(*args)
result = subprocess.run([self.__tiaf_bin] + args)
if result.returncode == 0:
# If the sequence completed 9with or without failures) we will update the historical meta-data
if result.returncode == 0 or result.returncode == 7:
print("Test impact analysis runtime returned successfully.")
if sequence_type == SequenceType.SEED:
if self.__is_pipeline_of_truth:
print("Writing historical meta-data...")
self.__write_last_run_hash(self.__dst_commit)
print("Complete!")
@@ -188,3 +231,48 @@ class TestImpact:
print(f"The test impact analysis runtime returned with error: '{result.returncode}'.")
return result.returncode
#args = []
#print("Please note: test impact analysis sequences will be run in read-only mode (seed sequences are unaffected).")
#if sequence_type == SequenceType.REGULAR:
# print("Sequence type: regular.")
# args.append("--sequence=regular")
# args.append("--fpolicy=abort")
#elif sequence_type == SequenceType.SEED:
# print("Sequence type: seed.")
# args.append("--sequence=seed")
# args.append("--fpolicy=continue")
#elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS:
# print("Sequence type: test impact analysis (no write).")
# args.append("--fpolicy=abort")
# if self.__has_change_list:
# args.append(f"-changelist={self.__change_list_path}")
# args.append("--sequence=tianowrite")
# else:
# print(f"No change list was generated, falling back to a regular sequence.")
# print("Sequence type: Regular.")
# args.append("--sequence=regular")
#else:
# raise ValueError(sequence_type)
#
#if test_timeout != None:
# args.append(f"--ttimeout={test_timeout}")
# print(f"Test target timeout is set to {test_timeout} seconds.")
#if global_timeout != None:
# args.append(f"--gtimeout={global_timeout}")
# print(f"Global sequence timeout is set to {test_timeout} seconds.")
#
#print("Args: ", end='')
#print(*args)
#result = subprocess.run([self.__tiaf_bin] + args)
#if result.returncode == 0:
# print("Test impact analysis runtime returned successfully.")
# if sequence_type == SequenceType.SEED:
# print("Writing historical meta-data...")
# self.__write_last_run_hash(self.__dst_commit)
# print("Complete!")
#else:
# print(f"The test impact analysis runtime returned with error: '{result.returncode}'.")
#return result.returncode
@@ -11,7 +11,6 @@
import argparse
from tiaf import TestImpact
from tiaf import SequenceType
import sys
import os
@@ -26,16 +25,6 @@ def parse_args():
else:
raise FileNotFoundError(value)
def sequence_type(value):
if value == "regular":
return SequenceType.REGULAR
elif value == "tia":
return SequenceType.TEST_IMPACT_ANALYSIS
elif value == "seed":
return SequenceType.SEED
else:
raise ValueError(value)
def timout_type(value):
value = int(value)
if value <= 0:
@@ -44,24 +33,20 @@ def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--config', dest="config", type=file_path, help="Path to the test impact analysis framework configuration file", required=True)
parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (not required for seed)")
parser.add_argument('--sequenceType', dest="sequence_type", type=sequence_type, help="Test sequence type to run ('regular', 'seed' or 'tia')", required=True)
parser.add_argument('--suites', dest="suites", nargs='*', help="Suites to include for regular tes sequences (use '*' for all suites)")
parser.add_argument('--testTimeout', dest="test_timeout", type=timout_type, help="Maximum flight time (in seconds) of any test target before being terminated", required=False)
parser.add_argument('--globalTimeout', dest="global_timeout", type=timout_type, help="Maximum tun time of the sequence before being terminated", required=False)
parser.set_defaults(suites="*")
parser.set_defaults(safe_mode=False)
parser.add_argument('--pipeline', dest="pipeline", help="Pipeline the test impact analysis framework is running on", required=True)
parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (ignored when seeding)", required=True)
parser.add_argument('--suite', dest="suite", help="Test suite to run", required=True)
parser.add_argument('--safeMode', dest="safe_mode", action='store_true', help="Run impact analysis tests in safe mode (ignored when seeding)")
parser.add_argument('--testTimeout', dest="test_timeout", type=timout_type, help="Maximum run time (in seconds) of any test target before being terminated", required=False)
parser.add_argument('--globalTimeout', dest="global_timeout", type=timout_type, help="Maximum run time of the sequence before being terminated", required=False)
parser.set_defaults(test_timeout=None)
parser.set_defaults(global_timeout=None)
args = parser.parse_args()
if args.sequence_type == SequenceType.TEST_IMPACT_ANALYSIS and args.dst_commit == None:
raise ValueError("Test impact analysis sequence must have a change list")
return args
if __name__ == "__main__":
args = parse_args()
tiaf = TestImpact(args.config, args.dst_commit)
return_code = tiaf.run(args.sequence_type, args.test_timeout, args.global_timeout)
tiaf = TestImpact(args.config, args.pipeline, args.dst_commit)
return_code = tiaf.run(args.suite, args.safe_mode, args.test_timeout, args.global_timeout)
sys.exit(return_code)