From 3584a4eceb8032c36a896952a87143fcf38d4ffb Mon Sep 17 00:00:00 2001 From: Brad King Date: Sun, 19 Oct 2008 11:53:01 -0400 Subject: [PATCH] ENH: Test CTest update logic with VCS tools This creates new tests "CTest.UpdateSVN" and "CTest.UpdateCVS". They test that the Update.xml produced by CTest for a version-controlled project contains entries for files added, changed, and removed. --- Tests/CMakeLists.txt | 51 +++++++++++ Tests/CTestUpdateCVS.cmake.in | 114 ++++++++++++++++++++++++ Tests/CTestUpdateCommon.cmake | 159 ++++++++++++++++++++++++++++++++++ Tests/CTestUpdateSVN.cmake.in | 116 +++++++++++++++++++++++++ 4 files changed, 440 insertions(+) create mode 100644 Tests/CTestUpdateCVS.cmake.in create mode 100644 Tests/CTestUpdateCommon.cmake create mode 100644 Tests/CTestUpdateSVN.cmake.in diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 336ccf056..deffd9fc8 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -820,6 +820,57 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=CVS -P ${CMake_SOURCE_DIR}/Utilities/Rel LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/BundleGeneratorTest") ENDIF(APPLE AND CTEST_TEST_CPACK) + SET(CTEST_TEST_UPDATE 1) + IF(CTEST_TEST_UPDATE) + # Test CTest Update with Subversion + FIND_PACKAGE(Subversion QUIET) + IF(Subversion_FOUND) + GET_FILENAME_COMPONENT(_Subversion_BIN_DIR + ${Subversion_SVN_EXECUTABLE} PATH) + FIND_PROGRAM(Subversion_SVNADMIN_EXECUTABLE svnadmin + HINTS ${_Subversion_BIN_DIR} + ) + MARK_AS_ADVANCED(Subversion_SVNADMIN_EXECUTABLE) + IF(NOT Subversion_SVNADMIN_EXECUTABLE) + SET(Subversion_FOUND FALSE) + ENDIF(NOT Subversion_SVNADMIN_EXECUTABLE) + ENDIF(Subversion_FOUND) + IF(Subversion_FOUND) + SET(CTestUpdateSVN_DIR "CTest UpdateSVN") + CONFIGURE_FILE("${CMake_SOURCE_DIR}/Tests/CTestUpdateSVN.cmake.in" + "${CMake_BINARY_DIR}/Tests/CTestUpdateSVN.cmake" @ONLY) + ADD_TEST(CTest.UpdateSVN ${CMAKE_CMAKE_COMMAND} + -P "${CMake_BINARY_DIR}/Tests/CTestUpdateSVN.cmake" + ) + LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${CTestUpdateSVN_DIR}") + ENDIF(Subversion_FOUND) + + # Test CTest Update with CVS + IF(UNIX) + # The test is expected to work by default on UNIX-like systems. + SET(CTEST_TEST_UPDATE_CVS 1) + ELSE(UNIX) + # Special CVS configuration is needed for test to pass on Windows. + OPTION(CTEST_TEST_UPDATE_CVS + "Enable CTest.UpdateCVS test. Requires extra CVS setup on windows." + OFF) + MARK_AS_ADVANCED(CTEST_TEST_UPDATE_CVS) + ENDIF(UNIX) + IF(CTEST_TEST_UPDATE_CVS) + FIND_PACKAGE(CVS QUIET) + ENDIF(CTEST_TEST_UPDATE_CVS) + IF(CTEST_TEST_UPDATE_CVS AND CVS_FOUND) + SET(CTestUpdateCVS_DIR "CTest UpdateCVS") + CONFIGURE_FILE("${CMake_SOURCE_DIR}/Tests/CTestUpdateCVS.cmake.in" + "${CMake_BINARY_DIR}/Tests/CTestUpdateCVS.cmake" @ONLY) + ADD_TEST(CTest.UpdateCVS ${CMAKE_CMAKE_COMMAND} + -P "${CMake_BINARY_DIR}/Tests/CTestUpdateCVS.cmake" + ) + LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${CTestUpdateCVS_DIR}") + ENDIF(CTEST_TEST_UPDATE_CVS AND CVS_FOUND) + + ENDIF(CTEST_TEST_UPDATE) + IF (CTEST_TEST_CTEST AND CMAKE_RUN_LONG_TESTS) CONFIGURE_FILE("${CMake_SOURCE_DIR}/Tests/CTestTest/test.cmake.in" "${CMake_BINARY_DIR}/Tests/CTestTest/test.cmake" @ONLY ESCAPE_QUOTES) diff --git a/Tests/CTestUpdateCVS.cmake.in b/Tests/CTestUpdateCVS.cmake.in new file mode 100644 index 000000000..f1bfc3065 --- /dev/null +++ b/Tests/CTestUpdateCVS.cmake.in @@ -0,0 +1,114 @@ +# This script drives creation of a CVS repository and checks +# that CTest can update from it. + +#----------------------------------------------------------------------------- +# Test in a directory next to this script. +get_filename_component(TOP "${CMAKE_CURRENT_LIST_FILE}" PATH) +set(TOP "${TOP}/@CTestUpdateCVS_DIR@") + +# Include code common to all update tests. +include("@CMAKE_CURRENT_SOURCE_DIR@/CTestUpdateCommon.cmake") + +#----------------------------------------------------------------------------- +# Report CVS tools in use. +message("Using CVS tools:") +set(CVS "@CVS_EXECUTABLE@") +message(" cvs = ${CVS}") + +set(REPO ${TOP}/repo) +set(CVSCMD ${CVS} -d${REPO}) + +#----------------------------------------------------------------------------- +# Initialize the testing directory. +message("Creating test directory...") +init_testing() + +#----------------------------------------------------------------------------- +# Create the repository. +message("Creating repository...") +file(MAKE_DIRECTORY ${TOP}/repo) +run_child( + COMMAND ${CVSCMD} init + ) + +#----------------------------------------------------------------------------- +# Import initial content into the repository. +message("Importing content...") +create_content(import) + +# Import the content into the repository. +run_child( + WORKING_DIRECTORY ${TOP}/import + COMMAND ${CVSCMD} import -m "Initial content" Project vendor-tag release-tag + ) + +#----------------------------------------------------------------------------- +# Create a working tree. +message("Checking out revision 1...") +run_child( + WORKING_DIRECTORY ${TOP} + COMMAND ${CVSCMD} co -d user-source Project + ) +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${CVSCMD} tag Revision1 + ) + +#----------------------------------------------------------------------------- +# Make changes in the working tree. +message("Changing content...") +update_content(user-source files_added files_removed) +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${CVSCMD} add ${files_added} + ) +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${CVSCMD} rm ${files_removed} + ) + +#----------------------------------------------------------------------------- +# Commit the changes to the repository. +message("Committing revision 2...") +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${CVSCMD} commit -m "Changed content" + ) + +#----------------------------------------------------------------------------- +# Go back to before the changes so we can test updating. +message("Backing up to revision 1...") +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${CVSCMD} up -rRevision1 + ) + +#----------------------------------------------------------------------------- +# Test updating the user work directory with the command-line interface. +message("Running CTest Dashboard Command Line...") + +# Create the user build tree. +create_build_tree(user-source user-binary) +file(APPEND ${TOP}/user-binary/CTestConfiguration.ini + "# CVS command configuration +CVSCommand: ${CVS} +CVSUpdateOptions: -dAP +") + +# Run the dashboard command line interface. +run_dashboard_command_line(user-binary) + +#----------------------------------------------------------------------------- +# Test initial checkout and update with a dashboard script. +message("Running CTest Dashboard Script...") + +create_dashboard_script(dashboard.cmake + "# CVS command configuration +set(CTEST_CVS_COMMAND \"${CVS}\") +set(CTEST_CVS_UPDATE_OPTIONS -dAP) +set(CTEST_CHECKOUT_COMMAND + \"\\\"\${CTEST_CVS_COMMAND}\\\" -d \\\"${REPO}\\\" co -rRevision1 -d dash-source Project\") +") + +# Run the dashboard script with CTest. +run_dashboard_script(dashboard.cmake) diff --git a/Tests/CTestUpdateCommon.cmake b/Tests/CTestUpdateCommon.cmake new file mode 100644 index 000000000..655a6cf6e --- /dev/null +++ b/Tests/CTestUpdateCommon.cmake @@ -0,0 +1,159 @@ +#----------------------------------------------------------------------------- +# Function to run a child process and report output only on error. +function(run_child) + execute_process(${ARGN} + RESULT_VARIABLE FAILED + OUTPUT_VARIABLE OUTPUT + ERROR_VARIABLE OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ) + if(FAILED) + string(REGEX REPLACE "\n" "\n " OUTPUT "${OUTPUT}") + message(FATAL_ERROR "Child failed. Output is\n ${OUTPUT}\n") + endif(FAILED) +endfunction(run_child) + +#----------------------------------------------------------------------------- +# Function to find the Update.xml file and check for expected entries. +function(check_updates build) + # Find the Update.xml file for the given build tree + set(PATTERN ${TOP}/${build}/Testing/*/Update.xml) + file(GLOB UPDATE_XML_FILE RELATIVE ${TOP} ${PATTERN}) + string(REGEX REPLACE "//Update.xml$" "/Update.xml" + UPDATE_XML_FILE "${UPDATE_XML_FILE}" + ) + if(NOT UPDATE_XML_FILE) + message(FATAL_ERROR "Cannot find Update.xml with pattern\n ${PATTERN}") + endif(NOT UPDATE_XML_FILE) + message(" found ${UPDATE_XML_FILE}") + + # Read entries from the Update.xml file + file(STRINGS ${TOP}/${UPDATE_XML_FILE} UPDATE_XML_ENTRIES + REGEX "FullName" + LIMIT_INPUT 4096 + ) + + # Verify that expected entries exist + set(MISSING) + foreach(f ${ARGN}) + if(NOT "${UPDATE_XML_ENTRIES}" MATCHES "${f}") + list(APPEND MISSING ${f}) + endif() + endforeach(f) + + # Report the result + if(MISSING) + # List the missing entries + set(MSG "Update.xml is missing an entry for:\n") + foreach(f ${MISSING}) + set(MSG "${MSG} ${f}\n") + endforeach(f) + + # Provide the log file + file(GLOB UPDATE_LOG_FILE + ${TOP}/${build}/Testing/Temporary/LastUpdate*.log) + if(UPDATE_LOG_FILE) + file(READ ${UPDATE_LOG_FILE} UPDATE_LOG LIMIT 4096) + string(REGEX REPLACE "\n" "\n " UPDATE_LOG "${UPDATE_LOG}") + set(MSG "${MSG}Update log:\n ${UPDATE_LOG}") + else(UPDATE_LOG_FILE) + set(MSG "${MSG}No update log found!") + endif(UPDATE_LOG_FILE) + + # Display the error message + message(FATAL_ERROR "${MSG}") + else(MISSING) + # Success + message(" no entries missing from Update.xml") + endif(MISSING) +endfunction(check_updates) + +#----------------------------------------------------------------------------- +# Function to create initial content. +function(create_content dir) + file(MAKE_DIRECTORY ${TOP}/${dir}) + + # An example CTest project configuration file. + file(WRITE ${TOP}/${dir}/CTestConfig.cmake + "# CTest Configuration File +set(CTEST_PROJECT_NAME TestProject) +set(CTEST_NIGHTLY_START_TIME \"21:00:00 EDT\") +") + + # Some other files. + file(WRITE ${TOP}/${dir}/foo.txt "foo\n") + file(WRITE ${TOP}/${dir}/bar.txt "bar\n") +endfunction(create_content) + +#----------------------------------------------------------------------------- +# Function to update content. +function(update_content dir added_var removed_var) + file(APPEND ${TOP}/${dir}/foo.txt "foo line 2\n") + file(WRITE ${TOP}/${dir}/zot.txt "zot\n") + file(REMOVE ${TOP}/${dir}/bar.txt) + set(${added_var} zot.txt PARENT_SCOPE) + set(${removed_var} bar.txt PARENT_SCOPE) +endfunction(update_content) + +#----------------------------------------------------------------------------- +# Function to write CTestConfiguration.ini content. +function(create_build_tree src_dir bin_dir) + file(MAKE_DIRECTORY ${TOP}/${bin_dir}) + file(WRITE ${TOP}/${bin_dir}/CTestConfiguration.ini + "# CTest Configuration File +SourceDirectory: ${TOP}/${src_dir} +BuildDirectory: ${TOP}/${bin_dir} +Site: test.site +BuildName: user-test +") +endfunction(create_build_tree) + +#----------------------------------------------------------------------------- +# Function to write the dashboard test script. +function(create_dashboard_script name custom_text) + # Write the dashboard script. + file(WRITE ${TOP}/dashboard.cmake + "# CTest Dashboard Script +set(CTEST_DASHBOARD_ROOT \"${TOP}\") +set(CTEST_SITE test.site) +set(CTEST_BUILD_NAME dash-test) +set(CTEST_SOURCE_DIRECTORY \${CTEST_DASHBOARD_ROOT}/dash-source) +set(CTEST_BINARY_DIRECTORY \${CTEST_DASHBOARD_ROOT}/dash-binary) +${custom_text} +# Start a dashboard and run the update step +ctest_start(Experimental) +ctest_update(SOURCE \${CTEST_SOURCE_DIRECTORY}) +") +endfunction(create_dashboard_script) + +#----------------------------------------------------------------------------- +# Function to run the dashboard through the command line +function(run_dashboard_command_line bin_dir) + run_child( + WORKING_DIRECTORY ${TOP}/${bin_dir} + COMMAND ${CMAKE_CTEST_COMMAND} -M Experimental -T Start -T Update + ) + + # Verify the updates reported by CTest. + check_updates(${bin_dir} foo.txt bar.txt zot.txt) +endfunction(run_dashboard_command_line) + +#----------------------------------------------------------------------------- +# Function to run the dashboard through a script +function(run_dashboard_script name) + run_child( + WORKING_DIRECTORY ${TOP} + COMMAND ${CMAKE_CTEST_COMMAND} -S ${name} -V + ) + + # Verify the updates reported by CTest. + check_updates(dash-binary foo.txt bar.txt zot.txt) +endfunction(run_dashboard_script) + +#----------------------------------------------------------------------------- +# Function to initialize the testing directory. +function(init_testing) + file(REMOVE_RECURSE ${TOP}) + file(MAKE_DIRECTORY ${TOP}) +endfunction(init_testing) diff --git a/Tests/CTestUpdateSVN.cmake.in b/Tests/CTestUpdateSVN.cmake.in new file mode 100644 index 000000000..48df4218b --- /dev/null +++ b/Tests/CTestUpdateSVN.cmake.in @@ -0,0 +1,116 @@ +# This script drives creation of a Subversion repository and checks +# that CTest can update from it. + +#----------------------------------------------------------------------------- +# Test in a directory next to this script. +get_filename_component(TOP "${CMAKE_CURRENT_LIST_FILE}" PATH) +set(TOP "${TOP}/@CTestUpdateSVN_DIR@") + +# Include code common to all update tests. +include("@CMAKE_CURRENT_SOURCE_DIR@/CTestUpdateCommon.cmake") + +#----------------------------------------------------------------------------- +# Report subversion tools in use. +message("Using subversion tools:") +set(SVN "@Subversion_SVN_EXECUTABLE@") +set(SVNADMIN "@Subversion_SVNADMIN_EXECUTABLE@") +message(" svn = ${SVN}") +message(" svnadmin = ${SVNADMIN}") + +# Isolate svn test operations from the user configuration. +file(MAKE_DIRECTORY ${TOP}/config) +set(SVNCMD ${SVN} --config-dir ${TOP}/config) +set(SVNUSER --username testauthor --non-interactive) + +#----------------------------------------------------------------------------- +# Initialize the testing directory. +message("Creating test directory...") +init_testing() + +#----------------------------------------------------------------------------- +# Create the repository. +message("Creating repository...") +file(MAKE_DIRECTORY ${TOP}/repo) +run_child( + COMMAND ${SVNADMIN} create --config-dir ${TOP}/config ${TOP}/repo + ) +set(REPO file:///${TOP}/repo/trunk) + +#----------------------------------------------------------------------------- +# Import initial content into the repository. +message("Importing content...") +create_content(import) + +# Import the content into the repository. +run_child( + WORKING_DIRECTORY ${TOP}/import + COMMAND ${SVNCMD} import ${SVNUSER} -m "Initial content" . "${REPO}" + ) + +#----------------------------------------------------------------------------- +# Create a working tree. +message("Checking out revision 1...") +run_child( + WORKING_DIRECTORY ${TOP} + COMMAND ${SVNCMD} co ${SVNUSER} ${REPO} user-source + ) + +#----------------------------------------------------------------------------- +# Make changes in the working tree. +message("Changing content...") +update_content(user-source files_added files_removed) +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${SVNCMD} add ${files_added} + ) +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${SVNCMD} rm ${files_removed} + ) + +#----------------------------------------------------------------------------- +# Commit the changes to the repository. +message("Committing revision 2...") +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${SVNCMD} commit -m "Changed content" + ) + +#----------------------------------------------------------------------------- +# Go back to before the changes so we can test updating. +message("Backing up to revision 1...") +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${SVNCMD} up -r1 + ) + +#----------------------------------------------------------------------------- +# Test updating the user work directory with the command-line interface. +message("Running CTest Dashboard Command Line...") + +# Create the user build tree. +create_build_tree(user-source user-binary) +file(APPEND ${TOP}/user-binary/CTestConfiguration.ini + "# SVN command configuration +SVNCommand: ${SVN} +SVNUpdateOptions: --config-dir \"${TOP}/config\" +") + +# Run the dashboard command line interface. +run_dashboard_command_line(user-binary) + +#----------------------------------------------------------------------------- +# Test initial checkout and update with a dashboard script. +message("Running CTest Dashboard Script...") + +create_dashboard_script(dashboard.cmake + "# Subversion command configuration +set(CTEST_SVN_COMMAND \"${SVN}\") +set(CTEST_SVN_UPDATE_OPTIONS + \"--config-dir \\\"\${CTEST_DASHBOARD_ROOT}/config\\\"\") +set(CTEST_CHECKOUT_COMMAND + \"\\\"\${CTEST_SVN_COMMAND}\\\" co -r1 \\\"${REPO}\\\" dash-source\") +") + +# Run the dashboard script with CTest. +run_dashboard_script(dashboard.cmake)