Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a testing script and a github workflow #897

Merged
merged 5 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/regression-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Regression tests

on:
pull_request:
types: [opened, synchronize, reopened]
push:
workflow_dispatch:

jobs:
regression-tests-linux-mac:
name: Run on ${{ matrix.os }} using ${{ matrix.compiler }}
runs-on: ${{ matrix.os }}
env:
CXX: ${{ matrix.compiler }}

strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
compiler: [g++-10, g++-13]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clang-12 can in principle be installed and used on the runner. However, I had issues analogous to those when I tried to use it, hence it is not included yet. On my Ubuntu I have no issues using that version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you often need to use the libc++ shipped with Clang, or an older libstdc++ version.
That's specially true if you live at HEAD.

include:
- os: macos-13
compiler: clang++
- os: windows-latest
compiler: cl.exe
steps:
- name: Checkout repo
uses: actions/checkout@v3

- name: Run regression tests - Linux and macOS version
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-13'
run: |
cd regression-tests
bash run-tests.sh -c ${{ matrix.compiler }}

- name: Run regression tests - Windows version
if: matrix.os == 'windows-latest'
run: |
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && ^
git config --local core.autocrlf false && ^
cd regression-tests && ^
bash run-tests.sh -c ${{ matrix.compiler }}
shell: cmd
284 changes: 284 additions & 0 deletions regression-tests/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
#!/bin/bash

################
usage() {
echo "Usage: $0 -c <compiler> [-t <tests to run>]"
echo " -c <compiler> The compiler to use for the test"
echo " -t <tests to run> Runs only the provided, comma-separated tests (filenames including .cpp2)"
echo " If the argument is not used all tests are run"
exit 1
}

################
# Check file existence and compare its state against the version in git
check_file () {
file="$1"
description="$2"

if [[ ! -f "$file" ]]; then
echo " The $description does not exist:"
echo " $file"
failure=1
return
fi

# Check if the file is tracked by git
git ls-files --error-unmatch "$file" > /dev/null 2>&1
untracked=$?

if [[ $untracked -eq 1 ]]; then
echo " The $description is not tracked by git:"
echo " $file"
# Add the file to the index to be able to diff it...
git add "$file"
# ... print the diff ...
git --no-pager diff HEAD -- "$file"
# ... and remove the file from the diff
git rm --cached -- "$file" > /dev/null 2>&1

failure=1
else
# Compare the content with the refernece value checked in git
diff_output=$(git diff --ignore-cr-at-eol -- "$file")
if [[ -n "$diff_output" ]]; then
echo " Non-matching $description:"
printf "\n$diff_output\n\n"
failure=1
fi
fi
}

optstring="c:t:"
while getopts ${optstring} arg; do
case "${arg}" in
c)
cxx_compiler="${OPTARG}"
;;
t)
# Replace commas with spaces
chosen_tests=${OPTARG/,/ }
;;
\?)
echo "Invalid option: -${OPTARG}."
echo
usage
;;
:)
echo "Missing option argument for -$OPTARG"
echo
usage
;;
*)
usage
exit 1
;;
esac
done

if [ -z "$cxx_compiler" ]; then
echo "Compiler not specified"
usage
fi

tests=$(ls | grep ".cpp2$")
if [[ -n "$chosen_tests" ]]; then
for test in $chosen_tests; do
if ! [[ -f "$test" ]]; then
echo "Requested test ($test) not found"
exit 1
fi
done
echo "Performing tests:"
for test in $chosen_tests; do
echo " $test"
done
echo
tests="$chosen_tests"
else
printf "Performing all regression tests\n\n"
fi

expected_results_dir="test-results"

################
# Get the directory with the exec outputs and compilation command
if [[ "$cxx_compiler" == *"cl.exe"* ]]; then
compiler_cmd='cl.exe -nologo -std:c++latest -MD -EHsc -I ..\include -I ..\..\..\include -experimental:module -Fe:'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 2 include paths are necessary due to the fact that compilation has to be requested in two different folders due to the current setup. This could be improved, but will require small modification in the tests result files.
With that modification, if compilation of the generated C++ is performed in the regression-tests directory, the error outputs from cppfront are more detailed and include pointers into the cpp2 source. This is because the cpp2 files are found in that directory.
I didn't want to modify a lot of files in this PR. I can create another PR that will explain better what I mean here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you use an absolute include path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried. The thing is the path may show up in test results, on compilation errors.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right.
Relative path is what the current scripts in the main branch are using.

exec_out_dir="$expected_results_dir/msvc-2022"
compiler_version=$(cl.exe)
else
compiler_cmd="$cxx_compiler -I../include -I../../../include -std=c++20 -pthread -o "

compiler_ver=$("$cxx_compiler" --version)
if [[ "$compiler_ver" == *"Apple clang version 14.0"* ]]; then
exec_out_dir="$expected_results_dir/apple-clang-14"
elif [[ "$compiler_ver" == *"clang version 12.0"* ]]; then
exec_out_dir="$expected_results_dir/clang-12"
elif [[ "$compiler_ver" == *"clang version 15.0"* ]]; then
exec_out_dir="$expected_results_dir/clang-15"
elif [[ "$compiler_ver" == *"g++-10"* ]]; then
exec_out_dir="$expected_results_dir/gcc-10"
elif [[ "$compiler_ver" == *"g++-12"* ||
"$compiler_ver" == *"g++-13"*
]]; then
exec_out_dir="$expected_results_dir/gcc-13"
fi

