Boost Unit Test Framework dependencies execution order

978 views Asked by At

I am trying to set dependencies in the Boost Unit Testing Framework. I have found this thread tbat has an example of how to use the test_unit::depends_on() method. So far so good, I can write some magickery around that to smooth it out. However, the UTF doesn't honor test dependencies during execution.

Scenario: A BOOST_AUTO_TEST_CASE A is declared before another (B), and A depends_on() B Excpected (desired) outcome: The framework detects the dependency and runs B first, and then A if B succeeded. Actual Outcome: A is skipped because B, which has not run yet, has "failed" (i.e. no/false result yet).

Now, my idea was to do a topological sort on the test cases / suites and then run them in the sorted order. For that, I created a test_tree_visitor to walk the suites and determine the order of the m_members test_suite member.

However, m_members is protected and not accessible via methods. Since I can't change the headers, (would make upgrading to a newer version more difficult, etc, etc), and the BOOST_* macros "hardcode" the class as test_suite, I was thinking about the following hackery:

class member_accessible_test_suite : public test_suite
{
public:
    const std::vector<test_unit_id> *get_members() const { return &m_members; }
};

class dependency_order_visitor : public test_tree_visitor
{
public:
    virtual void visit( test_case const& tu)
    {}

    virtual bool test_suite_start( test_suite const& tu)
    {
        const member_accessible_test_suite *psuite(reinterpret_cast<const member_accessible_test_suite*>(&tu));
        const std::vector<test_unit_id> *pmembers(psuite->get_members());
        /* do something with pmembers */
        return true;
    }

    virtual void test_suite_finish( test_suite const& tu)
    {}

};

See a watered down version on Coliru

So now to my questions:

  1. The boost libraries are generally well designed - am I making a fundamental mistake due to a misunderstanding about unit testing design by requiring this feature?

  2. Since member_accessible_test_suite has no data and adds only functions, is the reinterpret_cast() safe or the fast lane into UB land? Either way, I am worried to use such a ghastly hack in production.

  3. Is there a better way, and if so, at which point did this turn into an XY problem?

1

There are 1 answers

0
namezero On BEST ANSWER

The following way seems the most proper when dealing with base class members that are not exposed via any mechanism (but must be accessed for some close functionality, and the base class must not be modified):

Link: Access to private member variables

The reasoning behind this idea as to why this works can be found in the 14.7.2p8 of the standard:

The usual access checking rules do not apply to names used to specify explicit instantiations. In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible.]

I took the liberty to outsource this into two macros that may come in handy again one day.

As with all of these workarounds - use wisely!

/* The ROB_PRIVATE_MEMBER_INST() macro should be used for explicit instantiation of the template at the appropriate source/compilation unit
   The ROB_PRIVATE_MEMBER_ACCESS() macro should be used for access to the variable where required
*/
#define ROB_PRIVATE_MEMBER_INST(CLASS, TYPE, MEMBER)    \
template<typename T>                                    \
struct CLASS##_##MEMBER##_rob_tag {                     \
  typedef T CLASS::*type;                               \
  friend type get(CLASS##_##MEMBER##_rob_tag);          \
};                                                      \
template<typename Tag, typename Tag::type M>            \
struct CLASS##_##MEMBER##_rob_private                   \
{                                                       \
    friend typename Tag::type get(Tag) { return M; }    \
};                                                      \
template struct CLASS##_##MEMBER##_rob_private<         \
CLASS##_##MEMBER##_rob_tag<TYPE> , &CLASS::MEMBER>;     \
/**/

#define ROB_PRIVATE_MEMBER_ACCESS(CLASS, INSTANCE, TYPE, MEMBER) \
    (INSTANCE.*get(CLASS##_##MEMBER##_rob_tag<TYPE>()))          \
/**/