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

Detailed Description

Outline

Purpose

Provide utilities to help with testing templates.

Classes

Macros

See also
bsltf_simpletesttype, bslstl_map

Description

When testing a container template having a type parameter, we need to ensure that the template supports its contractually specified categories of parameter types. The bsltf package provides a representative set of types intended for testing that can be used as template parameters for doing this kind of verification.

Creating a separate test for each category of types supported by a template would be cumbersome. Instead, writing a single templatized test is usually preferable. Unfortunately, different types often require different syntaxes for constructing an object and getting an object's value. This inconsistency makes writing generic code rather difficult.

This component provides a solution with a utility struct, TemplateTestFacility, that defines two class method templates, create and getIdentifier, that respectively have consistent syntaxes for creating objects and getting a integer value representing the state of objects of a parameterized type.

This component also provides a macro, BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE, that serves as a convenient way to instantiate and invoke a template (for testing) having a type parameter for a specified list of types. In addition, this component provides a set of macros referring to commonly useful lists of types intended for testing that can be used as arguments to BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE.

The utilities and macros provided by this component, along with the types defined in the bsltf package, are explained in more detail in the following sections.

TemplateTestFacility

The TemplateTestFacility struct provides the following static (class) method templates to construct objects and get the states of objects of a supported parameterized type (supported types are those types intended for testing defined in the bsltf package):

Macros and Test Types

The BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE macro instantiates a specified class template for a specified list of types and call a specified class method of each instantiation. The macro takes in arguments in the following order:

This component also defines a set of macros, each providing a list of types, that can be used as the last argument to BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE. The following is a brief synopsis of these macros (note that all macros names have the BSLTF_TEMPLATETESTFACILITY_ prefix, which is omitted for layout efficiency):

The BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_PRIMITIVE macro refers to a representative set of primitive types that are useful for testing:

Type Description
---- -----------
signed char signed character
size_t signed integral type
TemplateTestFacility::ObjectPtr pointer to an object
TemplateTestFacility::FunctionPtr pointer to a function
TemplateTestFacility::MethodPtr pointer to a method

The BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_USER_DEFINED macro refers to a representative set of user-defined types that are useful for testing (note that all types described here belong to the bsltf namespace, which is not explicitly qualified for layout efficiency):

Type Description
---- -----------
EnumeratedTestType::Enum an enumeration
UnionTestType a union
SimpleTestType class with no special traits defined
AllocTestType class that allocates memory, defines
the 'bslma::UsesBslmaAllocator'
trait, and ensures it is not bitwise
moved
NonOptionalAllocTestType class that allocates memory, defines
trait, ensures it is not bitwise
moved, and does not have default ctor.
MovableTestType class that has both move and copy
semantics, and ensures it is not
bitwise moved
MovableAllocTestType class that has both move and copy
semantics, allocates memory, defines
trait, and ensures it is not bitwise
moved
MoveOnlyAllocTestType class that has only move semantics,
allocates memory, defines the
trait, and ensures it is not bitwise
moved
BitwiseCopyableTestType class that is bitwise-copyable and
defines the
trait
BitwiseMoveableTestType class that is bitwise-moveable and
defines the
trait
AllocatingBitwiseMoveableTestType class that allocates memory, is
bitwisemoveable, and defines the
traits
NonTypicalOverloadsTestType class that defines and assert on
invocation of certain
non-typically-overloaded operators
('operator new', 'operator delete',
'operator&') to ensure that they are
not called
EmplacableTestType class that takes <= 14 non-allocating
arguments and is used to ensure that
arguments are forwarded correctly
from functions and methods taking
variable number of arguments and their
analagous in C++03 environments.
AllocEmplacableTestType class that takes up to 14 allocating
arguments and is used to ensure that
arguments are forwarded correctly
from functions and methods taking
variable number of arguments and their
analagous in C++03 environments.
Definition balxml_encoderoptions.h:68
Definition bslmf_istriviallycopyable.h:329
Definition bslma_usesbslmaallocator.h:343
Definition bslmf_isbitwisemoveable.h:718

The BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_REGULAR macro refers to the union of the types provided by BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_PRIMITIVE and BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_USER_DEFINED. These types are designed to work within the regular operating conditions of a typical template. Typically, a test driver for a template instantiates its tests (using the BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE macro) for all of the types referred by BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_REGULAR.

The BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_AWKWARD macro refers to a set of types that have certain attributes that make them unlikely to work for all of the operations of a template. Typically, not all methods of a template are instantiable with these types, so these types are most often used independently in tests explicitly designed for a (single) type.

Type Description
---- -----------
NonAssignableTestType class having no assignment operator
NonCopyConstructibleTestType class having no copy constructor (Note
that this class can not be created with
'TemplateTestFacility::create' because
the class method returns the newly
constructed object by value.)
NonDefaultConstructibleTestType class having no default constructor
NonEqualComparableTestType class having no equality-comparison
operators

The BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_ALL refers to all the test types provided in the bsltf package.

Usage

This section illustrates intended use of this component.

Example 1: Using the BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE Macro

In this example, we demonstrate how to use BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE to call a class method of a template for a list of types.

First, we define a struct template TestTemplate taking in a parameterized TYPE that has a class method, printTypeName:

template <class TYPE>
struct TestTemplate {
// This 'struct' provides a namespace for a simple test method.
// CLASS METHODS
static void printTypeName();
// Prints the name of the parameterized 'TYPE' to the console.
};
template <>
void TestTemplate<int>::printTypeName()
{
printf("int\n");
}
template <>
void TestTemplate<char>::printTypeName()
{
printf("char\n");
}
template <>
void TestTemplate<double>::printTypeName()
{
printf("double\n");
}

Now, we can instantiate the TestTemplate class for each of the types int, char, and double, and call the printTypeName class method of each instantiation using the BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE macro:

printTypeName,
int, char, double);
#define BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE(CLASS, METHOD,...)
Definition bsltf_templatetestfacility.h:1053

Finally, we observe the console output:

int
char
double

Example 2: Writing a Type Independent Test Driver

In this example, we demonstrate using the TemplateTestFacility struct and the macros provided by this component to test the default constructor and primary manipulator of a class template in the context of a typical BDE-style test driver. Note that a goal of the demonstrated test is to validate the class template with a broad range of types emulating those with which the template might be instantiated.

First, we define a simple class template, MyNullableValue, that we will later need to test:

template <class TYPE>
class MyNullableValue {
// This (value-semantic) class template extends the parameterized
// 'TYPE' to include the notion of a "null" value.
// DATA
TYPE d_value; // non-null value
bool d_nullFlag; // flag to indicate if the value is null
public:
MyNullableValue()
// Create a 'MyNullableValue' that initially has a value of null.
: d_nullFlag(true)
{
}
bool isNull() const
// Return 'true' if this object is null, and 'false' otherwise.
{
return d_nullFlag;
}
void makeNull()
// Set this object to the null value.
{
d_nullFlag = true;
}
const TYPE& value() const {
// Return a reference providing non-modifiable access to the
// underlying object of the parameterized 'TYPE'. The behavior is
// undefined if the object is null.
return d_value;
}
void makeValue(const TYPE& value)
// Set the value of this object to be that of the specified 'value'
// of the parameterized 'TYPE'.
{
d_nullFlag = false;
d_value = value;
}
};
void makeValue(TYPE *object)

Then, we define some aliases for the micros that will be used by the test driver:

#define RUN_EACH_TYPE BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE
#define TEST_TYPES_REGULAR BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_REGULAR

Next, we define a struct template, MyTestDriver, that provides a namespace containing the test cases (here, only testCase2 is defined for brevity) of the test driver:

template <class TYPE>
struct MyTestDriver {
// This 'struct' provides a namespace for the class methods used to
// implement the test driver.
// TYPES
typedef MyNullableValue<TYPE> Obj;
// This 'typedef' provides an alias to the type under testing.
static void testCase2();
// Test primary manipulators.
};

Now, we define the implementation of MyTestDriver::testCase2:

template <class TYPE>
void MyTestDriver<TYPE>::testCase2()
{
// --------------------------------------------------------------------
// DEFAULT CTOR, PRIMARY MANIPULATORS, AND DTOR
// Ensure that we can use the default constructor to create an
// object (having the default-constructed value), use the primary
// manipulators to put that object into any state relevant for
// thorough testing, and use the destructor to destroy it safely.
//
// Concerns:
//: 1 An object created using the default constructor (with or without
//: a supplied allocator) has the contractually specified value.
//:
//: 2 The 'makeValue' method sets the value of a object to any
//: specified value.
//:
//: 3 The 'makeNull' method set the value of a object to null.
//:
//: 4 Objects of different values can coexist.
//:
//: 5 The destructor does not modify other objects.
//
// Plan:
//: 1 Default-construct an object and use the (as yet unproven) salient
//: attribute accessors to verify that the value of the object is the
//: null value. (C-1)
//:
//: 2 Default-construct another object, and use the 'makeValue' method,
//: to set the value of the object to a non-null value. Use the (as
//: yet unproven) salient attribute accessors to verify that the new
//: object has the expected value and the object created in P-1 still
//: has the same value. (C-2, 4)
//:
//: 3 Using the loop-based approach, for each identifier in a range of
//: integer identifiers:
//:
//: 1 Default-construct a modifiable object, 'mL', and use the (as
//: yet unproven) salient attribute accessors to verify the value
//: of the default constructed object is the null value. (C-1)
//:
//: 2 Create an object of the parameterized 'TYPE', 'LV', using the
//: 'TemplateTestFacility::create' class method template,
//: specifying the integer loop identifier.
//:
//: 3 Use the 'makeValue' method to set the value of 'mL' to 'LV'.
//: Use the (as yet unproven) salient attribute accessors to verify
//: 'mL' has the expected value. (C-2)
//:
//: 4 Invoke the 'makeNull' method of 'mL'. Use the attribute
//: accessors to verify the value of the object is now null. (C-3)
//:
//: 4 Create an object in a nested block. Below the block, verify the
//: objects created in P-1 and P-2 still have the same value. (C-5)
//
// Testing:
// MyNullableValue();
// ~MyNullableValue();
// void makeNull();
// void MakeValue(const TYPE& value);
// --------------------------------------------------------------------
if (verbose)
printf("\nDEFAULT CTOR, PRIMARY MANIPULATORS, AND DTOR"
"\n============================================\n");
if (verbose) printf("\nTesting default constructor.\n");
Obj mW; const Obj& W = mW;
assert(true == W.isNull());
Obj mX; const Obj& X = mX;
const TYPE XV = TemplateTestFacility::create<TYPE>(1);
mX.makeValue(XV);
assert(1 == TemplateTestFacility::getIdentifier<TYPE>(X.value()));
if (verbose) printf("\nTesting primary manipulators.\n");
for (size_t ti = 0; ti < 10; ++ti) {
if (veryVerbose) { T_ P(ti) }
Obj mL; const Obj& L = mL;
assert(true == L.isNull());
const TYPE LV = TemplateTestFacility::create<TYPE>(ti);
mL.makeValue(LV);
assert(false == L.isNull());
assert(LV == L.value());
mL.makeNull();
assert(true == L.isNull());
}
if (verbose) printf("\nTesting destructor.\n");
{
Obj Z;
}
assert(true == W.isNull());
assert(XV == X.value());
}

Notice that, we create objects of the parameterized TYPE using the TemplateTestFacility::create class method template specifying an integer identifier; the created object has a value that is uniquely associated with the integer identifier.

Also notice that we verified that an object of the parameterized TYPE has the expected value in two ways:

  1. By equal comparing (1) the integer identifier returned from calling the TemplateTestFacility::getIdentifier class method template (specifying the object), and (2) the integer identifier uniquely associated with the expected state of the object.
  2. By directly using the equality-comparison operator for the parameterized TYPE. Note that the equality-comparison operator is defined for all types intended for testing in the bsltf package except for bsltf::NonEqualComparableTestType.

Finally, we use the BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE macro to instantiate MyTestDriver for each of the types listed in BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_REGULAR and invoke the testCase2 class method of each instantiation:

case 2: {
// ----------------------------------------------------------------------
// DEFAULT CTOR & PRIMARY MANIPULATORS
// ----------------------------------------------------------------------
if (verbose) printf("\nDEFAULT CTOR & PRIMARY MANIPULATORS"
"\n===================================\n");
RUN_EACH_TYPE(MyTestDriver, testCase2, TEST_TYPES_REGULAR);
} break;