// bsls_protocoltest.h                                                -*-C++-*-
#ifndef INCLUDED_BSLS_PROTOCOLTEST
#define INCLUDED_BSLS_PROTOCOLTEST

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide classes and macros for testing abstract protocols.
//
//@CLASSES:
//  bsls::ProtocolTestImp: provides a framework for testing protocol classes
//  bsls::ProtocolTest: provides tests for protocol class concerns
//
//@MACROS:
//  BSLS_PROTOCOLTEST_ASSERT: macro for testing protocol methods
//
//@DESCRIPTION: This component provides classes and macros that simplify the
// creation of test drivers for protocol (i.e., pure abstract interface)
// classes.
//
// The purpose of a test driver for a protocol class is to verify concerns for
// that protocol's definition.  Although each protocol is different and
// requires its own test driver, there is a common set of concerns that apply
// to all protocol classes.  This component allows us to verify those concerns
// in a generic manner.
//
// Each protocol class has to satisfy the following set of requirements
// (concerns):
//: o The protocol is abstract: no objects of it can be created.
//: o The protocol has no data members.
//: o The protocol has a virtual destructor.
//: o All methods of the protocol are pure virtual.
//: o All methods of the protocol are publicly accessible.
//
// This protocol test component is intended to verify conformance to these
// requirements; however, it is not possible to verify all protocol
// requirements fully within the framework of the C++ language.  The following
// aspects of the above requirements are not verified by this component:
//: o Non-creator methods of the protocol are *pure* virtual.
//: o There are no methods in the protocol other than the ones being tested.
//
// Additionally some coding guidelines related to protocols are also not
// verified:
//: o The destructor is not pure virtual.
//: o The destructor is not implemented inline.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Testing a Protocol Class
///- - - - - - - - - - - - - - - - - -
// This example demonstrates how to test a protocol class, 'ProtocolClass',
// using this protocol test component.  Our 'ProtocolClass' provides two of
// pure virtual methods ('foo' and 'bar'), along with a virtual destructor:
//..
//  struct ProtocolClass {
//      virtual ~ProtocolClass();
//      virtual const char *bar(char const *, char const *) = 0;
//      virtual int foo(int) const = 0;
//  };
//
//  ProtocolClass::~ProtocolClass()
//  {
//  }
//..
// First, we define a test class derived from this protocol, and implement its
// virtual methods.  Rather than deriving the test class from 'ProtocolClass'
// directly, the test class is derived from
// 'bsls::ProtocolTestImp<ProtocolClass>' (which, in turn, is derived
// automatically from 'ProtocolClass').  This special base class implements
// boilerplate code and provides useful functionality for testing of protocols.
//..
//  // ========================================================================
//  //                  GLOBAL CLASSES/TYPEDEFS FOR TESTING
//  // ------------------------------------------------------------------------
//
//  struct ProtocolClassTestImp : bsls::ProtocolTestImp<ProtocolClass> {
//      const char *bar(char const *, char const *) { return markDone(); }
//      int foo(int) const                          { return markDone(); }
//  };
//..
// Notice that in 'ProtocolClassTestImp' we must provide an implementation for
// every protocol method except for the destructor.  The implementation of each
// method calls the (protected) 'markDone' which is provided by the base class
// for the purpose of verifying that the method from which it's called is
// declared as virtual in the protocol class.
//
// Then, in our protocol test case we describe the concerns we have for the
// protocol class and the plan to test those concerns:
//..
//  // ------------------------------------------------------------------------
//  // PROTOCOL TEST:
//  //   Ensure this class is a properly defined protocol.
//  //
//  // Concerns:
//  //: 1 The protocol is abstract: no objects of it can be created.
//  //:
//  //: 2 The protocol has no data members.
//  //:
//  //: 3 The protocol has a virtual destructor.
//  //:
//  //: 4 All methods of the protocol are pure virtual.
//  //:
//  //: 5 All methods of the protocol are publicly accessible.
//  //
//  // Plan:
//  //: 1 Define a concrete derived implementation, 'ProtocolClassTestImp',
//  //:   of the protocol.
//  //:
//  //: 2 Create an object of the 'bsls::ProtocolTest' class template
//  //:   parameterized by 'ProtocolClassTestImp', and use it to verify
//  //:   that:
//  //:
//  //:   1 The protocol is abstract. (C-1)
//  //:
//  //:   2 The protocol has no data members. (C-2)
//  //:
//  //:   3 The protocol has a virtual destructor. (C-3)
//  //:
//  //: 3 Use the 'BSLS_PROTOCOLTEST_ASSERT' macro to verify that
//  //:   non-creator methods of the protocol are:
//  //:
//  //:   1 virtual, (C-4)
//  //:
//  //:   2 publicly accessible. (C-5)
//  //
//  // Testing:
//  //   virtual ~ProtocolClass();
//  //   virtual const char *bar(char const *, char const *) = 0;
//  //   virtual int foo(int) const = 0;
//  // ------------------------------------------------------------------------
//..
// Next we print the banner for this test case:
//..
//  if (verbose) printf("\nPROTOCOL TEST"
//                      "\n=============\n");
//..
// Then, we create an object of type
// 'bsls::ProtocolTest<ProtocolClassTestImp>', 'testObj':
//..
//  if (verbose) printf("\nCreate a test object.\n");
//
//  bsls::ProtocolTest<ProtocolClassTestImp> testObj(veryVerbose);
//..
// Now we use the 'testObj' to test some general concerns about the protocol
// class.
//..
//  if (verbose) printf("\nVerify that the protocol is abstract.\n");
//
//  ASSERT(testObj.testAbstract());
//
//  if (verbose) printf("\nVerify that there are no data members.\n");
//
//  ASSERT(testObj.testNoDataMembers());
//
//  if (verbose) printf("\nVerify that the destructor is virtual.\n");
//
//  ASSERT(testObj.testVirtualDestructor());
//..
// Finally we use the 'testObj' to test concerns for each individual method of
// the protocol class.  To test a protocol method we need to call it from
// inside the 'BSLS_PROTOCOLTEST_ASSERT' macro, and also pass the 'testObj':
//..
//  if (verbose) printf("\nVerify that methods are public and virtual.\n");
//
//  BSLS_PROTOCOLTEST_ASSERT(testObj, foo(77));
//  BSLS_PROTOCOLTEST_ASSERT(testObj, bar("", ""));
//..
// These steps conclude the protocol testing.  If there are any failures, they
// will be reported via standard test driver assertions (i.e., the standard
// 'ASSERT' macro).
//
///Implementation Note
///- - - - - - - - - -
// This component has a number of private meta-functions on some platforms,
// e.g., 'ProtocolTest_EnableIf', 'ProtocolTest_IsClass', and
// 'ProtocolTest_IsAbstract'.  These mimic, to a limited extent, standard
// library meta-functions in the namespace 'std' that are not available on all
// platforms.  For general use, see the {'bslmf'} package and the {'bsl'}
// namespace for portable implementations of some of these meta-functions.

