Commit d6a833c3 authored by Imdad Sardharwalla's avatar Imdad Sardharwalla Committed by GitHub Enterprise
Browse files

MTBR-740 Add utility to recursively scan a directory for test files (#18)

This Python script has similar functionality to the scan application previously
included in ReForm.

It will search for tests with certain extensions below a certain parent directory,
and output the results into files that can be consumed by CMake/CTest. The file
<name>.<ext> will be ignored if the file <name>.<ext>.ignore exists.

The script itself contains a detailed explanation of how it works and what inputs
need to be supplied.
parent c6782d57
# Ignore temporary files and folders created by Python scripts
\ No newline at end of file
import os
import sys
from pathlib import Path
def get_file_extension(_filename):
return os.path.splitext(_filename)[1]
# Creates a list of tests to be be included in CMakeList.txt. If the test file
# already exists, read it in and compare it to the generated test list. Only
# write the generated test list if it differs from the existing list. This
# prevents CMake from reconfiguring if nothing has changed.
def make_test_list(_test_set, _output_dir, _filename, _cmd):
# Sort tests by file size (descending). Tests with larger file sizes are
# likely to take longer, and therefore should be run first to improve
# overall test time. Note: this is only relevant for the first test run, as
# CTest performs its own reordering of the tests for subsequent runs.
sorted_by_size = sorted(_test_set, key=lambda f:(os.stat(f).st_size, f), reverse=True)
# Set output path
output_path = os.path.join(_output_dir, _filename)
# Generate a new test list with each line of the form:
# <cmd>(<file>)
# Note: CMake requires '/' as its path separator and thus replace all
# instances of '\' with '/' in the path.
new_test_list = ''
for file in sorted_by_size:
new_test_list += _cmd + '(' + file.replace ('\\', '/') + ')\n'
# Remove some issues with the final EOL characters
new_test_list = new_test_list.rstrip()
# Attempt to read existing file
with open(output_path, 'r') as fr:
existing_test_list =
except FileNotFoundError:
existing_test_list = ''
# Write new list if it is different to existing list
if (new_test_list == existing_test_list):
print('No changes were made to "' + _filename + '".')
with open(output_path, 'w') as fw:
print('"' + _filename + '" has been updated.')
This function scans an input directory (_input_dir) for test files and writes
their paths into _cpp_out_filename and _other_out_filename in the output
directory (_output_dir). These output files are in a format that can be consumed
by CMake/CTest.
Test files fall into two categories:
* C++ tests: those with a C++ extension (in the set _cpp_exts); and
* Other tests: those with an allowed extension (in the set _other_exts)
Any test "<name>.<ext>" with an associated .ignore file "<name>.<ext>.ignore"
will be ignored by the scan.
def scan(_input_dir, _output_dir,
_cpp_exts, _cpp_cmd, _cpp_out_filename,
_other_exts, _other_cmd, _other_out_filename):
# Check _input_dir is valid
if not Path(_input_dir).is_dir():
raise ValueError('Error: invalid test directory ("' + _input_dir + '").')
# Check _output_dir is valid
if not Path(_output_dir).is_dir():
raise ValueError('Error: invalid output directory ("' + _output_dir + '").')
# Change working directory to _input_dir
initial_wd = os.getcwd()
print("Scanning for tests...", end='', flush=True)
ignored_tests = set()
cpp_tests_all = set()
other_tests_all = set()
# Scan all test input directory files and sort them into different sets.
# Note: all paths are relative to the current working directory, and thus
# will start with either './' (on Unix) or '.\' (on Windows). We remove
# these characters before storing the path.
for p, d, f in os.walk('.'):
for file in f:
if file.endswith('.ignore'):
# Tests to be ignored (remove .ignore suffix before storing)
ignored_tests.add(os.path.join(p, file[:-7])[2:])
elif get_file_extension(file) in _cpp_exts:
# C++ tests
cpp_tests_all.add(os.path.join(p, file)[2:])
elif get_file_extension(file) in _other_exts:
# Any other tests
other_tests_all.add(os.path.join(p, file)[2:])
print(" ...complete.")
# Remove ignored tests from the sets of matching tests
cpp_tests = cpp_tests_all.difference(ignored_tests)
other_tests = other_tests_all.difference(ignored_tests)
# Display statistics
print("Number of C++ tests =", len(cpp_tests))
print("Number of other tests =", len(other_tests))
print("Total number of tests =", len(cpp_tests) + len(other_tests))
make_test_list(cpp_tests, _output_dir, _cpp_out_filename, _cpp_cmd)
make_test_list(other_tests, _output_dir, _other_out_filename, _other_cmd)
# Return to initial working directory
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment