Skip to content

Commit

Permalink
Fix nasa#832, add "handler" feature to utassert stub API
Browse files Browse the repository at this point in the history
Adds the concept of a "handler" function to UT assert.  A handler
is basically the custom logic that exists between the hook function
and the return to the stub caller.  In current UT stubs, this is
hard coded, and it generally comprises setting output parameters
and translating return values as needed.

This concept adds the basic plumbing to allow the handler to be
configured just like a hook function already does.  The difference
is that the handler is directly responsible for setting all outputs.

This also includes a script to auto-generate stub functions that
match this pattern.  Given an API header file, the script extracts
the declarations, and generates a source file with stub definitions
that rely on a separate handler to deal with the needed outputs.

Note this initial commit only adds the basic framework.  It does
not change any existing stubs or tests, and is fully backward
compatible, as it is a new feature and it is a no-op unless
actually configured/used by the stub or test case.  Follow on
commits will update the stubs to use this pattern.
  • Loading branch information
jphickey committed Apr 27, 2021
1 parent afb5f7b commit 838a7eb
Show file tree
Hide file tree
Showing 4 changed files with 970 additions and 49 deletions.
81 changes: 81 additions & 0 deletions ut_assert/inc/utgenstub.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* NASA Docket No. GSC-18,370-1, and identified as "Operating System Abstraction Layer"
*
* Copyright (c) 2019 United States Government as represented by
* the Administrator of the National Aeronautics and Space Administration.
* All Rights Reserved.
*
* 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.
*/

/**
* \file
*
* Provides a set of macros to facilitate generating stub code
*
* These macros are primarily used in the code generated by the "generate_stubs.pl" script
*/

#ifndef UTGENSTUB_H
#define UTGENSTUB_H

#include "common_types.h"
#include "utstubs.h"

/**
* Helper macro to set up a return buffer in a generated stub
*
* Registers a buffer that the hook/handler can fill. This works
* with any return data type, not just int32.
*
* \param FuncName The name of the function
* \param ReturnType The type of return value
*/
#define UT_GenStub_SetupReturnBuffer(FuncName, ReturnType) \
UT_Stub_RegisterReturnType(UT_KEY(FuncName), sizeof(ReturnType))

/**
* Helper macro to get the return value from the handler
*
* Gets the value exported from the hook/handler
*
* \param FuncName The name of the function
* \param ReturnType The type of return value
*/
#define UT_GenStub_GetReturnValue(FuncName, ReturnType) \
(*(ReturnType *)UT_Stub_GetReturnValuePtr(UT_KEY(FuncName), sizeof(ReturnType)))

/**
* Helper macro to add a local parameter to the current context
*
* This makes the parameter accessible from hook/handler functions
*
* \param FuncName The name of the function
* \param ParamType The type of the parameter
* \param ParamName The name of the parameter
*/
#define UT_GenStub_AddParam(FuncName, ParamType, ParamName) \
UT_Stub_RegisterContextWithMetaData(UT_KEY(FuncName), #ParamName, UT_STUBCONTEXT_ARG_TYPE_INDIRECT, &(ParamName), \
sizeof(ParamType))

/**
* Helper macro to execute the actual stub handler
*
* Additional arguments are passed through
*
* \param FuncName The name of the function
* \param Type The type of handler to execute (Va or Basic)
*/
#define UT_GenStub_Execute(FuncName, Type, ...) UT_Execute##Type##Handler(UT_KEY(FuncName), #FuncName, __VA_ARGS__);

#endif /* UTGENSTUB_H */
228 changes: 224 additions & 4 deletions ut_assert/inc/utstubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,59 @@ typedef struct
*/
typedef struct
{
int32 Int32StatusCode;
bool Int32StatusIsSet;
uint32 ArgCount;
const void * ArgPtr[UT_STUBCONTEXT_MAXSIZE];
UT_StubArgMetaData_t Meta[UT_STUBCONTEXT_MAXSIZE];
} UT_StubContext_t;

/**
* Function pointer for user-specified hooks/stub callbacks
* Function pointer for user-specified hook callbacks
*
* Hook functions provide a user-specified supplement to the handler
* function. They are invoked prior to the actual stub handler, and can
* perform any additional test-specific logic necessary.
*
* \sa UT_HandlerFunc_t
*/
typedef int32 (*UT_HookFunc_t)(void *UserObj, int32 StubRetcode, uint32 CallCount, const UT_StubContext_t *Context);

/**
* Function pointer for user-specified variable-argument callbacks
*
* \copydoc UT_HookFunc_t
*/
typedef int32 (*UT_VaHookFunc_t)(void *UserObj, int32 StubRetcode, uint32 CallCount, const UT_StubContext_t *Context,
va_list va);

/**
* Function pointer for user-specified stub handlers
*
* Handler functions are the final logic associated with a stub
*
* Handlers must perform the role of generating the actual
* return code to be sent to the caller, and must also output
* any required output parameters.
*
* \note Stubs should no longer have any "assumed" logic. Any
* additional outputs or return code translation is now
* dealt with only in the handler function.
*
* \param[inout] UserObj Opaque object from original registration
* \param[in] FuncKey UT key of function currently being handled
* \param[in] Context Context information for current handler
*/
typedef void (*UT_HandlerFunc_t)(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context);

/**
* Function pointer for user-specified variable-argument stub handlers
*
* \copydoc UT_HandlerFunc_t
* \param[in] va Variable argument list from initial stub call
*/
typedef void (*UT_VaHandlerFunc_t)(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context, va_list va);

/**************************************************************
* Functions for use within test code implementation
**************************************************************/
Expand Down Expand Up @@ -201,30 +242,73 @@ void UT_ClearDefaultReturnValue(UT_EntryKey_t FuncKey);
* The callback may optionally pass back context data, depending on the stub
* implementation.
*
* In UT assert, hook functions supplement the handler functions, and will
* be called prior to the handler to add additional logic. This facility should
* be used in cases where the default handling logic is acceptable, but needs some
* extensions or additional work performed at the time the stub is invoked, such
* as setting a state variable in the parent test program.
*
*
* \param FuncKey The stub function to add the hook to.
* \param HookFunc User defined hook function. Set NULL to delete/clear an entry.
* \param UserObj Arbitrary user data object to pass to the hook function
*/
void UT_SetHookFunction(UT_EntryKey_t FuncKey, UT_HookFunc_t HookFunc, void *UserObj);

/**
* Set a Handler function for a particular call
*
* This allows the user to completely replace the handler for a given function.
*
* The handler assumes all responsibility for the final behavior of the stub,
* including translating return values, and any outputs/side effects the stub
* function should have. The default handler for the stub is NOT used.
*
* \param FuncKey The stub function to add the hook to.
* \param HandlerFunc User defined hook function. Set NULL to delete/clear an entry.
* \param UserObj Arbitrary user data object to pass to the hook function
*/
void UT_SetHandlerFunction(UT_EntryKey_t FuncKey, UT_HandlerFunc_t HandlerFunc, void *UserObj);

/**
* Set a variable-argument Hook function for a particular call
*
* Identical to the normal hook function except that it includes a va_list argument
* such that the entire set of args from the original call can be passed to the hook.
* Identical to UT_SetHookFunction() except that it includes a va_list argument
* such that it can be used with a variable-argument stub function.
*
* This can be important for printf-like stubs where correctness might depend on the
* contents of the arguments but the types of arguments aren't known.
*
* However, some systems have limited support for va_list, so this might not be
* available on those systems. Tests should use the generic (non-va) hook function
* unless the arguments are truly necessary.
*
* \sa UT_SetHookFunction
*
* \param FuncKey The stub function to add the hook to.
* \param HookFunc User defined hook function. Set NULL to delete/clear an entry.
* \param UserObj Arbitrary user data object to pass to the hook function
*/
void UT_SetVaHookFunction(UT_EntryKey_t FuncKey, UT_VaHookFunc_t HookFunc, void *UserObj);