#include <bsls_compilerfeatures.h>
#include <bsls_libraryfeatures.h>
#include <bsls_platform.h>

#ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER
#include <type_traits>
#endif

#include <cstddef>
#include <cstdio>

namespace BloombergLP {
namespace bsls {

                        // =============================
                        // class ProtocolTest_IsAbstract
                        // =============================

template <class T>
struct ProtocolTest_IsAbstract;
    // This class template is a compile-time meta-function, parameterized with
    // type 'T', the output of which is 'VALUE', which will be 'true' if 'T' is
    // abstract and 'false' otherwise.  On some platforms, the 'IsAbstract'
    // test makes use of the fact that a type 'an array of objects of an
    // abstract type' (e.g., 'T[1]') cannot exist.  Note that it is only an
    // approximation, because this is also true for an incomplete type.  But,
    // this approximation is good enough for the purpose of testing protocol
    // classes.  On certain other platforms, the 'IsAbstract' test will make
    // use of the fact that abstract types cannot be returned.  This
    // approximation also has issues, noted below, but is also good enough for
    // the purpose of testing protocol classes.

#ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER

template <class T>
struct ProtocolTest_IsAbstract {
    enum { VALUE = std::is_abstract<T>::value };
};

#elif defined(BSLS_PLATFORM_CMP_GNU) && BSLS_PLATFORM_CMP_VERSION >= 110000

///Implementation Note
///-------------------
// GCC 11 and later adhere to the core language changes resulting from the
// paper P0929, which was approved as a defect report and applied to C++03 and
// later.  One effect of this paper is that it became well-formed to name the
// type 'T[1]' where 'T' is an abstract class type.  As a result, platforms
// that apply P0929 require a different implementation of an abstractness test
// in C++03 mode.

template <class T>
struct ProtocolTest_VoidType {
    // This component-private, meta-function 'struct' template provides a
    // single-parameter type trait that behaves like the 'bslmf::VoidType'
    // meta-function for use in the implementation of the
    // 'ProtocolTest_IsAbstract' meta-function when compiling in C++03 mode on
    // compilers that apply P0929 to that mode.  Note that this component is in
    // the 'bsls' package, which is levelized below 'bslmf', and so cannot
    // depend on 'bslmf::VoidType'.

