Является ли хорошей практикой запуск потока из модульного теста? - PullRequest
0 голосов
/ 27 октября 2018

У меня есть класс, который имеет функцию execute ().Выполнение функции execute () останавливается только при вызове функции terminate ().Я хочу проверить функцию execute ().

class Process{
public:

    void execute(){ // start execution until terminate() is called.. }

    void terminate(){ //stop the processing of execute()... }

}

Мой пример модульного теста приведен ниже.Я использую MSTest.

TEST_METHOD(StartTest)
{
    Process p;
    bool isRunning = true;
    std::thread th([&](){
        p.execute();
        isRunning = false;
    });
    th.detach();
    std::this_thread::sleep_for(std::chrono::milliseconds(300));

    Assert::isTrue(isRunning);
}

Если вы используете поток, рекомендуется ли закрывать поток внутри контрольного примера, а не отсоединять его от основного потока?

Также лучше предложение.

1 Ответ

0 голосов
/ 27 октября 2018

Прежде всего доступ к isRunning должен быть синхронизирован. В вашем примере вы можете просто использовать std::atomic<bool> и покончить с этим.

Отказ от ответственности: прошло много времени с тех пор, как я выполнил какую-либо серию многопоточности, поэтому возьмите это с крошкой соли. Кроме того, я не проверял код, кроме проверки его компиляции.

Вот где я бы начал:

auto test()
{
    std::condition_variable cv{};
    std::mutex m{};

    Process p{};
    bool isRunning{true};

    std::thread th([&] {
        p.execute();        
        {
            std::lock_guard<std::mutex> lk{m};
            isRunning = false;
        }
        cv.notify_one();
    });

    {
        std::unique_lock<std::mutex> lk{m};
        // expect timeout
        Assert::isFalse(cv.wait_for(lk, std::chrono::milliseconds(300),
                                   [&] () { return !isRunning; }));
    }

    p.terminate();

    {
        std::unique_lock<std::mutex> lk{m};
        // expect condition to change
        Assert::isTrue(cv.wait_for(lk, std::chrono::milliseconds(300),
                                   [&] () { return !isRunning; }));
    }

    th.join();
}

Таким образом, вы проверяете блокировку execute и завершение terminate, и вы получаете больше гибкости. Если execute рано разблокируется, вы не ждете своего полного тайм-аута, и для terminate у вас появляется покачивание, чтобы дождаться завершения другого потока, и вы разблокируете, как только он это сделает.


Если terminate () не может остановить выполнение, будет ли продолжен поток его выполнение после окончания этого теста?

Если terminate не останавливает выполнение, то второе wait_for заканчивается после тайм-аута, возвращающего false, и активируется подтверждение. Я не знаю, какую платформу тестирования вы используете и что делает Assert.

  • если он вернет выполнение к test, тогда тест будет блокироваться на join, пока поток не завершит

  • если выдается исключение, то join не вызывается и вызывается деструктор th, если поток еще не закончился, std::terminate вызывается. Это можно изменить с помощью try catch

  • если он вызывает выход (например, вызывает std::terminate), тогда ... ну ... ваша программа завершается независимо от

Это действительно проблема, которую вам нужно проанализировать. Все зависит от того, что вы хотите сделать, если terminate не сможет остановить execute в пределах вашего интервала ожидания.

  • если вы в порядке ожидания в test, то все, что вам нужно сделать, это убедиться, что join вызван. Как я уже сказал, это разрешимо с try catch.

  • если вы хотите завершить текущий тест, но согласны с тем, что поток все еще продолжается, вам нужно отсоединить поток, если terminate не удалось завершить его.

  • если вы хотите убить поток, тогда ... это невозможно. Вместо этого вы можете убить все приложение через std::terminate.

...