#!/bin/perl ############################################################################################## # Copyright (c) Microsoft # # All rights reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file # except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER # EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF # TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. # # See the Apache Version 2.0 License for specific language governing permissions and # limitations under the License. ############################################################################################## #usage: # perl run_test.pl [] # test root dir: base directory to look for tests from. # test list: if provided, a list of test.cpp files to test, one per line. # Otherwise all are run. If provided the script will exit 1 if any # tests fail. use Cwd; use Cwd 'abs_path'; use File::Basename; use File::Find; use Safe; use strict; my $cflag_define = '"-D%s=%s"'; # to be used as sprintf($cflag_define, "NAME", "VALUE"); my $run_log = abs_path('run.log'); mkdir("stl-temp"); my $tmpdir = abs_path('./stl-temp'); my $test_exec = "$tmpdir/test.out"; my $tests_root = @ARGV[0]; if (!$tests_root) { print "ERROR: Test root dir not provided\n"; exit(1); } $tests_root = abs_path($tests_root); chdir($tests_root); my $CLANG_AMP_HOME="@PROJECT_SOURCE_DIR@"; my $CLANG_AMP_BUILD_DIR="@PROJECT_BINARY_DIR@"; my $RUNTESTSDIR="@RUNTESTSDIR@"; my $CLANG_AMP="$CLANG_AMP_BUILD_DIR/compiler/bin/clang++"; my $SHARED_CXXFLAGS="-std=c++11 -I$CLANG_AMP_HOME/include -I/usr/include -I$CLANG_AMP_BUILD_DIR/compiler/lib/clang/3.5.0/include/"; ### Prepare environment if(-e $run_log) { system("rm $run_log"); } ### Find tests my @tests; my $has_test_list=0; sub match_tests { if(lc($_) =~ m/cpp/) { push @tests, cwd().'/'.$_; } } if (@ARGV[1]) { open(TESTLIST, @ARGV[1]) or &exit_message(1, "Cannot open test list: @ARGV[1]"); while() { chomp; push @tests, abs_path($tests_root."/".$_); } close(TESTLIST); $has_test_list=1; } else { find(\&match_tests, cwd()); } ### Execute tests use constant PASS => 0; use constant SKIP => 1; use constant FAIL => 2; my $num_passed = 0; my $num_skipped = 0; my $num_failed = 0; chdir($tmpdir); foreach my $test (@tests) { log_message("Test: $test"); # Read test configuration undef %Test::config; if(not defined $Test::config{'definitions'}) { $Test::config{'definitions'} = [{}]; } # Find "expects error" directives in cpp open(TEST_CPP, $test) or &exit_message(1, "Cannot open $test"); $Test::config{'expected_success'} = (grep m@//#error@i, ) == 0; close(TEST_CPP); open(TEST_CPP, $test) or &exit_message(1, "Cannot open $test"); $Test::config{'expected_failure'} = (grep m@//#fail@i, ) != 0; close(TEST_CPP); log_message('Compile only: '.bool_str($Test::config{'compile_only'})."\n" .'Expected success: '.bool_str($Test::config{'expected_success'})); # For each set of definitions foreach my $def_set (@{$Test::config{'definitions'}}) { print "$test : "; my $command; if ($Test::config{'compile_only'}) { $command = "\\ $CLANG_AMP -cc1 -fcxx-exceptions -fsyntax-only $SHARED_CXXFLAGS $test 2>>$run_log 1>>$run_log"; } else { $command = "\\ $CLANG_AMP $SHARED_CXXFLAGS $test -o $tmpdir/test.out 2>>$run_log 1>>$run_log"; } log_message("Command: $command\n" ."Build output:\n" ."<<<"); my $build_exit_code = system($command) >> 8; log_message(">>>\n" ."Build exit code: $build_exit_code"); my $exec_exit_code; my $timeout=0; if((not $Test::config{'compile_only'}) && $build_exit_code == 0 && $Test::config{'expected_success'}) { log_message("Execution output:\n" .'<<<'); eval { local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required alarm 60; $exec_exit_code = system("$test_exec 2>>$run_log 1>>$run_log") >> 8; alarm 0; }; if ($@) { die unless $@ eq "alarm\n"; # propagate unexpected errors $timeout=1; } log_message(">>>\n" ."Execution exit code: $exec_exit_code"); } # Interpret result my $result; if($timeout == 1) { $result = FAIL; } elsif(not $Test::config{'expected_success'}) # Negative test { if($build_exit_code != 0) { $result = PASS; } else { $result = FAIL; } } elsif($Test::config{'expected_failure'}) # Compile only test { if($build_exit_code != 0) { $result = FAIL; } elsif($exec_exit_code == 0) { $result = FAIL; } else { $result = PASS; } } elsif($Test::config{'compile_only'}) # Compile only test { if($build_exit_code == 0) { $result = PASS; } else { $result = FAIL; } } else # Executable test { if($build_exit_code != 0) { $result = FAIL; } elsif($exec_exit_code == 0) { $result = PASS; } elsif($exec_exit_code == 2) { $result = SKIP; } else { $result = FAIL; } } if($result == PASS) { $num_passed++; print "passed\n"; log_message('Result: passed'); } elsif($result == FAIL) { $num_failed++; if ($timeout == 1) { print "failed, timeout\n"; log_message('Result: failed, timeout'); } else { print "failed\n"; log_message('Result: failed'); } } elsif($result == SKIP) { $num_skipped++; print "skipped\n"; log_message('Result: skipped'); } else { exit_message(1, "Unexpected result!"); } } #chdir($tests_root); log_message("====================================================="); } ### Print summary my $num_total = $num_passed + $num_skipped + $num_failed; print "==========================\n"; if($num_total != 0) { printf(" Passed: %d (%.3f%%)\n", $num_passed, $num_passed / $num_total * 100); printf(" Skipped: %d (%.3f%%)\n", $num_skipped, $num_skipped / $num_total * 100); printf(" Failed: %d (%.3f%%)\n", $num_failed, $num_failed / $num_total * 100); } print " Total: $num_total\n"; print "==========================\n"; if ($has_test_list && $num_failed>0) { exit_message(1, "Conformance tests failed\n"); } ### Subroutines # Use: exit_message(code, msg) sub exit_message { if(@_ != 2) { die('exit_message expects 2 arguments'); } print("\n".($_[0] == 0 ? 'SUCCESS' : 'FAILURE').": ".$_[1]); exit($_[0]); } # Use: log_message(msg, ...) sub log_message { open(FH, ">>", $run_log) or &exit_message(1, "Cannot open $run_log"); print FH "@_\n"; close(FH); } # Use: bool_str(val) # Returns: string 'true'/'false' sub bool_str { return $_[0] ? 'true' : 'false'; }