John Dibling
John Dibling

Reputation: 101484

Automatically generate unit test for all classes derived from a particular base class

G++4.8.2, gtest.

We are developing a code library (used internally) of classes which derive from a common base class, BasicGizmo.

Today each class derived from BasicGizmo is required to have an enum member named Size, which has the expected size of the class. For example:

#pragma (pack 1)
class SpecialGizmo
:
  BasicGizmo
{
public:
  enum {Size = 4};
  uint32_t mSomethingInteresting;
};

class SuperGizmo
:
  BasicGizmo
{
public:
  enum {Size=8};
  uint64_t mKewlData;
};

Elsewhere we are using gtest to construct unit tests to ensure that the actual sizeof these classes is what we are expecting them to be according to the Size enumeration:

CHECK_EQUAL (sizeof (SpecialGizmo), SpecialGizmo::Size); CHECK_EQUAL (sizeof (SuperGizmo), SuperGizmo::Size);

This all works as expected, except that the requirement that each of these derived classes both has a Size enum and that there's a unit test to check it is only enforced through code reviews. I would rather have the compiler catch omissions before the code is ever even committed. To do so, I would need to have a way to automatically generate the unit tests for every class that is derived from BasicGizmo.

Is any such mechanism possible?

Upvotes: 0

Views: 718

Answers (2)

Galik
Galik

Reputation: 48635

Your scheme could break on different platforms or different compiler settings.

If I was going to do something like this I would consider generating the test code for a given platform. You could add all the class names to a file (a std::stringstream in my example) and generate a testing program that outputs a reference information file on the size of each class/struct.

I would also consider engineering a way to include the platform and compiler settings at the beginning of the output reference information file.

If you make an official change to a class then you need to regenerate the test program to update the reference information file.

#include <string>
#include <iostream>

// These could be listed in a file
// opened with std::ifstream
std::istringstream iss
{
    "ClassA\n"
    "ClassB\n"
    "ClassC\n"
    "ClassD\n"
    "ClassE\n"
};

int main()
{
    // output an #include containing all the
    // headers you want to test
    std::cout << "#include \"all-classes.h\"" << '\n';

    // needed for generated code
    std::cout << "#include <iostream>\"" << '\n';

    // output testing code
    std::cout << "\nint main()\n{" << '\n';

    std::string class_name;
    while(std::getline(iss, class_name))
    {
        std::cout << "\tstd::cout << \"" + class_name + ": \" <<  sizeof(\"" + class_name + "\") << '\\n';" << '\n';
    }

    std::cout << "}" << '\n';
}

Output:

#include "all-classes.h"
#include <iostream>"

int main()
{
    std::cout << "ClassA: " <<  sizeof("ClassA") << '\n';
    std::cout << "ClassB: " <<  sizeof("ClassB") << '\n';
    std::cout << "ClassC: " <<  sizeof("ClassC") << '\n';
    std::cout << "ClassD: " <<  sizeof("ClassD") << '\n';
    std::cout << "ClassE: " <<  sizeof("ClassE") << '\n';
}

Then compile the output and you have your test program.

Upvotes: 1

a486408
a486408

Reputation: 78

It would be better to use static_assert:

template<typename tp_DerivedFromGizmo> struct
t_SizeEnumMemberChecker
{
    // you can also check that tp_DerivedFromGizmo really derives from BasicGizmo
    static const bool result = (sizeof(tp_DerivedFromGizmo) == tp_DerivedFromGizmo::Size);
};

static_assert(t_SizeEnumMemberChecker<SpecialGizmo>::result, "");

Upvotes: 1

Related Questions