5

私は、実行時間が 0.001 秒から 1800 秒の間で変化する、400 近くのテスト実行可能ファイルを含む大規模なコード ベースに取り組んでいます。コードの一部が変更されると、CMake は変更されたターゲットのみをインテリジェントに再構築します。多くの場合、実際のテスト実行よりも短い時間がかかります。

これについて私が知っている唯一の方法は、実行したいことがわかっているテストを手動でフィルタリングすることです。私の直感では、失敗したか再コンパイルされたために、成功した実行が保存されていないテスト スイートを再実行したいと考えています。

これは可能ですか?もしそうなら、どのように?

4

1 に答える 1

6

ctest コマンドは、実行する一連のテストに影響を与えるいくつかのパラメーターを受け入れます。たとえば、"-R" - テストを名前でフィルター処理し、"-L" - テストをラベルでフィルター処理します。おそらく、ダッシュボード関連のオプションを使用して、実行するテストを選択することもできます。

変更された実行可能ファイルに応じてこれらのオプションの値を生成する場合、実行可能ファイルの変更時間をチェックしたり、失敗したテストを見つけるために最後のログ ファイルを解析したりするプログラムまたはスクリプトを作成できます。

変更された実行可能ファイルのみを実行する別の方法は、テストを追加のスクリプトにラップすることです。このスクリプトは、いくつかの条件が満たされた場合にのみ実行可能になります。

Linux ラッパー スクリプトの場合、次のように実装できます。

test_wrapper.sh :

# test_wrapper.sh <test_name> <executable> <params..>
# Run executable, given as second argument, with parameters, given as futher arguments.
#
# If environment variable `LAST_LOG_FILE` is set,
# checks that this file is older than the executable.
#
# If environment variable LAST_LOG_FAILED_FILE is set,
# check that testname is listed in this file.
#
# Test executable is run only if one of these checks succeed, or if none of checks is performed.

check_succeed=
check_performed=
if [ -n $LAST_LOG_FILE ]; then
    check_performed=1
    executable=$2
    if [ ! ( -e "$LAST_LOG_FILE" ) ]; then
        check_succeed=1 # Log file is absent
    elif [ "$LAST_LOG_FILE" -ot "$executable" ]; then
        check_succeed=1 # Log file is older than executable
    fi
fi

if [ -n "$LAST_LOG_FAILED_FILE" ]; then
    check_performed=1
    testname=$1
    if [ ! ( -e "$LAST_LOG_FAILED_FILE" ) ]; then
        # No failed tests at all
    elif grep ":${testname}\$" "$LAST_LOG_FAILED_FILE" > /dev/null; then
        check_succeed=1 # Test has been failed previously
    fi
fi

if [ -n "$check_performed" -a -z "$check_succeed" ]; then
    echo "Needn't to run test."
    exit 0
fi

shift 1 # remove `testname` argument
eval "$*"

ラップされたテストを追加するための CMake マクロ:

CMakeLists.txt :

# Similar to add_test(), but test is executed with our wrapper.
function(add_wrapped_test name command)
    if(name STREQUAL "NAME")
        # Complex add_test() command flow: NAME <name> COMMAND <command> ...
        set(other_params ${ARGN})
        list(REMOVE_AT other_params 0) # COMMAND keyword
        # Actual `command` argument
        list(GET other_params 0 real_command)
        list(REMOVE_AT other_params 0)
        # If `real_command` is a target, need to translate it to path to executable.
        if(TARGET real_command)
            # Generator expression is perfectly OK here.
            set(real_command "$<TARGET_FILE:${real_command}")
        endif()
        # `command` is actually value of 'NAME' parameter
        add_test("NAME" ${command} "COMMAND" /bin/sh <...>/test_wrapper.sh
            ${command} ${real_command} ${other_params}
        )
    else() # Simple add_test() command flow
        add_test(${name} /bin/sh <...>/test_wrapper.sh
            ${name} ${command} ${ARGN}
        )
    endif()
endfunction(add_wrapped_test)

前回の実行以降に変更された実行可能ファイルまたは前回失敗した実行可能ファイルのみを実行する場合は、次を使用します。

LAST_LOG_FILE=<build-dir>/Testing/Temporary/LastTest.log \
LAST_FAILED_LOG_FILE=<build-dir>/Testing/Temporary/LastTestsFailed.log \
ctest

他のすべてのテストは自動的に合格します。

于 2016-01-12T11:34:20.957 に答える