Как я могу использовать cmake для тестирования процессов, которые, как ожидается, завершатся ошибкой с исключением? (например, сбои из-за дезинфицирующего средства адреса clang)

У меня есть несколько тестов, которые проверяют, что дезинфицирующее средство для адресов clang выявляет определенные ошибки. (Я хочу убедиться, что мое понимание типов ошибок, которые он может отловить, правильное, и что будущие версии продолжат отлавливать тип ошибок, который я ожидаю от них.) Это означает, что у меня есть несколько тестов, которые проваливаются из-за ошибки OTHER_FAULT, что, по-видимому, является фиксированным способом, при котором среда выполнения clang сообщает об ошибке.

Я установил флаг WILL_FAIL на TRUE для этих тестов, но это, похоже, только для проверки возвращаемого значения после успешного отказа без исключений. Если процесс завершается с исключением, cmake все равно классифицирует его как сбой.

Я также пытался использовать PASS_REGULAR_EXPRESSION для отслеживания отличительных сообщений, которые распечатываются при возникновении этой ошибки, но опять же, cmake, похоже, классифицирует тест как неудачный, если он завершается с исключением.

Могу ли я что-нибудь сделать, чтобы обойти это?

(ответы, относящиеся к clang, также являются вариантом! - но я сомневаюсь, что это будет последний раз, когда мне нужно будет протестировать что-то подобное, поэтому я бы предпочел знать, как это сделать с помощью cmake в целом, если это возможно)


person Tom Seddon    schedule 13.11.2015    source источник


Ответы (2)


CTest предоставляет только основные, широко используемые интерпретаторы результатов тестовых программ. Для реализации других интерпретаторов вы можете написать простую программу/скрипт, который оборачивает тест и интерпретирует его результат по мере необходимости. Например. Программа C (для Linux):

test_that_crash.c:

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

int main(int argc, char** argv)
{
    pid_t pid = fork();
    if(pid == -1)
    {
        // fork fails
        return 1;
    }
    else if(pid)
    {
        // Parent - wait child and interpret its result
        int status = 0;
        wait(&status);
        if(WIFSIGNALED(status)) return 0; // Signal-terminated means success
        else return 1;
    }
    else
    {
        // Child - execute wrapped command
        execvp(argv[1], argv + 1);
        exit(1);
    }
}

Эту программу можно использовать в CMake следующим образом:

CMakeLists.txt:

# Compile our wrapper
add_executable(test_that_crash test_that_crash.c)
# Similar to add_test(name command), but test is assumed successfull only if it is crashed(signalled)
macro(add_test_crashed name command)
    # Use generic flow of add_test() command for automatically recognize our executable target
    add_test(NAME ${name} COMMAND test_that_crash ${command} ${ARGN})
endmacro(add_test_crashed)
# ...

# Add some test, which should crash
add_test_crashed(clang.crash.1 <clang-executable> <clang-args>)
person Tsyvarev    schedule 13.11.2015
comment
Спасибо. Фрагмент cmake тоже был оценен :) Одна вещь, которую я обнаружил, когда собирал это вместе, заключалась в том, что в clang-700.1.76 (которую я использую в OS X) сбой дезинфицирующего средства останавливает процесс с помощью SIGABRT, что делает вышеописанное необходимым. Но с gcc 4.8.4 (который я использую в Linux) процесс просто завершается с кодом выхода 1, что означает, что вы можете просто установить WILL_FAIL в TRUE. (Linux clang пока неизвестен.) - person Tom Seddon; 15.11.2015
comment
Для всех, кто читает это позже: дезинфицирующее средство Linux clang также завершает работу с кодом выхода 1. Интересно, почему они не сделали этого для OS X. - person Tom Seddon; 15.11.2015

Существует также специфичное для clang решение: настроить его способ выхода с помощью переменной окружения ASAN_OPTIONS. (См. https://github.com/google/sanitizers/wiki/AddressSanitizerFlags.) Для этого установите для переменной среды ASAN_OPTIONS значение abort_on_error=0. Когда средство очистки адресов обнаружит проблему, процесс выполнит _exit(1), а не (предположительно) abort(), и, таким образом, будет казаться, что он завершился корректно. Затем вы можете подобрать это, используя механизм cmake WILL_FAIL. (До сих пор неясно, почему OS X и Linux различаются в этом отношении, но вот так.)

В качестве бонуса тест проваливается гораздо быстрее.

(Еще одна удобная опция, которая может сократить время обработки при выполнении cmake, — установить ASAN_SYMBOLIZER_PATH в пустое значение, что остановит дезинфицирующее средство адреса, символизирующее трассировку стека. Символизация занимает некоторое время, но нет смысла делать это при запуске через cmake, так как вы вывод не видно)

Вместо того, чтобы делать это вручную, я сделал скрипт Python, который соответствующим образом настраивает среду в OS X (ничего не делая в Linux) и вызывает тест. Затем я добавляю каждый тест асан с помощью макроса в соответствии с ответом Цыварева.

macro(add_asan_test basename)
  add_executable(${basename} ${basename}.c)
  add_test(NAME test/${basename} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/wrap_clang_sanitizer_test.py -a $<TARGET_FILE:${basename}>)
  set_tests_properties(test/${basename} PROPERTIES WILL_FAIL TRUE)
endmacro()

Это дает простой проход/непрохождение как можно быстрее. У меня есть привычка исследовать сбои, запуская соответствующий тест из оболочки вручную и проверяя вывод, и в этом случае я получаю трассировку стека как обычно (и тот факт, что выход с помощью abort немного медленный, меньше проблема).

(Есть аналогичные варианты для других дезинфицирующих средств, но я их не исследовал.)

person Tom Seddon    schedule 17.11.2015