Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to ASSERT_DOES_NOT_COMPILE with GTest?

Assume a template class where we assert at compile time that the integer template argument must be greater zero:

template<int N>
class A
{
public:

    A() {
        static_assert(N > 0, "N needs to be greater 0.");
    }

};

Is it possible to create a googletest unit test that compiles, but reports the error at runtime? For example:

TEST(TestA, ConstructionNotAllowedWithZero)
{
    ASSERT_DOES_NOT_COMPILE( 
        {
            A< 0 > a;
        }
    );
}
like image 702
Dimitri Schachmann Avatar asked Jul 24 '15 10:07

Dimitri Schachmann


People also ask

How do I skip a Gtest test?

If you have a broken test that you cannot fix right away, you can add the DISABLED_ prefix to its name. This will exclude it from execution. This is better than commenting out the code or using #if 0 , as disabled tests are still compiled (and thus won't rot).

Does Gtest run tests in parallel?

gtest-parallel is a script that executes Google Test binaries in parallel, providing good speedup for single-threaded tests (on multi-core machines) and tests that do not run at 100% CPU (on single- or multi-core machines).

Is Gtest header only?

Google Test is not header-only: there are libraries to build. So, as a Visual Studio user, you have essentially two options.

Can I use Gtest for C?

Google test, or gtest is an open source framework for unit testing C\C++ projects. It easily integrates with CMake, has a great assertion engine, and produces XML reports to be for display so that it can be integrated with common CI\CD frameworks.


1 Answers

There is a way, but sadly it's probably not the way you want.

My first thought was to try to get SFINAE to discount an overload by expanding an invalid lambda in an unevaluated context. Unhappily (in your case), this is specifically disallowed...

#define CODE { \
 utter garbage \
}
struct test
{
    template<class T>
    static std::false_type try_compile(...) { return{}; }
    template<class T>
    static auto try_compile(int)
    -> decltype([]() CODE, void(), std::true_type());
    { return {}; }
};
struct tag {};
using does_compile = decltype(test::try_compile<tag>(0));

output:

./maybe_compile.cpp:88:17: error: lambda expression in an unevaluated operand
    -> decltype([]() CODE, void(), std::true_type());

So it was back to the drawing board and a good old system call to call out to the compiler...

#include <iostream>
#include <string>
#include <cstdlib>
#include <fstream>
#include <sstream>

struct temp_file {
    temp_file()
    : filename(std::tmpnam(nullptr))
    {}

    ~temp_file() {
        std::remove(filename.c_str());
    }

    std::string filename;
};

bool compiles(const std::string code, std::ostream& reasons)
{
    using namespace std::string_literals;

    temp_file capture_file;
    temp_file cpp_file;

    std::ofstream of(cpp_file.filename);
    std::copy(std::begin(code), std::end(code), std::ostream_iterator<char>(of));
    of.flush();
    of.close();
    const auto cmd_line = "c++ -x c++ -o /dev/null "s + cpp_file.filename + " 2> " + capture_file.filename;
    auto val = system(cmd_line.c_str());

    std::ifstream ifs(capture_file.filename);
    reasons << ifs.rdbuf();
    ifs.close();

    return val == 0;
}

auto main() -> int
{
    std::stringstream reasons1;
    const auto code1 =
R"code(
    #include <iostream>
    int main() {
        return 0;
    }
)code";
    std::cout << "compiles: " << compiles(code1, reasons1) << std::endl;

    std::stringstream reasons2;
    const auto code2 =
R"code(
    #include <iostream>
    int main() {
        FOO!!!!XC@£$%^&*()VBNMYGHH
        return 0;
    }
)code";
    std::cout << "compiles: " << compiles(code2, reasons2) << std::endl;
    std::cout << "\nAnd here's why...\n";
    std::cout << reasons2.str() << std::endl;

    return 0;
}

which in my case gives the following example output:

compiles: 1
compiles: 0

And here's why...
/var/tmp/tmp.3.2dADZ7:4:9: error: use of undeclared identifier 'FOO'
        FOO!!!!XC@£$%^&*()VBNMYGHH
        ^
/var/tmp/tmp.3.2dADZ7:4:19: error: non-ASCII characters are not allowed outside of literals and identifiers
        FOO!!!!XC@£$%^&*()VBNMYGHH
                  ^
2 errors generated.

of course you can add all the necessary macros around the call to compiles() in order to GTESTify it. You will of course have to set command line options on the c-compiler invocation to set the correct paths and defines.

like image 200
Richard Hodges Avatar answered Sep 28 '22 22:09

Richard Hodges