compiler_version=$("$cxx_compiler" --version)
fi

if [[ -d "$exec_out_dir" ]]; then
printf "Full compiler version for '$cxx_compiler':\n$compiler_version\n\n"

printf "Directory with reference compilation/execution files to use:\n$exec_out_dir\n\n"
else
printf "not found for compiler: '$cxx_compiler'\n\n"
fi

################
cppfront_cmd="cppfront.exe"
echo "Building cppfront"
$compiler_cmd"$cppfront_cmd" ../source/cppfront.cpp
if [[ $? -ne 0 ]]; then
echo "Compilation failed"
exit 2
fi

################
failed_tests=()
failed_compilations=()
skipped_tests=()
echo "Running regression tests"
for test_file in $tests; do
test_name=${test_file%.*}
expeced_output="$expected_results_dir/$test_file.output"
generated_cpp_name=$test_name.cpp
expected_src="$expected_results_dir/$generated_cpp_name"
test_bin="test.exe"

# Choose mode - default to mixed code
descr="mixed Cpp1 and Cpp2 code"
opt=""
# Using naming convention to discriminate pure Cpp2 code
if [[ $test_name == "pure2"* ]]; then
descr="pure Cpp2 code"
opt="-p"
fi
echo " Testing $descr: $test_name.cpp2"

########
# Run the translation test
echo " Generating Cpp1 code"
./"$cppfront_cmd" "$test_file" -o "$expected_src" $opt > "$expeced_output" 2>&1

failure=0
compiler_issue=0
########
# The C++1 generation output has to exist and to be tracked by git
check_file "$expeced_output" "Cpp1 generation output file"

########
# Check the generated code
if [ -f "$expected_src" ]; then
# The file was generated, so it should be tracked by git
check_file "$expected_src" "generated Cpp1 file"
########
# Compile and run the generated code in a sub-shell
expected_src_compil_out="$exec_out_dir/$generated_cpp_name.output"
expected_src_exec_out="$exec_out_dir/$generated_cpp_name.execution"
expected_files="$expected_results_dir/$test_name.files"

echo " Compiling the generated Cpp1 code"

# For some tests the binary needs to be placed in "$exec_out_dir"
# For that reason the compilation is done directly in that dir
# The source is temporarily copied to avoid issues with bash paths in cl.exe
(cd $exec_out_dir; \
cp ../../$expected_src $generated_cpp_name;
$compiler_cmd"$test_bin" \
$generated_cpp_name \
> $generated_cpp_name.output 2>&1)
compilation_result=$?
rm $exec_out_dir/$generated_cpp_name

if [ -f "$expected_src_compil_out" ]; then
# Check for local compiler issues
if [[ $compilation_result -ne 0 ]]; then
# Workaround an issue with MSVC missing std modules
if cat $expected_src_compil_out | grep -q "error C1011"; then
echo " Skipping further checks due to missing std modules support"
compiler_issue=1
fi
fi
########
# Check the Cpp1 compilation message (if there are no local compiler issues)
if [[ $compiler_issue -ne 1 ]]; then
check_file "$expected_src_compil_out" "Cpp1 compilation message file"
fi
# Check the result of a successful compilation
if [[ $compilation_result -eq 0 ]]; then
########
# Execute the compiled code in $exec_out_dir
echo " Executing the compiled test binary"
# Run the binary in a sub-shell in $exec_out_dir so that files are written there
( cd "$exec_out_dir"; ./$test_bin > "$generated_cpp_name.execution" 2>&1 )

check_file "$expected_src_exec_out" "execution output file"
# If the test generates files check their content
if [[ -f "$expected_files" ]]; then
echo " Checking files written by the binary"
files="$(cat "$expected_files")"
for file in ${files/,/ }; do
check_file "$exec_out_dir/$file" "file meant to be written by the binary"
done
fi
fi
fi
elif [[ $(cat "$expeced_output") != *"error"* ]]; then
echo " Missing generated src file treated as failure"
echo " Failing compilation message needs to contain 'error'"
failure=1
fi

if [[ $failure -ne 0 ]]; then
failed_tests+=($test_name)
fi

if [[ $compiler_issue -ne 0 ]]; then
skipped_tests+=($test_name)
elif [[ $compilation_result -ne 0 ]]; then
failed_compilations+=($test_name)
fi
done

################
# Report missing reference data direcotry
if [[ ! -d "$exec_out_dir" ]]; then
echo "Reference data directory not found for compiler: '$cxx_compiler'"
exit 3
fi

################
# Report skipped/failed tests
report_issues() {
local msg=$1
shift 1
local reported_tests=("$@")
local num_reported_tests=$(wc -w <<< ${reported_tests[@]})
if [ $num_reported_tests -ne 0 ]; then
echo "$msg: $num_reported_tests"
for reported_test in ${reported_tests[@]}; do
echo " $reported_test.cpp2"
done
fi
return $num_reported_tests
}

report_issues "Tests skipped due to compiler issues" "${skipped_tests[@]}"
report_issues "Tests with failing compilation step" "${failed_compilations[@]}"
report_issues "Failed tests" "${failed_tests[@]}"
num_failed_tests=$?
if [ $num_failed_tests -eq 0 ]; then
echo "All tests passed"
fi
exit $num_failed_tests
1 change: 1 addition & 0 deletions regression-tests/test-results/pure2-stdio-with-raii.files
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xyzzy
1 change: 1 addition & 0 deletions regression-tests/test-results/pure2-stdio.files
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xyzzy