    typedef void Type;
};

template <class VOID_TYPE, class T>
struct ProtocolTest_IsClassTypeImp {
    // This component-private, meta-function (primary) 'struct' template
    // provides part of the implementation of a type trait that behaves like
    // 'std::is_class' for use in the implementation of the
    // 'ProtocolTest_IsAbstract' meta-function when compiling in C++03 mode on
    // compilers that apply P0929 to that mode.  Note that in this mode,
    // 'std::is_class' is not available.

    enum { VALUE = false };
};

template <class T>
struct ProtocolTest_IsClassTypeImp<
    typename ProtocolTest_VoidType<int T::*>::Type,
    T> {
    // This component-private, meta-function 'struct' template (partial
    // specialization) provides part of the implementation of a type trait that
    // behaves like 'std::is_class' for use in the implementation of the
    // 'ProtocolTest_IsAbstract' meta-function when compiling in C++03 mode on
    // compilers that apply P0929 to that mode.  Note that in this mode,
    // 'std::is_class' is not available.

    enum { VALUE = true };
};

template <class T>
struct ProtocolTest_IsClassType {
    // This component-private, meta-function 'struct' template provides the
    // implementation of a type trait that behaves like 'std::is_class' for use
    // in the implementation of the 'ProtocolTest_IsAbstract' meta-function
    // when compiling in C++03 mode on compilers that apply P0929 to that mode.
    // Note that in this mode, 'std::is_class' is not available.

    enum {
        VALUE = ProtocolTest_IsClassTypeImp<void, T>::VALUE
    };
};

template <bool CONDITION, class T = void>
struct ProtocolTest_EnableIf {
    // This component-private, meta-function (primary) 'struct' template
    // provides part of the implementation of a type trait that behaves like
    // 'std::enable_if' for use in the implementation of the
    // 'ProtocolTest_IsAbstract' meta-function when compiling in C++03 mode on
    // compilers that apply P0929 to that mode.  Note that in this mode,
    // 'std::enable_if' is not available.
};

template <class T>
struct ProtocolTest_EnableIf<true, T> {
    // This component-private, meta-function 'struct' template (partial
    // specialization) provides part of the implementation of a type trait that
    // behaves like 'std::enable_if' for use in the implementation of the
    // 'ProtocolTest_IsAbstract' meta-function when compiling in C++03 mode on
    // compilers that apply P0929 to that mode.  Note that in this mode,
    // 'std::enable_if' is not available.

    typedef T Type;
};

struct ProtocolTest_NoType {
    // this component-private 'struct' provides a type having a size that is
    // guaranteed to be different than the size of 'ProtocolTest_YesType', and
    // is used in the implementation of the 'ProtocolTest_IsAbstract'
    // meta-function when compiling in C++03 mode on compilers that apply P0929
    // to that mode.

    char d_padding;
};

struct ProtocolTest_YesType {
    // this component-private 'struct' provides a type having a size that is
    // guaranteed to be different than the size of 'ProtocolTest_NoType', and
    // is used in the implementation of the 'ProtocolTest_IsAbstract'
    // meta-function when compiling in C++03 mode on compilers that apply P0929
    // to that mode.