/**
* Set a variable-argument Handler function for a particular call
*
* This allows the user to completely replace the handler for a given function.
*
* The handler assumes all responsibility for the final behavior of the stub,
* including translating return values, and any outputs/side effects the stub
* function should have. The default handler for the stub is NOT used.
*
* This is identical to UT_SetHandlerFunction() but the function includes a va_list
* argument to allow use with variadic functions.
*
* \param FuncKey The stub function to add the hook to.
* \param HandlerFunc User defined hook function. Set NULL to delete/clear an entry.
* \param UserObj Arbitrary user data object to pass to the hook function
*/
void UT_SetVaHandlerFunction(UT_EntryKey_t FuncKey, UT_VaHandlerFunc_t HandlerFunc, void *UserObj);

/**
* Get a count for the number of times a stub was invoked, at its most recent return value
*
Expand Down Expand Up @@ -349,6 +433,100 @@ size_t UT_Stub_CopyFromLocal(UT_EntryKey_t FuncKey, const void *LocalBuffer, siz
UT_Stub_RegisterContextWithMetaData(FuncKey, #Parameter, UT_STUBCONTEXT_ARG_TYPE_INDIRECT, &Parameter, \
sizeof(Parameter))

/**
* Set a stub return value from the handler function
*
* This sets/copies the specified value to the buffer that will be
* returned from the original stub call back to the caller. This
* provides the actual return value and it will override/replace
* any assumed or default return value.
*
* The passed-in buffer should be a pointer to the same data type
* that the stub returns. Any type mismatch is considered an error.
*
* The handler function must call this routine for any stub which
* returns a data type other than int32.
*
* @note If there is no handler function or the handler does not call
* this routine to set a return value, the implementation will use the
* "Int32StatusCode" value as a return if the size matches sizeof(int32).
*
* \param FuncKey The stub function associated with the buffer
* \param BufferPtr Pointer to the local return value
* \param BufferSize Size of the local return value
*/
void UT_Stub_SetReturnValue(UT_EntryKey_t FuncKey, const void *BufferPtr, size_t BufferSize);

/**
* Creates a buffer to hold the return value for the stub
*
* \param FuncKey The stub function associated with the buffer
* \param ReturnSize Size of the return value
*/
void UT_Stub_RegisterReturnType(UT_EntryKey_t FuncKey, size_t ReturnSize);

/**
* Obtains direct pointer to buffer for stub return value
*
* This is a helper routine and not intended to be invoked directly
* by handler/hook routines. Use UT_Stub_CopyToReturnValue() to
* set the return value from a local variable.
*
* \sa UT_Stub_CopyToReturnValue()
*
* \param FuncKey The stub function associated with the buffer
* \param ReturnSize Size of the return value
*/
void *UT_Stub_GetReturnValuePtr(UT_EntryKey_t FuncKey, size_t ReturnSize);

/**
* Exports a value from a hook/handler and stages it to be returned to the caller.
*
* This is the preferred alternative to using "int32" type returns, since not all
* functions return int32 status codes. This method works with any return type.
*
* \param FuncKey The stub function associated with the buffer
* \param ReturnPtr Pointer to return value data
* \param ReturnSize Size of the return value data
*/
void UT_Stub_CopyToReturnValue(UT_EntryKey_t FuncKey, const void *ReturnPtr, size_t ReturnSize);

/**
* Macro to simplify use of the UT_Stub_CopyToReturnValue() in a hook/handler context.
* This returns the value stored in the specified variable
*
* \param FuncKey The stub function identifier
* \param ReturnValue The value to return
*/
#define UT_Stub_SetReturnValue(FuncKey, ReturnValue) \
UT_Stub_CopyToReturnValue(FuncKey, &ReturnValue, sizeof(ReturnValue))

