-
Notifications
You must be signed in to change notification settings - Fork 217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #832, add "handler" feature to utassert stub API #966
Fix #832, add "handler" feature to utassert stub API #966
Conversation
a76d7ac
to
7757078
Compare
This can be reviewed now. For review purposes I split this into multiple commits. The main API addition is in the first commit, which only affects a few files. This should be the first focus of review. The second commit is the only one that touched FSW, and the goal was to split up "Impl" headers where they were grouped in a problematic way for scripting. It just splits a header file into two. The remainder of commits focus on fixing/updating all the existing stubs to use this new pattern. Existing "post-hook" logic from the original stub is moved to a default handler function, and the stub itself is now generated directly from the C header using the generator script. Note that no part of this PR changes a single test case. All changes are transparent to existing tests (migrating the "special sauce" from being in the stub itself into a default handler preserves all existing behavior so all tests still work as they always have). |
Slight addendum to my above statement about not affecting existing tests - there was a direct ref to the "ArgPtr" member of the context in the sample_lib test, which actually did break because auto-generated stub registers this differently. See nasa/sample_lib#60 In reality all hooks should use the provided macro - it is much more robust for several reasons. |
There is a bit to get through here, but I will start on it; reviewing the stub generation is where I will begin. I do have a couple questions about the Testing performed
Is that after the change is implemented or before? Were any tests performed to show use cases where the stub logic is completely replaced? I'm sure I will get there at some point, just curious if there are any current results. |
Both. This is one reason I pushed as separate commits. This way the first commit would also individually have CI run against it to get the green check mark, showing that the first commit alone does pass CI. Basically you can take only the first commit and run all tests, they still pass, and still show same coverage level. (i.e. with only the new stub API and internal utassert stub impl changes added, but no actual stub changes, nothing breaks). You can then add the remainder of commits which update all the OSAL stubs to use the generated/split (stub+default handler) patterns, and run tests again. Still passes, with same coverage levels.
As this being a new feature there are no current test cases in OSAL or CFE that make use of the handler replacement. I did test it by specifically changing an ES test case to call Regarding the generated stubs, they all follow a very basic pattern. Here is a random example:
The pattern is very simple, with the goal being to:
The main benefit of scripting this sort of thing is that every function stub is now exactly the same, no special sauce are added to any of them, and everything is done exactly the same way in all of them, because a script generated them all. It greatly minimizes the risk that any stub becomes "special" in any way - any the special sauce must be put into the handler, not the script-generated stub function. And thus it can be overridden, always. |
A sample from my first run on a cf header:
Putting those stubs in their place! Ya, Basic! |
Built all cf stub files that have associated headers (looks good!), will move on to trying them out on Monday. |
What if I have functions that do not appear in a header, but are accessed through an extern? (can be done by hand, just curious)
|
This script does assume / require that functions needing stubs are prototyped in a separate header. I believe this is also required by most coding standards (i.e. that any function that serves as an interface between source units should be declared in a separate header file). Local helper functions are an exception (i.e. functions used only within the same unit) but by definition those shouldn't need stubs, either. My recommendation is that if you have functions which are defined and one source file and referred to in another, that you move the declaration to a common header (it can be a private header, that's fine, as long as its a header). |
BTW - as far as my previous comment goes - I totally acknowledge that the generator script currently does exactly this ( |
CCB:2021-04-21 APPROVED with changes
|
I have setup and run the same experiment as I did for #839 and all tests completed as expected which verifies that the "do-no-harm" approach is intact; however, there is a caveat here. Current stubs must be transmogrified into the default hook functions if the behaviors they exhibit are to be preserved. The experiment added default hooks for two cFE API functions CFE_MSG_GetSize and CFE_MSG_GetMsgId, but the work was done by hand without any automation. It would seem that having the ability to automate the creation of default hooks from the current stubs will be a necessity. That being said, the design appears to work well in the small simulation I have run. 14 unit tests were run without any updates on nasa/cFS@c58824d and all the tests performed as expected. cFE was updated with the two stub changes and OSAL was updated to https://github.com/jphickey/osal/tree/fix-832-stub-handler-replacements. Tests were run again, unaltered, and received the same results as before the update (no harm!). The seven experimental tests were then updated to use the new handler functionality while the control tests still used the hook functionality. The 7 experiment tests correctly changed behavior and the 7 control tests stayed static (stub control enabled!). I will concur with the update once the conflict is resolved. |
7757078
to
6e30293
Compare
Rebased to latest main branch, should merge clean now. |
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.
To imporove scriptability of generating the shared layer stubs, makes two minor adjustments: - adds a typedef for the callback in OS_ObjectIdIteratorProcessEntry. This avoids the complex syntax of function pointer argument, which is hard to read but also harder to parse in a script, too. - Splits the "os-shared-printf.h" header into two parts, adding a new header file "os-shared-console.h". This is because OS_ConsoleOutput_Impl is implemented in a separate unit in the source, and this allows a better relationship between stub files and header files.
The "built-in" logic from existing stubs is converted to a handler function and moved to a different source file. The generated stub references this function if it exists and uses it as a default handler, so the default behavior is not changed. In this pattern the stubs in this directory are strictly limited to the same public APIs that are defined in header files under src/os/inc. This has the side effect of removing some internal stubs required for coverage test. Those stubs will be reinstated in the coverage specific stubs where other internal functions are.
Update the coverage-specific (shared layer internal) stubs to use generated stubs.
6e30293
to
e9f344b
Compare
nasa/osal#972, update documentation for read/write nasa/osal#966, add "handler" feature to utassert stub API nasa/osal#953, Adds local makefile and bundle/local unit test actions with coverage verification nasa/osal#971, socket accept using incorrect record
Combines: nasa/cFE#1431 nasa/osal#975 nasa/sample_lib#61 Includes: nasa/cFE#1379, memory pool pointer type nasa/cFE#1289, ES child task functional test nasa/cFE#1289, typo in macro name nasa/cFE#1286, Remove broken BUILDDIR reference nasa/cFE#1305, remove option for "osal_compatible" nasa/cFE#1374, CFE_SUCCESS constant type nasa/cFE#1316, Remove Unused Error Codes nasa/cFE#1370, better warning about malformed startup line nasa/cFE#1373, check status of call to `CFE_ES_CDS_CachePreload` nasa/cFE#1384, update documentation for `CFE_ES_DeleteCDS` nasa/cFE#1385, exception logic when app/task is not found nasa/cFE#1372, error if alignment size not a power of two nasa/cFE#1368, remove unneeded CFE_ES_SYSLOG_APPEND macro nasa/cFE#1382, improve documentation for resourceID patterns nasa/cFE#1371, assert `CFE_RESOURCEID_MAX` is a bitmask nasa/osal#972, update documentation for read/write nasa/osal#966, add "handler" feature to utassert stub API nasa/osal#953, Adds local makefile and bundle/local unit test actions with coverage verification nasa/osal#971, socket accept using incorrect record nasa/osal#959, move async console option nasa/sample_lib#60, replace direct ref to ArgPtr with macro
As the name field is a multiple of 4 bytes, there needs to be 3 bytes of padding, not 1, to avoid implicit padding. This doesn't change anything, it just makes the padding explicit instead of implicit.
Describe the contribution
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 definitionsthat rely on a separate handler to deal with the needed outputs.
Lastly, all existing stubs in OSAL will be updated to use the auto-generated stub logic from the script, created directly from the C header. This ensures that stubs will match the FSW implementation. Note - one header in FSW (
os-shared-printf.h
) was broken into two parts, to improve the ability to do this.Fixes #832
Testing performed
Build and sanity check CFE
Run all unit tests, ensure all tests pass. Confirm that unmodified coverage test scripts are passing as-is, and coverage is still 100%.
Expected behavior changes
This change only affects coverage testing with stubs, and should be fully backward compatible with existing tests.
Once a stub is regenerated via the auto-generator script:
int32
)System(s) tested on
Ubuntu 20.04
RTEMS 4.11
Additional context
The "handler" function uses a different hook function prototype than the hook functions do. The latter is kept the same for backward compatibility with existing tests.
The intent is to address a few long-standing shortcomings in the design of the hook function. In particular, hook function design assumes/requires that the function returns
int32
. While most do, some do not, and this is a major issue if the goal is to completely replace the handler associated with a stub.In this design there is now a dedicated data buffer to hold the return value, which can be of any data type, and a macro that the handler (or hook) implementation can use to fill/set that return value. However the framework will automatically populate that buffer from the
int32
status code if the hook/handler did not populate it, which was what ~90% of stub functions did in their "post-hook" logic. This simplifies things in that those functions don't need any handler at all unless they had other work to do.Contributor Info - All information REQUIRED for consideration of pull request
Joseph Hickey, Vantage Systems, Inc.