    char d_padding[17];
};

struct ProtocolTest_IsReturnableImpUtil {
    // This component-private 'struct' provides a namespace for a 'test'
    // overload set used to determine if a specified type can be returned from
    // a function-call expression or not.  This 'struct' is used in the
    // implementation of the 'ProtocolTest_IsAbstract' meta-function when
    // compiling in C++03 mode on compilers that apply P0929 to that mode.

  private:
    // PRIVATE CLASS METHODS
    template <class T>
    static T returnThe();
        // Return a prvalue of the specified 'T' type.  Note that this function
        // is declared but not defined.  It is similar in nature to
        // 'std::declval', with the important distinction that return type of
        // 'std::declval' is a reference type, and the return type of this
        // function is not (necessarily) a reference type.

  public:
    // CLASS METHODS
    template <class T>
    static ProtocolTest_NoType test(...);
    template <class T>
    static ProtocolTest_YesType
    test(typename ProtocolTest_EnableIf<static_cast<bool>(
             sizeof(static_cast<void>(returnThe<T>()), 0))>::Type *);
        // Return a 'ProtocolTest_YesType' prvalue if the specified 'T' type
        // can be returned from a function-call expression, and return a
        // 'ProtocolTest_NoType' prvalue otherwise.  The behavior is undefined
        // unless this function is invoked with a single argument that is
        // convertible to a 'void *'.  Note that this function is declared but
        // not defined.
};

template <class T>
struct ProtocolTest_IsReturnable {
    // This component-private, meta-function 'struct' template provides a
    // compile-time constant 'VALUE' class member with the value 'true' if the
    // supplied 'T' type can be returned from a function-call expression, and
    // provides a 'VALUE' class member with the value 'false' otherwise.  This
    // meta-function is used in the implementation of the
    // 'ProtocolTest_IsAbstract' meta-function when compiling in C++03 mode on
    // compilers that apply P0929 to that mode.

    enum {
        VALUE = sizeof(ProtocolTest_YesType) ==
                sizeof(ProtocolTest_IsReturnableImpUtil::test<T>(0))
    };
};


template <class T>
struct ProtocolTest_IsAbstract {
    // This component-private, meta-function 'struct' template provides a
    // compile-time constant 'VALUE' class member with the value 'true' if the
    // supplied 'T' type is an abstract class type (or, and this is a defect,
    // if 'T' is a class type with a private destructor), and provides a
    // 'VALUE' class member with the value 'false' otherwise.  This
    // meta-function matches the behavior 'std::is_abstract' would have if it
    // were available except for non-abstract types with a private destructor,
    // and is for use when compiling in C++03 mode on compilers that apply
    // P0929 to that mode.

    enum {
        VALUE = ProtocolTest_IsClassType<T>::VALUE &&
                !ProtocolTest_IsReturnable<T>::VALUE
    };
};

#else

template <class T>
struct ProtocolTest_IsAbstract {
    // This component-private, meta-function 'struct' template provides a
    // compile-time constant 'VALUE' class member with the value 'true' if the
    // supplied 'T' type is an abstract class type, and provides a 'VALUE'
    // class member with the value 'false' otherwise.  This meta-function
    // matches the behavior 'std::is_abstract' would have if it were
    // available on C++03 platforms.

    typedef char                    YesType;
    typedef struct { char a[2]; }   NoType;

    template <class U>
    static NoType test(U (*)[1]);

    template <class U>
    static YesType test(...);

    enum { VALUE = sizeof(test<T>(0)) == sizeof(YesType) };
};

#endif

                     // ===================================
                     // class ProtocolTest_MethodReturnType
                     // ===================================

struct ProtocolTest_MethodReturnType {
    // This class is a proxy for a return type designed to simplify testing
    // implementations of protocol methods.
    // 'ProtocolTest_MethodReturnType' can be converted to any
    // non-reference type (i.e., the type can be either a value or pointer
    // type, but not a reference type).  When an object of this class is
    // returned from a test implementation of a protocol method, it is
    // implicitly converted to the return type of the protocol method.

    // ACCESSORS
    template <class T>
    operator T() const;
        // Return a temporary value of type 'T'.  The returned object is valid
        // but it does not have any meaningful value so it should not be used.
        // Type 'T' is required to be default-constructible.
};