/**
* Checks if the int32 status code is set, and optionally gets its value
*
* The UT framework is optimized to handle functions that return an int32 status
* code, as this is a very common pattern in CFE, OSAL, and related applications.
*
* In the context of a hook or handler function, this retrieves the value of an
* int32 status code configured by the test case, either via the "deferred" or
* the fixed/forced status code APIs, or generated by a previous hook function.
*
* This returns whether or not a status code was explicitly set by the test case.
* If no status code was set, the value will be set to a default (0).
*
* \param[in] Context The context object that was passed to the hook
* \param[out] StatusCodeBuffer If non-NULL, this will be set to the actual status code value
* If status code was not unset, this will be set to the default code value
*
* \returns true if code was explicitly set, false if it is not set (and has default value)
*
* \note The return value allows a hook/handler function to differentiate between a default
* status code value that was explicitly set vs. implicitly set. That is, if the test case
* explicitly sets a status code of "0", this will return "true", even if the default value
* is also "0".
*/
bool UT_Stub_GetInt32StatusCode(const UT_StubContext_t *Context, int32 *StatusCodeBuffer);

/**
* Registers a single context element for the hook callback
*
Expand Down Expand Up @@ -402,14 +580,37 @@ const void *UT_Hook_GetArgPtr(const UT_StubContext_t *ContextPtr, const char *Na
* Checks first for a deferred retcode, then for a constant retcode, and a default (0) if neither is present.
* Optionally also prints a debug level status message to show that the function was called.
*
* If a hook function is registered, then it is called using the supplied va_list of arguments.
* If a hook function is registered, then it will be called with the stub context
*
* Finally, the handler function will be invoked, if one is associated with the stub. The handler
* assumes final responsibility for any output parameters or return values.
*
* \param FunctionName The printable name of the actual function called, for the debug message. If
* NULL then no debug message will be generated.
* \param FuncKey The Key to look up in the table
*/
int32 UT_DefaultStubImplWithArgs(const char *FunctionName, UT_EntryKey_t FuncKey, int32 DefaultRc, va_list va);

/**
* Handles a stub call for a variadic function
*
* Implements the complete sequence of a stub for a "bare" stub function (i.e. a stub that has no
* built-in/assumed logic). This is the preferred stub architecture as it allows a complete override
* of any and all output behavior / side effects by the test program.
*
* The DefaultHandler will be invoked unless the parent program has registered a replacement handler.
*
* \note The "generate_stubs.pl" tool can be used to auto-generate bare stub functions that are
* compatible with this pattern.
*
* \sa UT_DefaultStubImplWithArgs()
*
* \param FuncKey The key of the stub being executed
* \param FunctionName The printable name of the actual function called, for the debug message.
*/
void UT_ExecuteVaHandler(UT_EntryKey_t FuncKey, const char *FunctionName, UT_VaHandlerFunc_t DefaultHandler,
va_list VaList);

/**
* Default implementation for a stub function that should be useful for most cases.
*
Expand All @@ -422,6 +623,25 @@ int32 UT_DefaultStubImplWithArgs(const char *FunctionName, UT_EntryKey_t FuncKey
*/
int32 UT_DefaultStubImpl(const char *FunctionName, UT_EntryKey_t FuncKey, int32 DefaultRc, ...);

/**
* Handles a stub call for a normal (non-variadic) function
*
* Implements the complete sequence of a stub for a "bare" stub function (i.e. a stub that has no
* built-in/assumed logic). This is the preferred stub architecture as it allows a complete override
* of any and all output behavior / side effects by the test program.
*
* The DefaultHandler will be invoked unless the parent program has registered a replacement handler.
*
* \note The "generate_stubs.pl" tool can be used to auto-generate bare stub functions that are
* compatible with this pattern.
*
* \sa UT_DefaultStubImplWithArgs()
*
* \param FuncKey The key of the stub being executed
* \param FunctionName The printable name of the actual function called, for the debug message.
*/
void UT_ExecuteBasicHandler(UT_EntryKey_t FuncKey, const char *FunctionName, UT_HandlerFunc_t DefaultHandler);

/**
* Macro to simplify usage of the UT_DefaultStubImpl() function
*
Expand Down
Loading

0 comments on commit 838a7eb

Please sign in to comment.