0

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?

Community
  • 1
  • 1
namezero
  • 1,951
  • 3
  • 19
  • 35
  • 1
    I have nothing to say about this boost library, but the cast is ok. See for instance [Type aliasing section at cppreference](http://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing): `the resulting pointer or reference may be accessed if ... T2 is the (possibly cv-qualified) dynamic type of the object ...`. And you can cast directly to reference - `reinterpret_cast(tu)` – dewaffled Nov 29 '14 at 00:46
  • Thanks for the link. I think "T2 is a (possibly cv-qualified) base class of the dynamic type of the object" would apply, as the pointer is actually a test_suite, not a member_accessible_test_suite. I just wanted to know from the standard whizzes if the compiler is allowed to do awkward things, like realign or store hidden/internal data members with the derived class. – namezero Nov 29 '14 at 09:09
  • What you are describing seems rather to be of the level of a bug (`boost.test` is doing the topological sort and everything). Would you please post the code? I believe this can be easily fixed instead of implementing what is already there. – Raffi Oct 09 '16 at 22:20

1 Answers1

-1

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>()))          \
/**/
namezero
  • 1,951
  • 3
  • 19
  • 35