5

With GTEST_SKIP, we can (conditionally) skip a test. The way it is implemented is as follows:

#define GTEST_SKIP_(message) \
  return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip)

If we are in the main test body, all is fine.

How can I skip a test from deep within my code base? This does not work:

int skip_this(int x) {
  if (x == 3) GTEST_SKIP();
  return x;
}

TEST(X, Y) {
  skip_this(2);
  skip_this(3);
  throw std::runtime_error("Did not skip");
}

Error:

/Users/markus/Projekte/Opossum/Git/src/test/operators/aggregate_test.cpp:183:15: error: cannot initialize return object of type 'int' with an rvalue of type 'void'
  if (x == 3) GTEST_SKIP();
              ^~~~~~~~~~~~

In other words: How can I modify skip_this so that the test passes or is marked as "skipped"?

mrks
  • 8,033
  • 1
  • 33
  • 62

2 Answers2

3

This is not really nice, but works for my purpose. The idea is to unwind the stack, search for the googletest method that called the test and reset the instruction pointer to that stack frame.

Note that the stack is not properly destroyed, meaning that, e.g., resources can leak.

#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <cxxabi.h>
#include <iostream>

#include <gtest/gtest.h>

void skip() {
  unw_cursor_t cursor;
  unw_context_t context;

  unw_getcontext(&context);
  unw_init_local(&cursor, &context);

  while (unw_step(&cursor)) {
    unw_word_t off;

    char symbol[256] = {"<unknown>"};
    char* name = symbol;

    if (!unw_get_proc_name(&cursor, symbol, sizeof(symbol), &off)) {
      int status;
      if ((name = abi::__cxa_demangle(symbol, nullptr, nullptr, &status)) == nullptr) name = symbol;
    }

    if (std::string(name) == "testing::Test::Run()") {
      ::testing::internal::AssertHelper(::testing::TestPartResult::kSkip, __FILE__, __LINE__, "skipped") = ::testing::Message();
      unw_resume(&cursor);
    }

    if (name != symbol) free(name);
  }

  throw std::runtime_error("Did not find test method on the stack, could not skip test");
}

Example test (can be pasted below the code above):

int foo(int x) {
  if (x == 3) skip();
  return 11;
}

TEST(SkipTest, ShouldNotBeSkipped) {
  foo(2);
  EXPECT_TRUE(false);
}

TEST(SkipTest, ShouldBeSkipped) {
  foo(3);
  EXPECT_TRUE(false);
}

TEST(SkipTest, TestExecutionContinues) {
  EXPECT_FALSE(false);
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Output:

[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from SkipTest
[ RUN      ] SkipTest.ShouldNotBeSkipped
skip_test.cpp:44: Failure
Value of: false
  Actual: false
Expected: true
[  FAILED  ] SkipTest.ShouldNotBeSkipped (0 ms)
[ RUN      ] SkipTest.ShouldBeSkipped
[  SKIPPED ] SkipTest.ShouldBeSkipped (0 ms)
[ RUN      ] SkipTest.TestExecutionContinues
[       OK ] SkipTest.TestExecutionContinues (0 ms)
[----------] 3 tests from SkipTest (0 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 1 test.
[  SKIPPED ] 1 test, listed below:
[  SKIPPED ] SkipTest.ShouldBeSkipped
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SkipTest.ShouldNotBeSkipped

 1 FAILED TEST
mrks
  • 8,033
  • 1
  • 33
  • 62
1

You cant use GTEST_SKIP in the inner function. It will not take effect. I can only offer this solution or something similar:

#define SKIPCODE -1

int skip_this(int x)
{
   if (x == 3)
      return SKIPCODE;
   else return x;
}

TEST(Test)
{
   int n = 3;

   if (skip_this(n) == SKIPCODE)
      GTEST_SKIP();
   ASSERT_TRUE(false);
}

int main(int argc, char** argv)
{
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
}
Dmytro Dadyka
  • 2,208
  • 5
  • 18
  • 31
  • 2
    Can you demonstrate it working from an ordinary function, as well as from a `TEST()`? The error in the question seemed to be more due to the embedded `return` without a value. – Toby Speight Dec 13 '18 at 17:29
  • 1
    Yepp. You are demonstrating the intended/documented use of `GTEST_SKIP`. I would be interested in a way to skip the test above from within `skip_this`. – mrks Dec 13 '18 at 17:35
  • You are right, I somehow got confused with the question – Dmytro Dadyka Dec 13 '18 at 17:44