"""Module to understand which integration tests should be launched in a PR. Usage: 1. Save path to files which were changed in branch of a PR using command git diff origin/master --name-status >> files_list.txt 2. In Jenkins save paths to ruby tests ruby_tests=$(python get_flows_to_test.py files_list.txt --language ruby) for flow in $ruby_tests do pipenv run rspec $flow done 3. Save paths to python tests python_test=$(python get_flows_to_test.py files_list.txt --language python) for flow in $python_test do pytest $flow done """ import argparse import os import re RUBY_TEST_PATH = 'spec/post_etl/{feed_name}_post_etl_spec.rb' PYTHON_TEST_PATH = 'integration_tests/python_integration_tests/{feed_name}/' PATHS_WITH_GENERAL_TASKS = ('feed_ingestion/tasks', 'feed_ingestion/util') ruby_tests = ['pandora', 'youtube_monthly', 'deezer'] python_tests = ['youtube_weekly'] def get_flow_name(line): """Get flow name from line with path to paticular flow. Args: line (str): String with path to flow. Returns: str: The flow name. """ try: return re.search(r'feed_ingestion/flows/(\w+)/', line).group(1) except AttributeError: return def scan_imports(line, flow_tests): """Scan all flows with tests with imports with path. It is needed to scan flows witch have integration tests ether they have imports of changed file or not. It is only suitable for general tasks/utils. So for example if path = 'feed_ingestion/tasks', it is needed to scan imports 'feed_ingestion.tasks'. Args: path (str): Path to file which was changed. flow_tests (list): Flows with integration tests. Returns: set: The flows which contain imports of this path. """ result = set() try: match = re.search('([\w/]+)/(\w+).py', line) path = match.group(1).replace('/', '.') module = match.group(2) search_import = r'{path}[\w. ]+{module}'.format( path=path, module=module) for flow_name in flow_tests: for file in os.listdir( 'feed_ingestion/flows/{}/'.format(flow_name)): filename = 'feed_ingestion/flows/{}/{}'.format(flow_name, file) if os.path.isfile(filename): with open(filename) as f: data = f.read() if re.search(search_import, data): result.add(get_flow_name(filename)) except Exception: pass return result def parse_file(filename, flow_tests): """Parse file with paths to filenames which were changed. This file could be obtain by command git diff master --name-status >> files_list.txt Args: filename (str): The filename which it is needed to parse. flow_tests (list): Flows with integration tests. Returns: set: The set of flow names which integration tests should be launched. """ result = set() with open(filename) as file: for line in file: if 'feed_ingestion/flows/' in line: flow_name = get_flow_name(line) if flow_name: result.add(get_flow_name(line)) else: # for files which in this directory (e.g. base.py) # assume that any flow uses this result.update(flow_tests) else: for path in PATHS_WITH_GENERAL_TASKS: if re.search(path, line): result.update(scan_imports(line, flow_tests)) return result & set(flow_tests) def print_list_of_tests(flows, language): """Print string with paths to this tests to launch them from jenkins. Args: flows (set): The set of flow names which integration tests should be launched. language (str): Ruby or python. """ tests_list = [] path = RUBY_TEST_PATH if language == 'ruby' else PYTHON_TEST_PATH for flow_name in flows: tests_list.append(path.format(feed_name=flow_name)) print(' '.join(tests_list)) if __name__ == '__main__': parser = argparse.ArgumentParser( description='Analyze which integration tests it is needed to check') parser.add_argument( 'file', help='filename with list of files to analyze') parser.add_argument( '--language', help='The language name', default='ruby', choices=['ruby', 'python']) args = parser.parse_args() # save which integration tests it is needed to check now flow_tests = ruby_tests if args.language == 'ruby' else python_tests # get set of flow names which integration tests should be launched flows = parse_file(args.file, flow_tests) # print string with paths to this tests to launch them from jenkins print_list_of_tests(flows, args.language)