                   // ======================================
                   // class ProtocolTest_MethodReturnRefType
                   // ======================================

struct ProtocolTest_MethodReturnRefType {
    // This class is a proxy for a return type designed to simplify testing
    // implementations of protocol methods.
    // 'ProtocolTest_MethodReturnRefType' can be converted to any
    // reference type.  When an object of this class is returned from a test
    // implementation of a protocol method, it is implicitly converted to
    // the return type of the protocol method.

    // ACCESSORS
    template <class T>
    operator T&() const;
        // Return a 'T&' reference to an invalid object.  The returned value
        // should not be used and should be immediately discarded.
};

                           // =======================
                           // class ProtocolTest_Dtor
                           // =======================

template <class BSLS_TESTIMP>
struct ProtocolTest_Dtor : BSLS_TESTIMP {
    // This class template is a helper protocol-test implementation class that
    // tests that a protocol destructor is declared 'virtual', which it does by
    // calling the 'markDone' function from its destructor.  The destructor
    // will be executed if the protocol's destructor is declared 'virtual' and
    // not executed otherwise.  Note that the 'BSLS_TESTIMP' template parameter
    // is required to be a type derived from 'ProtocolTestImp' class.

    // CREATORS
    ~ProtocolTest_Dtor();
        // Destroy this object and call the 'markDone' method, indicating that
        // the base class's destructor was declared 'virtual'.
};

                          // =========================
                          // class ProtocolTest_Status
                          // =========================

class ProtocolTest_Status {
    // This class keeps track of the test status, which includes the status of
    // the last test and the number of failures across all tests.

  private:
    // DATA
    int  d_failures;   // number of test failures encountered so far
    bool d_last;       // result of the last test ('true' indicates success)

  public:
    // CREATORS
    ProtocolTest_Status();
        // Create an object of the 'ProtocolTest_Status' class with the
        // default state in which 'failures() == 0' and 'last() == true'.

    // MANIPULATORS
    void resetLast();
        // Reset the status of the last test to 'true'.

    void fail();
        // Record a test failure by increasing the number of 'failures' and
        // setting the status of the last test to 'false'.

    // ACCESSORS
    int failures() const;
        // Return the number of failures encountered during testing of a
        // protocol class, which is 0 if all tests succeeded or if no tests
        // ran.

    bool last() const;
        // Return 'true' if the last test completed successfully (or no test
        // has yet completed), and 'false' if it failed.
};

                         // ===========================
                         // class ProtocolTest_AsBigAsT
                         // ===========================

template <class T>
class ProtocolTest_AsBigAsT {
    // This auxiliary structure has a size no less than the size of (template
    // parameter) 'T'.

#if defined (BSLS_LIBRARYFEATURES_HAS_CPP11_MISCELLANEOUS_UTILITIES)
    // DATA
    std::max_align_t d_dummy[sizeof(T) / sizeof(std::max_align_t) + 1];
#else
    // PRIVATE TYPES
    union MaxAlignType {
        void               *d_v_p;
        unsigned long long  d_ull;
        long double         d_ul;
    };

    // DATA
    MaxAlignType d_dummy[sizeof(T) / sizeof(MaxAlignType) + 1];
#endif
};

                            // =====================
                            // class ProtocolTestImp
                            // =====================

template <class BSLS_PROTOCOL>
class ProtocolTestImp : public BSLS_PROTOCOL {
    // This mechanism class template is a base class for a test implementation
    // of a protocol class defined by the 'BSLS_PROTOCOL' template parameter.
    // Its purpose is to reduce the boilerplate test code required to verify
    // that derived virtual methods are called.  It provides 'markDone' member
    // functions one of which should be called from each method of the protocol
    // class test implementation to indicate that the virtual method is
    // correctly overridden.  It also overloads 'operator->' to serve as a
    // proxy to 'BSLS_PROTOCOL' and detect when 'BSLS_PROTOCOL' methods are
    // called.

  private:
    // DATA
    ProtocolTest_Status      *d_status;   // test status object for test
                                          // failure reporting

    bool                      d_entered;  // 'true' if this object entered a
                                          // protocol method call

    bool mutable              d_exited;   // 'true' if this object exited a
                                          // protocol method in the derived
                                          // class; mutable, so it can be set
                                          // from 'const' methods
  public:
    // TYPES
    typedef BSLS_PROTOCOL ProtocolType;

