1

I was doing Unit Testing and finding code coverage using gtest and lcov. My function is

void MyClass::MyFunction(const std::string& argument1, const std::string& argument2) {
    std::thread([this, argument1, argument2]() {
        std::unique_lock<std::mutex> live_lock_(live_mutex_);
        int64_t time_stamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        int64_t time_to_live = myList[argument1] - (time_stamp % myList[argument1]); 
        cond_var_.wait_for(time_to_live_lock_, std::chrono::milliseconds(time_to_live), [this, argument1] { return cond_var_status_[argument1]; });
        if (message_master_.find(argument1) != message_master_.end()) {
            //something
        }
    }).detach();
    std::cout<< "end line is executed"<<std::endl; }

and my test function is

TEST(test, test1) {
    Myclass testObj;
    testObj.MyFunction("arg1", "arg2");
    ASSERT_EQ("","");
};

When I run the test, all codes except those inside thread are executed. So is there any solution to call those codes inside thread too?

Jff
  • 91
  • 1
  • 8
  • 1
    I doubt it, since you have gone out of your way to create a thread and detach it, so its execution continues independently of the calling thread - without keeping any reference to the `std::thread` object that manages the thread. Which means that the calling thread immediately relinquishes control. Unless the thread does "something" with external observable effects, and your test case explicitly finds a way to wait on the thread and check if observable effects have occurred, there is no solution. You need to design for both multithreading and testability, not just for multithreading. – Peter Jan 25 '21 at 08:28
  • @Yksisarvinen Yes, `cond_var_` is `std::condition_variable` – Jff Jan 25 '21 at 08:34
  • If you can't test results of thread execution externally, then why bother with multithreading? Just test whatever in this thread happens in separate test, instead of separate thread. PS: If you need to test inter-thread synchronization, then write correct tests. – sklott Jan 25 '21 at 08:36
  • 2
    The function `TEST(test, test1)` will likely return long before the code in the thread has executed. `testObj` will be destroyed and all the variables it owns with it, makeing the code in the thread using a dangling `this` pointer so, undefined behavior. – Ted Lyngmo Jan 25 '21 at 08:41

2 Answers2

0

How do you know none of those lines are being executed? I would suggest making a test program, compiling with -g and -Wall, then using gdb to make sure a thread is actually being created. If it is, step through the code. It could be that the compiler gets rid of code it thinks is doing nothing.

  • i checked the code coverage using lcov, that's how i found which all lines are executed. i'm using cmake to build the code. – Jff Jan 25 '21 at 08:38
0

This is how I would test it (I modified the code a little, but in principle it is the same thing. All the globals shall be moved to MyClass most probably):

#include <atomic>
#include <condition_variable>
#include <map>
#include <mutex>
#include <thread>

std::mutex time_to_live_mutex_;
std::map<std::string, int64_t> myList;
std::map<std::string, bool> cond_var_status_;
std::map<std::string, std::string> incoming_message_master_;
std::condition_variable cond_var_;
std::atomic_bool something_happened{false};


void foo(const std::string& argument1, const std::string& argument2) {
    std::thread([argument1, argument2]() {
        std::unique_lock<std::mutex> time_to_live_lock_(time_to_live_mutex_);
        int64_t time_stamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        int64_t time_to_live = myList[argument1] - (time_stamp % myList[argument1]); 
        cond_var_.wait_for(time_to_live_lock_, std::chrono::milliseconds(time_to_live), [argument1] { return cond_var_status_[argument1]; });
        if (incoming_message_master_.find(argument1) != incoming_message_master_.end()) {
            //something
            something_happened = true;
        }
    }).detach();
}

TEST(TestFoo, testing_foo_thread) {
  int64_t timeout = 10000;
  myList["bar"] = timeout; // so that cond variable waits long time
  cond_var_status_["bar"] = false; // so that cond variable returns false at first try
  incoming_message_master_["bar"] = std::string("42"); // so that something can happen
  foo("bar", "fiz");

  ASSERT_FALSE(something_happened); // cond variable is still waiting at this point

  {
    std::unique_lock<std::mutex> time_to_live_lock_(time_to_live_mutex_);
    cond_var_status_["bar"] = true; // so that cond variable returns eventually
  }

  while(!something_happened && timeout > 0) {
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    timeout -= 10;
  }

  ASSERT_TRUE(something_happened); // cond variable is not waiting, should have returned
}
Quarra
  • 2,527
  • 1
  • 19
  • 27