Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bsltf_templatetestfacility
[Package bsltf]

Provide utilities to help with testing templates. More...

Namespaces

namespace  bsltf

Detailed Description

Outline
Purpose:
Provide utilities to help with testing templates.
Classes:
bsltf::TemplateTestFacility namespace for template-testing utilities
Macros:
BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE(CLASS, METHOD, TYPE...) run all
BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_PRIMITIVE list of primitive types
BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_USER_DEFINED list user types
BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_REGULAR list of typical types
BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_AWKWARD list of atypical types
BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_ALL list all bslmf types
See also:
Component bsltf_simpletesttype, Component 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):
  • create: Return an object of the parameterized TYPE whose value is uniquely associated with a specified integer identifier.
  • getIdentifier: Return the integer identifier used to create a specified object of the parameterized TYPE.
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:
  • The name of the class template to be instantiated
  • The name of the class method to be invoked
  • The names of the types for which the class template will be instantiated (up to 20)
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):
  • TEST_TYPES_PRIMITIVE: list of primitive types
  • TEST_TYPES_USER_DEFINED: list of user-defined types
  • TEST_TYPES_REGULAR: list of typically used types
  • TEST_TYPES_AWKWARD: list of types with odd behaviors
  • TEST_TYPES_ALL: list of all of the types
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
                                      the 'bslma::UsesBslmaAllocator'
                                      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
                                      the 'bslma::UsesBslmaAllocator'
                                      trait, and ensures it is not bitwise
                                      moved

  MoveOnlyAllocTestType               class that has only move semantics,
                                      allocates memory, defines the
                                      'bslma::UsesBslmaAllocator'
                                      trait, and ensures it is not bitwise
                                      moved

  BitwiseCopyableTestType             class that is bitwise-copyable and
                                      defines the
                                      'bsl::is_trivially_copyable'
                                      trait

  BitwiseMoveableTestType             class that is bitwise-moveable and
                                      defines the
                                      'bslmf::IsBitwiseMoveable'
                                      trait

  AllocatingBitwiseMoveableTestType   class that allocates memory, is
                                      bitwisemoveable, and defines the
                                      'bslma::UsesBslmaAllocator'
                                      and 'bslmf::IsBitwiseMoveable'
                                      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.
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:
  BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE(TestTemplate,
                                           printTypeName,
                                           int, char, double);
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;
      }
  };
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;