The Stubs API provides out-of-the-box support for simple type values. Advanced users can extend the Stubs API to establish dedicated support for selected complex types. This process is described in Advanced Approach to Working with Complex Types. In many situations, however, the Basic Approach to Working with Complex Types, used with the helper named argument to the stub body, is sufficient. An example of this approach is presented below.

Basic Approach to Working with Complex Types

Consider the following structure definition:

struct Point { 
          int _x;
          int _y;

};

The stub is created for the following function declaration:

Point movePoint(Point line, int xoff, int yoff);

If modifying the function return value is required, the following modification to the stub body must be applied:

  1. Helper variables for each member to be modified should be introduced
  2. Variables should be exposed for external modification (by test case code)
  3. Modified values should be assigned back to the original return value

 

Point CppTest_Stub_movePoint (Point point)
{
   Point __return = Point();
   int __callOrig = 0;
   int __point_x = __return._x; /* [1] helper variable for complex type member */    
   int __point_y = __return._y; /* [1] helper variable for complex type member */
   CPPTEST_ACTUAL_CALL("movePoint").WithArg("point", &point).
     WithArg("__return", &__return)).WithArg("__callOrig", ByRef(__callOrig)). 
     /* [2] expose variable for external modification */
     WithArg("__point_x", ByRef(__point_x)).
     /* [b] expose variable for external modification */
     WithArg("__point_y", ByRef(__point_y)).
     End();
   __return._x = __point_x; /* [3] update return value with modified value */
   __return._y = __point_y; /* [3] update return value with modified value */ 
   if (__callOrig) {
      Point __return = movePoint(point); 
      CPPTEST_ACTUAL_AFTER_CALL("movePoint").
         WithArg("point", &point).WithArg("__return", &__return)).
         WithArg("__callOrig", ByRef(__callOrig)).End();
      return __return;
   }
   return __return;
}

 

And then, inside the test case return value can be modified as shown below:

CPPTEST_ON_CALL("movePoint").Arg("__point_x").Assign(1).
Arg("__point_y").Assign(2);

Advanced Approach to Working with Complex Types

Using complex types with the Stubs API requires writing additional type-specific helper code to facilitate access to complex type data members. When this additional type specific utility code is provided, you can access complex type fields using functions from the base Stubs API.

To illustrate this concept, assume the following structures definition:

struct Point {
          int _x;
          int _y;
};
struct Line {
         Point _p1;
         Point _p2;
};

The stub is created is for the following function declaration:

Line moveLine(Line line, int xoff, int yoff);

In this example, the goal is to enable stub behavior configuration for complex type arguments and parameters per the following construction:

CPPTEST_ON_CALL("moveLine").Arg("__return").Field("_p1").Field("_x").Assign(0);
CPPTEST_ON_CALL("moveLine").Arg("__line").Field("_p2").Field("_y").Assign(12);

To achieve the above goal, the following code has to be added to the source file where stub for moveLine() function is defined.
 

/**
  * Field access function for Point structure
  * /
 tgr_value CppTest_Tgr_GetField_Point(tgr_value_ptr obj, tgr_value_ptr field_name) 
{
   Point *point = (Point *)obj->value.ptr;
   const char* name = tgr_value_get_string(field_name);
   if (tgr_strcmp(name, "_x") == 0) {
      return tgr_int_ref(&point->_x);
   } else if (tgr_strcmp(name, "_y") == 0) {
      return tgr_int_ref(&point->_y);    
   } else {
      return tgr_error(c_tgr_error_wrong_get_field_argument);
   }
}
/**
 * Helper function returning pointer to structure with Point type
 * specific utilities
 */
 tgr_type_ptr CppTest_Tgr_Type_Data(void)
{
   static struct tgr_class_definition class_definition = {tgr_false};
   if (!class_definition.initialized) {
      tgr_initialize_class_definition(&class_definition);
      class_definition.vtbl.get_field = CppTest_Tgr_GetField_Data;
  }
  return &class_definition.type;
}
/**
 * Field access function for Line structure
 */
tgr_value TRIGGER_CDECL CppTest_Tgr_GetField_Line(tgr_value_ptr obj, 
tgr_value_ptr field_name)
{
   Line *line = (Line *)obj->value.ptr;
   const char* name = tgr_value_get_string(field_name);

   if (tgr_strcmp(name, "_p1") == 0) {
      return tgr_object(&line->p1, CppTest_Tgr_Type_Point());
   } else if (tgr_strcmp(name, "_p2") == 0) {
      return tgr_object(&line->_p2, CppTest_Tgr_Type_Point());
   } else {
     return tgr_error(c_tgr_error_wrong_get_field_argument);
   }
}
/**
* Helper function returning pointer to structure with Line type
* specific utilities
*/
tgr_type_ptr CppTest_Tgr_Type_Line(void)
{
   static struct tgr_class_definition class_definition = {tgr_false};
   if (!class_definition.initialized) {
      tgr_initialize_class_definition(&class_definition);
      class_definition.vtbl.get_field = CppTest_Tgr_GetField_Line;
   }
   return &class_definition.type;
}

 

Lastly, type-specific manipulation code needs to be installed in the stub body. This requires modifying automatically generated stub body and replacing the following method invocation with a new invocation:

WithArg(const char* name, RValue value)

Replacement invocation:

WithArg(const char* name, const volatile void* obj, tgr_type_ptr type)

Method invocations allow you to specify dedicated type utilities. Modified stub body should look similar to the following example:

 

Line CppTest_Stub_moveLine (Line line, int xoff, int yoff)
{
   Line __return = Line();
   int __callOrig = 0;
   CPPTEST_ACTUAL_CALL("moveLine ").
      WithArg("line", &line, CppTest_Tgr_Type_Line()).
      WithArg("xoff", ByRef(xoff)).
      WithArg("xoff", ByRef(yoff)).
      WithArg("__return", &__return, CppTest_Tgr_Type_Line()).
      WithArg("__callOrig", ByRef(__callOrig)).End();
   if (__callOrig) {
      Line __return = moveLine(line, xoff, yoff);
      CPPTEST_ACTUAL_AFTER_CALL("moveLine ").
         WithArg("line", &line, CppTest_Tgr_Type_Line()).
         WithArg("xoff", ByRef(xoff)).
         WithArg("xoff", ByRef(yoff)).
         WithArg("__return", &__return, CppTest_Tgr_Type_Line()).
         WithArg("__callOrig", ByRef(__callOrig)).End();
      return __return;
   }    
   return __return;
}
  • No labels