    // CREATORS
    ProtocolTestImp();
        // Create an object of the 'ProtocolTestImp' class.

    ~ProtocolTestImp();
        // Destroy this object and check the status of the test execution
        // (success or failure).  On test failure, report it to
        // 'ProtocolTest_Status'.

    // MANIPULATORS
    BSLS_PROTOCOL *operator->();
        // Dereference this object as if it were a pointer to 'BSLS_PROTOCOL'
        // in order to call a method on 'BSLS_PROTOCOL'.  Also mark this
        // object as 'entered' for the purpose of calling a protocol method.

    void markEnter();
        // Mark this object as entered for the purpose of calling a protocol
        // method.  The 'entered' property is tested in the destructor to
        // check for test failures (i.e., if 'entered == false' then the test
        // cannot fail since it never ran).  Note that 'markEnter' and
        // 'markDone' calls have to be paired for a protocol-method-call test
        // to succeed.

    void setTestStatus(ProtocolTest_Status *testStatus);
        // Connect this protocol test object with the specified 'testStatus'
        // object, which will be used for test failure reporting.

    // ACCESSORS
    ProtocolTest_MethodReturnType markDone() const;
        // Return a proxy object convertible to any value or pointer type.
        // Derived classed should call this method from their implementations
        // of protocol virtual methods to indicate that virtual methods were
        // overridden correctly.

    ProtocolTest_MethodReturnRefType markDoneRef() const;
        // Return a proxy object convertible to any reference type.  Derived
        // classed should call this method from their implementations of
        // protocol virtual methods to indicate that virtual methods were
        // overridden correctly.

    template <class T>
    T markDoneVal(const T& value) const;
        // Return the specified 'value'.  Derived classes should call this
        // method from their implementations of protocol virtual methods to
        // indicate that virtual methods were overridden correctly.
};

                             // ==================
                             // class ProtocolTest
                             // ==================

template <class BSLS_TESTIMP>
class ProtocolTest {
    // This mechanism class template provides the implementation of protocol
    // testing concerns via 'test*' methods (for non-method concerns), and via
    // 'operator->' (for method concerns).  The 'BSLS_TESTIMP' template
    // parameter is required to be a class derived from 'ProtocolTestImp'
    // that provides test implementations of all protocol methods.

  private:
    // TYPES
    typedef typename BSLS_TESTIMP::ProtocolType ProtocolType;

    // DATA
    ProtocolTest_Status d_status;
    bool                d_verbose;  // print trace messages if 'true'

  private:
    // PRIVATE MANIPULATORS
    void startTest();
        // Start a new test by resetting this object to the state before the
        // test.

    void trace(char const *message) const;
        // Print a trace 'message' if 'd_verbose' is 'true'.

  public:
    // CREATORS
    explicit
    ProtocolTest(bool verbose = false);
        // Construct a 'ProtocolTest' object.

    // MANIPULATORS
    BSLS_TESTIMP method(const char *methodDesc = "");
        // Return a 'BSLS_TESTIMP' object to perform testing of a specific
        // method which gets called via 'operator->()'.  Note that
        // 'BSLS_TESTIMP' is a proxy to the actual protocol class.

    bool testAbstract();
        // Return 'true' (i.e., the test passed) if the protocol class being
        // tested is abstract and return 'false' (i.e., the test failed)
        // otherwise.  Increase the count of 'failures' and set 'lastStatus' to
        // 'false' on failure.

    bool testNoDataMembers();
        // Return 'true' (i.e., the test passed) if the protocol class being
        // tested contains no data fields and return 'false' (i.e., the test
        // failed) otherwise.  Increase the count of 'failures' and set
        // 'lastStatus' to 'false' on failure.

    bool testVirtualDestructor();
        // Return 'true' (i.e., the test passed) if the protocol class being
        // tested has a virtual destructor and return 'false' (i.e., the test
        // failed) otherwise.  Increase the 'failures' count and set
        // 'lastStatus' to 'false' on failure.

    // ACCESSORS
    int failures() const;
        // Return the number of failures encountered during testing of a
        // protocol class.  The returned value is 0 if all tests succeeded, or
        // no tests ran.

