Increase rcl_lifecycle test coverage and add more safety checks#649
Increase rcl_lifecycle test coverage and add more safety checks#649
Conversation
Signed-off-by: Stephen Brawner <brawner@gmail.com>
c277cfe to
44dbbed
Compare
|
It looks like there is a bit more I can do to boost coverage. I'll work on this more tomorrow. |
479aa12 to
db71eb1
Compare
db71eb1 to
66e036f
Compare
Signed-off-by: Stephen Brawner <brawner@gmail.com>
66e036f to
70465b7
Compare
|
Much better coverage, but com_interface and default_state_machine are dragging it down.
|
ahcorde
left a comment
There was a problem hiding this comment.
Some of the errors are better defined as RCL_RET INVALID_ARGUMENT. If you decide to change this you should change some error in the tests.
| } | ||
| if (!label) { | ||
| RCL_SET_ERROR_MSG("State label is null\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
| if (!transition) { | ||
| RCL_SET_ERROR_MSG("transition pointer is null\n"); | ||
| return RCL_RET_OK; | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
|
|
||
| if (!label) { | ||
| RCL_SET_ERROR_MSG("label pointer is null\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
|
|
||
| if (!start) { | ||
| RCL_SET_ERROR_MSG("start state pointer is null\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
|
|
||
| if (!goal) { | ||
| RCL_SET_ERROR_MSG("goal state pointer is null\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
| if (!allocator) { | ||
| RCL_SET_ERROR_MSG("can't initialize transition, no allocator given\n"); | ||
| RCL_SET_ERROR_MSG("can't finalize transition, no allocator given\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
| { | ||
| if (!state_machine) { | ||
| RCL_SET_ERROR_MSG("State machine is null\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
| } | ||
| if (!node_handle) { | ||
| RCL_SET_ERROR_MSG("Node handle is null\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
| } | ||
| if (!allocator) { | ||
| RCL_SET_ERROR_MSG("can't initialize state machine, no allocator given\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
| { | ||
| if (!allocator) { | ||
| RCL_SET_ERROR_MSG("can't free transition map, no allocator given\n"); | ||
| return RCL_RET_ERROR; |
There was a problem hiding this comment.
| return RCL_RET_ERROR; | |
| return RCL_RET_INVALID_ARGUMENT; |
|
I'm not sure I can change the error codes of already existing functions this late in the game. While, I agree invalid_argument is better than error, I chose RCL_RET_ERROR to match the rest of the code. |
Karsten1987
left a comment
There was a problem hiding this comment.
Looks good to me besides one comment.
| return ret; | ||
|
|
||
| fail: | ||
| // if rcl_lifecycle_transition_map_fini() fails, it will clobber the error string here, twice |
There was a problem hiding this comment.
is that meant as a TODO here? If fini() fails and sets its own error message, I think we can either reset it here or concatenate. I think overwriting the error message actually yields a warning.
There was a problem hiding this comment.
No it was an issue I had found, and didn't have a great way of resolving it. I didn't know concatenation would work, I'll add that in.
It will indeed print a warning if the error message is set and then it proceeds to set another error message. However, I didn't want to lose the trace of errors messages as they fall from one failure to the next (so it would have been impossible to see the original failure case).
Signed-off-by: Stephen Brawner <stephenbrawner@verbsurgical.com>
7a3b0b9 to
93ca2ec
Compare
93ca2ec to
71607bc
Compare
|
|
||
| fail: | ||
| // Semicolon handles the "a label can only be part of a statement..." error | ||
| fail:; |
There was a problem hiding this comment.
| fail:; | |
| fail: |
Or was that semicolon intended?
There was a problem hiding this comment.
The compiler wouldn't let me declare current_error directly after fail: without it. The semicolon effectively creates a new statement. Should I move the variable declarations to the top?
|
|
||
| if (rcl_lifecycle_transition_map_fini(&state_machine->transition_map, allocator) != RCL_RET_OK) { | ||
| RCL_SET_ERROR_MSG("could not free lifecycle transition map. Leaking memory!\n"); | ||
| const char * fini_error = (rcl_error_is_set()) ? rcl_get_error_string().str : ""; |
There was a problem hiding this comment.
if I interpret this correctly this line will always result in an empty string, given that you reset the error message just above.
| } else if (strcmp(current_error, "") != 0) { | ||
| RCL_SET_ERROR_MSG(current_error); |
There was a problem hiding this comment.
Sorry to iterate again over this, but I think that else if branch is not needed. If I see this correctly, we'll fetch the error message before just to reset it again in this branch.
Why not just retrieving the existing error message within the first if branch?
something like this:
if (rcl_lifecycle_transition_map_fini(&state_machine->transition_map, allocator) != RCL_RET_OK) {
const char * fini_error = "";
if (rcl_error_is_set()) {
fini_error = rcl_get_error_string().str;
rcl_reset_error();
}
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Freeing transition map failed while handling a previous error. Leaking memory!"
"\nOriginal error:\n\t%s\nError encountered in rcl_lifecycle_transition_map_fini():\n\t%s\n",
current_error, fini_error);
}
if (!rcl_error_is_set()) {
RCL_SET_ERROR_MSG("Unspecified error ..");
}
There was a problem hiding this comment.
The problem originally was that the error message was already set. But if rcl_lifecycle_transition_map_fini failed, it would set its own message, and then the message would get set a third time inside this first if loop. So the goal was to keep track of current_message, and append fini_error if it existed and throw a combined error that was hopefully more helpful than either of them individually.
However, if rcl_lifecycle_transition_map_fini succeeds, we still want the user to know what caused the code to reach fail: in the first place, which is in current_message , but only if rcl_error_is_set() was true in line 697.
Originally when I submitted this, I just relied on rcutils to log the transition of error messages, since it warns if a new error message is set while a previous one had already existed. However, since I'm pretty new to the ROS 2 C codebase, I'm really not confident about any particular approach here 🤷♂️
|
Address @Karsten1987's feedback and am running CI again. |
Signed-off-by: Stephen Brawner <stephenbrawner@verbsurgical.com>
be69602 to
661b4b7
Compare
Karsten1987
left a comment
There was a problem hiding this comment.
this looks good to me now. Thanks for iterating with me over this.
|
Thank you for your reviews and help! |
|
@brawner @Karsten1987 this patch is causing test failures in |
|
Looks like a transition can be lazy initialized where start and goal state don't have to be present. Which means we should remove the nullptr checks for these two. |
As part of the push to Quality Level 1, this adds more more tests of the rcl_lifecycle API. While writing these tests, I found a few instances where more checks were needed.
If I didn't understand parts of the API and added checks where they weren't desired, feel free to make comments below. Alternatively, if there are more thorough checks that people feel are necessary I can also add those in. These were pretty much what I found passing around bad parameters.