BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bsls_protocoltest

Macros

#define BSLS_PROTOCOLTEST_ASSERT(test, methodCall)
 
#define BSLS_PROTOCOLTEST_RV_ASSERT(test, methodCall, returnValue)
 
#define bsls_ProtocolTest   bsls::ProtocolTest
 This alias is defined for backward compatibility.
 
#define bsls_ProtocolTestImp   bsls::ProtocolTestImp
 This alias is defined for backward compatibility.
 

Detailed Description

Outline

Purpose

Provide classes and macros for testing abstract protocols.

Classes

Macros

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):

There are two main exceptions to the above requirements:

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:

Additionally some coding guidelines related to protocols are also not verified:

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(); }
};
Definition bsls_protocoltest.h:688
ProtocolTest_MethodReturnType markDone() const
Definition bsls_protocoltest.h:1003

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) puts("\nPROTOCOL TEST"
"\n=============");

Then, we create an object of type bsls::ProtocolTest<ProtocolClassTestImp>, testObj:

if (verbose) puts("\n\tCreate a test object.");
Definition bsls_protocoltest.h:781

Now we use the testObj to test some general concerns about the protocol class.

if (verbose) puts("\tVerify that the protocol is abstract.");
ASSERT(testObj.testAbstract());
if (verbose) puts("\tVerify that there are no data members.");
ASSERT(testObj.testNoDataMembers());
if (verbose) puts("\tVerify that the destructor is virtual.");
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) puts("\tVerify that methods are public and virtual.");
BSLS_PROTOCOLTEST_ASSERT(testObj, foo(77));
BSLS_PROTOCOLTEST_ASSERT(testObj, bar("", ""));
#define BSLS_PROTOCOLTEST_ASSERT(test, methodCall)
Definition bsls_protocoltest.h:856

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).

Example 2: Testing a Method Overloaded on constness

Suppose we have a protocol that represent a sequence of integers. Such a protocol will have an overloaded at() method of both the const and the "mutable" variation. In verification of such methods we need to ensure that we verify both overloads of the virtual function.

First let's define the interesting parts of our imaginary sequence, the overloaded at() methods, and a virtual destructor to avoid warnings:

struct IntSeqExample {
// CREATORS
virtual ~IntSeqExample();
// MANIPULATORS
virtual int& at(bsl::size_t index) = 0;
// ACCESSORS
virtual int at(bsl::size_t index) const = 0;
};
IntSeqExample::~IntSeqExample()
{
}

Next, we define the test implementation as usual:

struct IntSeqExampleTestImp : bsls::ProtocolTestImp<IntSeqExample> {
static int s_int;
int& at(size_t) { s_int = 4; markDone(); return s_int; }
int at(size_t) const { s_int = 2; return markDone(); }
};
int IntSeqExampleTestImp::s_int = 0;

Note the use of a dummy variable to return a reference. We also use that variable, by giving it different values in the two overloads, to demonstrate that we have called the overload we have intended.

Then, we test the non-const overload as usual:

Now, we can verify that we have indeed tested the non-const overload:

assert(4 == IntSeqExampleTestImp::s_int);

Finally, we test at(size_t) const and also verify that we indeed called the intended overload. Notice that we "force" the const variant of the method to be picked by specifying a const Implementation type argument to bsls::ProtocolTest:

BSLS_PROTOCOLTEST_ASSERT(test_OBJ, at(0));
assert(2 == IntSeqExampleTestImp::s_int);

Note that the assertion that verifies that the intended overload was called is not strictly necessary, it is included for demonstration purposes.

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.

Macro Definition Documentation

◆ bsls_ProtocolTest

#define bsls_ProtocolTest   bsls::ProtocolTest

◆ BSLS_PROTOCOLTEST_ASSERT

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

◆ BSLS_PROTOCOLTEST_RV_ASSERT

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

◆ bsls_ProtocolTestImp

#define bsls_ProtocolTestImp   bsls::ProtocolTestImp