From ceaef94cccf10a02e1bcce29d3cfa6955acf4565 Mon Sep 17 00:00:00 2001 From: David Cole Date: Wed, 26 Nov 2008 14:38:43 -0500 Subject: ENH: Implement feature request from issue 7885. Allow setting environment variables on a per-test basis for ctest using set_test_properties ENVIRONMENT. --- Source/CTest/cmCTestBuildAndTestHandler.cxx | 2 +- Source/CTest/cmCTestTestHandler.cxx | 20 +++++++++--- Source/CTest/cmCTestTestHandler.h | 1 + Source/cmCTest.cxx | 27 +++++++++++++++- Source/cmCTest.h | 6 ++-- Source/cmSystemTools.cxx | 50 +++++++++++++++++++++++++++++ Source/cmSystemTools.h | 11 +++++++ Tests/CMakeLists.txt | 13 ++++++++ Tests/Environment/CMakeLists.txt | 26 +++++++++++++++ Tests/Environment/main.cxx | 16 +++++++++ 10 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 Tests/Environment/CMakeLists.txt create mode 100644 Tests/Environment/main.cxx diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index cb713a6..92d8270 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -382,7 +382,7 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) } int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, 0, - remainingTime); + remainingTime, 0); if(runTestRes != cmsysProcess_State_Exited || retval != 0) { diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 69090d6..9a747ac 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -733,11 +733,11 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it, if ( !this->CTest->GetShowOnly() ) { res = this->CTest->RunTest(arguments, &output, &retVal, this->LogFile, - it->Timeout); + it->Timeout, &it->Environment); } - - clock_finish = cmSystemTools::GetTime(); - + + clock_finish = cmSystemTools::GetTime(); + if ( this->LogFile ) { double ttime = clock_finish - clock_start; @@ -2209,7 +2209,7 @@ bool cmCTestTestHandler::SetTestsProperties( } } if ( key == "DEPENDS" ) - { + { std::vector lval; cmSystemTools::ExpandListArgument(val.c_str(), lval); std::vector::iterator crit; @@ -2218,6 +2218,16 @@ bool cmCTestTestHandler::SetTestsProperties( rtit->Depends.push_back(*crit); } } + if ( key == "ENVIRONMENT" ) + { + std::vector lval; + cmSystemTools::ExpandListArgument(val.c_str(), lval); + std::vector::iterator crit; + for ( crit = lval.begin(); crit != lval.end(); ++ crit ) + { + rtit->Environment.push_back(*crit); + } + } if ( key == "MEASUREMENT" ) { size_t pos = val.find_first_of("="); diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index b56f817..451a477 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -90,6 +90,7 @@ public: bool WillFail; double Timeout; int Index; + std::vector Environment; }; struct cmCTestTestResult diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 049248e..629282b 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -1105,8 +1105,12 @@ int cmCTest::RunMakeCommand(const char* command, std::string* output, //---------------------------------------------------------------------- int cmCTest::RunTest(std::vector argv, std::string* output, int *retVal, - std::ostream* log, double testTimeOut) + std::ostream* log, double testTimeOut, + std::vector* environment) { + std::vector origEnv; + bool modifyEnv = (environment && environment->size()>0); + // determine how much time we have double timeout = this->GetRemainingTimeAllowed() - 120; if (this->TimeOut && this->TimeOut < timeout) @@ -1156,6 +1160,11 @@ int cmCTest::RunTest(std::vector argv, } std::string oldpath = cmSystemTools::GetCurrentWorkingDirectory(); + if (modifyEnv) + { + origEnv = cmSystemTools::AppendEnv(environment); + } + *retVal = inst.Run(args, output); if ( *log ) { @@ -1166,6 +1175,12 @@ int cmCTest::RunTest(std::vector argv, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Internal cmCTest object used to run test." << std::endl << *output << std::endl); + + if (modifyEnv) + { + cmSystemTools::RestoreEnv(origEnv); + } + return cmsysProcess_State_Exited; } std::vector tempOutput; @@ -1174,6 +1189,11 @@ int cmCTest::RunTest(std::vector argv, *output = ""; } + if (modifyEnv) + { + origEnv = cmSystemTools::AppendEnv(environment); + } + cmsysProcess* cp = cmsysProcess_New(); cmsysProcess_SetCommand(cp, &*argv.begin()); cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl); @@ -1233,6 +1253,11 @@ int cmCTest::RunTest(std::vector argv, } cmsysProcess_Delete(cp); + if (modifyEnv) + { + cmSystemTools::RestoreEnv(origEnv); + } + return result; } diff --git a/Source/cmCTest.h b/Source/cmCTest.h index a337460..2a18063 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -246,9 +246,11 @@ public: void SetProduceXML(bool v); //! Run command specialized for tests. Returns process status and retVal is - // return value or exception. + // return value or exception. If environment is non-null, it is used to set + // environment variables prior to running the test. After running the test, + // environment variables are restored to their previous values. int RunTest(std::vector args, std::string* output, int *retVal, - std::ostream* logfile, double testTimeOut); + std::ostream* logfile, double testTimeOut, std::vector* environment); /** * Execute handler and return its result. If the handler fails, it returns diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 298d779..e47ca19 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -1556,6 +1556,7 @@ bool cmSystemTools::PutEnv(const char* value) } #ifdef CMAKE_BUILD_WITH_CMAKE +//---------------------------------------------------------------------- bool cmSystemTools::UnsetEnv(const char* value) { #if !defined(HAVE_UNSETENV) @@ -1568,6 +1569,7 @@ bool cmSystemTools::UnsetEnv(const char* value) #endif } +//---------------------------------------------------------------------- std::vector cmSystemTools::GetEnvironmentVariables() { std::vector env; @@ -1578,6 +1580,54 @@ std::vector cmSystemTools::GetEnvironmentVariables() } return env; } + +//---------------------------------------------------------------------- +std::vector cmSystemTools::AppendEnv( + std::vector* env) +{ + std::vector origEnv = GetEnvironmentVariables(); + + if (env && env->size()>0) + { + std::vector::const_iterator eit; + + for (eit = env->begin(); eit!= env->end(); ++eit) + { + PutEnv(eit->c_str()); + } + } + + return origEnv; +} + +//---------------------------------------------------------------------- +void cmSystemTools::RestoreEnv(const std::vector& env) +{ + std::vector::const_iterator eit; + + // First clear everything in the current environment: + // + std::vector currentEnv = GetEnvironmentVariables(); + for (eit = currentEnv.begin(); eit!= currentEnv.end(); ++eit) + { + std::string var(*eit); + + int pos = var.find("="); + if (pos != std::string::npos) + { + var = var.substr(0, pos); + } + + UnsetEnv(var.c_str()); + } + + // Then put back each entry from the original environment: + // + for (eit = env.begin(); eit!= env.end(); ++eit) + { + PutEnv(eit->c_str()); + } +} #endif void cmSystemTools::EnableVSConsoleOutput() diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 1ff12bf..3726c44 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -335,6 +335,17 @@ public: /** Get the list of all environment variables */ static std::vector GetEnvironmentVariables(); + + /** Append multiple variables to the current environment. + Return the original environment, as it was before the + append. */ + static std::vector AppendEnv( + std::vector* env); + + /** Restore the full environment to "env" - use after + AppendEnv to put the environment back to the way it + was. */ + static void RestoreEnv(const std::vector& env); #endif /** Setup the environment to enable VS 8 IDE output. */ diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 1035427..05dafb0 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -497,6 +497,19 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=CVS -P ${CMake_SOURCE_DIR}/Utilities/Rel ) LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Example") + ADD_TEST(Environment ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Environment" + "${CMake_BINARY_DIR}/Tests/Environment" + --build-generator ${CMAKE_TEST_GENERATOR} + --build-project EnvironmentProj + --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM} + --build-exe-dir "${CMake_BINARY_DIR}/Tests/Environment" + --force-new-ctest-process + --test-command ${CMAKE_CTEST_COMMAND} -VV + ) + LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Environment") + # do each of the tutorial steps FOREACH(STP RANGE 1 7) ADD_TEST(TutorialStep${STP} ${CMAKE_CTEST_COMMAND} diff --git a/Tests/Environment/CMakeLists.txt b/Tests/Environment/CMakeLists.txt new file mode 100644 index 0000000..2b18d24 --- /dev/null +++ b/Tests/Environment/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 2.6) +project(EnvironmentProj) + +add_executable(Environment main.cxx) + +enable_testing() + +add_test(Environment1 Environment) +add_test(Environment2 Environment) +add_test(EchoEnvironment1 ${CMAKE_COMMAND} -E environment) +add_test(EchoEnvironment2 ${CMAKE_COMMAND} -E environment) + +# Make sure "CMAKE_ENV.*Happy Thanksgiving" is in the output of +# the "1" tests: +# +set_tests_properties(Environment1 EchoEnvironment1 PROPERTIES + ENVIRONMENT "CMAKE_ENVIRONMENT_TEST_VAR=Happy Thanksgiving!" + PASS_REGULAR_EXPRESSION "CMAKE_ENV.*Happy Thanksgiving" +) + +# Make sure "CMAKE_ENV.*Happy Thanksgiving" is *NOT* in the output of +# the "2" tests: +# +set_tests_properties(Environment2 EchoEnvironment2 PROPERTIES + FAIL_REGULAR_EXPRESSION "CMAKE_ENV.*Happy Thanksgiving" +) diff --git a/Tests/Environment/main.cxx b/Tests/Environment/main.cxx new file mode 100644 index 0000000..2e1bf4c --- /dev/null +++ b/Tests/Environment/main.cxx @@ -0,0 +1,16 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + char* var = getenv("CMAKE_ENVIRONMENT_TEST_VAR"); + if (!var) + { + var = "(null)"; + } + + fprintf(stdout, "Environment:\n"); + fprintf(stdout, " CMAKE_ENVIRONMENT_TEST_VAR='%s'\n", var); + + return 0; +} -- cgit v0.12