    bool lastStatus() const;
        // Return 'true' if the last test completed successfully (or no test
        // has yes completed) and 'false' otherwise.
};

}  // close package namespace

                          // ========================
                          // BSLS_PROTOCOLTEST_ASSERT
                          // ========================

// This macro provides a test for method-related concerns of a protocol class.
// It ensures that a method is publicly accessible and declared 'virtual'.  It
// requires that a standard test driver 'ASSERT' macro is defined, which is
// used to assert the test completion status.

#define BSLS_PROTOCOLTEST_ASSERT(test, methodCall)                            \
    do {                                                                      \
        test.method(                                                          \
                "inside BSLS_PROTOCOLTEST_ASSERT("#methodCall")")->methodCall;\
        if (!test.lastStatus()) {                                             \
            ASSERT(0 && "Not a virtual method: "#methodCall);                 \
        }                                                                     \
    } while (0)

// ============================================================================
//                        INLINE FUNCTION DEFINITIONS
// ============================================================================

namespace bsls {

                     // -----------------------------------
                     // class ProtocolTest_MethodReturnType
                     // -----------------------------------

// ACCESSORS
template <class T>
inline
ProtocolTest_MethodReturnType::operator T() const
{
    return T();
}

                   // --------------------------------------
                   // class ProtocolTest_MethodReturnRefType
                   // --------------------------------------

// ACCESSORS
template <class T>
inline
ProtocolTest_MethodReturnRefType::operator T&() const
{
    static ProtocolTest_AsBigAsT<T> obj;
    return *reinterpret_cast<T *>(&obj);
}

                       // -----------------------
                       // class ProtocolTest_Dtor
                       // -----------------------

// CREATORS
template <class BSLS_TESTIMP>
inline
ProtocolTest_Dtor<BSLS_TESTIMP>::~ProtocolTest_Dtor()
{
    this->markDone();
}

                      // -------------------------
                      // class ProtocolTest_Status
                      // -------------------------

// CREATORS
inline
ProtocolTest_Status::ProtocolTest_Status()
: d_failures(0)
, d_last(true)
{
}

// MANIPULATORS
inline
void ProtocolTest_Status::resetLast()
{
    d_last = true;
}

inline
void ProtocolTest_Status::fail()
{
    ++d_failures;
    d_last = false;
}

// ACCESSORS
inline
int ProtocolTest_Status::failures() const
{
    return d_failures;
}

inline
bool ProtocolTest_Status::last() const
{
    return d_last;
}

                          // ---------------------
                          // class ProtocolTestImp
                          // ---------------------

// CREATORS
template <class BSLS_PROTOCOL>
inline
ProtocolTestImp<BSLS_PROTOCOL>::ProtocolTestImp()
: d_status(0)
, d_entered(false)
, d_exited(false)
{
}

template <class BSLS_PROTOCOL>
inline
ProtocolTestImp<BSLS_PROTOCOL>::~ProtocolTestImp()
{
    if (d_entered && !d_exited) {
        d_status->fail();
    }
}

// MANIPULATORS
template <class BSLS_PROTOCOL>
inline
typename ProtocolTestImp<BSLS_PROTOCOL>::ProtocolType *
ProtocolTestImp<BSLS_PROTOCOL>::operator->()
{
    markEnter();
    return static_cast<BSLS_PROTOCOL *>(this);
}

template <class BSLS_PROTOCOL>
inline
void
ProtocolTestImp<BSLS_PROTOCOL>::setTestStatus(ProtocolTest_Status *testStatus)
{
    d_status = testStatus;
}

template <class BSLS_PROTOCOL>
inline
void ProtocolTestImp<BSLS_PROTOCOL>::markEnter()
{
    d_entered = true;
}

// ACCESSORS
template <class BSLS_PROTOCOL>
inline
ProtocolTest_MethodReturnType
ProtocolTestImp<BSLS_PROTOCOL>::markDone() const
{
    d_exited = true;
    return ProtocolTest_MethodReturnType();
}

template <class BSLS_PROTOCOL>
inline
ProtocolTest_MethodReturnRefType
ProtocolTestImp<BSLS_PROTOCOL>::markDoneRef() const
{
    d_exited = true;
    return ProtocolTest_MethodReturnRefType();
}

template <class BSLS_PROTOCOL>
template <class T>
inline
T ProtocolTestImp<BSLS_PROTOCOL>::markDoneVal(const T& value) const
{
    d_exited = true;
    return value;
}

                       // ------------------
                       // class ProtocolTest
                       // ------------------

// PRIVATE MANIPULATORS
template <class BSLS_TESTIMP>
inline
void ProtocolTest<BSLS_TESTIMP>::startTest()
{
    d_status.resetLast();
}

template <class BSLS_TESTIMP>
inline
void ProtocolTest<BSLS_TESTIMP>::trace(char const *message) const
{
    if (d_verbose) {
        std::printf("\t%s\n", message);
    }
}

// CREATORS
template <class BSLS_TESTIMP>
inline
ProtocolTest<BSLS_TESTIMP>::ProtocolTest(bool verbose)
: d_verbose(verbose)
{
}

// MANIPULATORS
template <class BSLS_TESTIMP>
inline
BSLS_TESTIMP ProtocolTest<BSLS_TESTIMP>::method(const char *methodDesc)
{
    trace(methodDesc);
    startTest();

    BSLS_TESTIMP impl;
    impl.setTestStatus(&d_status);
    return impl;
}

template <class BSLS_TESTIMP>
inline
bool ProtocolTest<BSLS_TESTIMP>::testAbstract()
{
    trace("inside ProtocolTest::testAbstract()");
    startTest();

    if (!ProtocolTest_IsAbstract<ProtocolType>::VALUE) {
        d_status.fail();
    }

    return lastStatus();
}

template <class BSLS_TESTIMP>
inline
bool ProtocolTest<BSLS_TESTIMP>::testNoDataMembers()
{
    trace("inside ProtocolTest::testNoDataMembers()");
    struct EmptyProtocol
    {
        virtual ~EmptyProtocol() {}
    };

    startTest();

    if (sizeof(EmptyProtocol) != sizeof(ProtocolType)) {
        d_status.fail();
    }

    return lastStatus();
}

template <class BSLS_TESTIMP>
bool ProtocolTest<BSLS_TESTIMP>::testVirtualDestructor()
{
    trace("inside ProtocolTest::testVirtualDestructor");
    startTest();

    // Can't use an automatic buffer and the placement new for an object of
    // type ProtocolTest_Dtor<BSLS_TESTIMP> here, because bslma::Allocator
    // defines its own placement new, making it impossible to test
    // bslma::Allocator protocol this way.

    // Prepare a test
    ProtocolTest_Dtor<BSLS_TESTIMP> * obj =
                                    new ProtocolTest_Dtor<BSLS_TESTIMP>();
    BSLS_TESTIMP * base = obj;
    obj->setTestStatus(&d_status);

    // Run the test.
    obj->markEnter();
    delete base;

    // 'ProtocolTest_Dtor::~ProtocolTest_Dtor' will be called only if
    // the destructor was declared 'virtual' in the interface, but
    // 'BSLS_TESTIMP::~BSLS_TESTIMP' is always executed to check if the
    // derived destructor was called.

    return lastStatus();
}

// ACCESSORS
template <class BSLS_TESTIMP>
inline
int ProtocolTest<BSLS_TESTIMP>::failures() const
{
    return d_status.failures();
}

template <class BSLS_TESTIMP>
inline
bool ProtocolTest<BSLS_TESTIMP>::lastStatus() const
{
    return d_status.last();
}

}  // close package namespace

#ifndef BDE_OPENSOURCE_PUBLICATION  // BACKWARD_COMPATIBILITY
// ============================================================================
//                           BACKWARD COMPATIBILITY
// ============================================================================

#ifdef bsls_ProtocolTest
#undef bsls_ProtocolTest
#endif
#define bsls_ProtocolTest bsls::ProtocolTest
    // This alias is defined for backward compatibility.

#ifdef bsls_ProtocolTestImp
#undef bsls_ProtocolTestImp
#endif
#define bsls_ProtocolTestImp bsls::ProtocolTestImp
    // This alias is defined for backward compatibility.
#endif  // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------