DAOS-18304 ddb: Introduce DdbAPI interface for Go unit testing#17967
DAOS-18304 ddb: Introduce DdbAPI interface for Go unit testing#17967
Conversation
|
Ticket title is 'Add unit test to ddb go code' |
|
Test stage Unit Test completed with status UNSTABLE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net/job/daos-stack/job/daos//view/change-requests/job/PR-17967/1/testReport/ |
aadfb16 to
92d3255
Compare
|
Test stage Unit Test completed with status UNSTABLE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net/job/daos-stack/job/daos//view/change-requests/job/PR-17967/3/testReport/ |
92d3255 to
b2a2966
Compare
|
Test stage Functional on EL 9 completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net//job/daos-stack/job/daos/view/change-requests/job/PR-17967/4/execution/node/1032/log |
1c2251d to
2b2bad2
Compare
|
Test stage Unit Test bdev with memcheck completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net/job/daos-stack/job/daos/job/PR-17967/7/display/redirect |
|
Test stage Unit Test with memcheck completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net/job/daos-stack/job/daos/job/PR-17967/7/display/redirect |
111eb0e to
44d2003
Compare
|
Test stage Build on EL 9 completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net//job/daos-stack/job/daos/view/change-requests/job/PR-17967/11/execution/node/272/log |
|
Test stage Build on EL 8 completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net//job/daos-stack/job/daos/view/change-requests/job/PR-17967/11/execution/node/280/log |
|
Test stage Build on Leap 15 completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net//job/daos-stack/job/daos/view/change-requests/job/PR-17967/11/execution/node/288/log |
f6bdb6e to
2ebb6be
Compare
|
Test stage Functional Hardware Medium MD on SSD completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net//job/daos-stack/job/daos/view/change-requests/job/PR-17967/14/execution/node/1335/log |
|
Test stage Functional Hardware Medium Verbs Provider MD on SSD completed with status FAILURE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net//job/daos-stack/job/daos/view/change-requests/job/PR-17967/14/execution/node/1356/log |
91c32a6 to
3c4faba
Compare
3c4faba to
24d96e6
Compare
Preliminary refactoring to enable Go unit test coverage for the ddb command layer in a follow-up patch. A DdbAPI interface is introduced to decouple the command layer from the C VOS implementation, so that future tests can use stubs without requiring a live VOS environment. - Convert all ddbXxx() free functions to (ctx *DdbContext) Xxx() methods and propagate DdbAPI through addAppCommands(), createGrumbleApp() and parseOpts(). - Remove dc_pool_path/dc_db_path from ddb_ctx; pass db_path explicitly at every call site. - Simplify dv_pool_open/dv_pool_destroy to (path, db_path, ctx, flags); move vos_file_parts allocation into a new create_vos_file_parts() helper. - Fix ddb_run_feature(): guard dc_poh/dc_write_mode reset with the close flag; add missing error message on open failure. - Fix memory leaks in Feature() (enable/disable C strings) and DtxStat() (freeString defer ordering). - Propagate errors from runCmdStr() and runFileCmds() in non-interactive mode. - Add typed unknownCmdError; fix duplicated ERROR: prefix in exitWithError(). - Remove os.Exit() from parseOpts() to make it fully testable. - Raise DB_PATH_SIZE and VOS_PATH_SIZE to PATH_MAX; store the original VOS path in vf_vos_file_path. - Fix test VOS tree path indices and dtx_stat message. - Fix helper_stat_open_modify_close_stat(): restore the pool handle saved from dv_test_setup after close instead of leaving a stale one. - Fix read_only_vs_write_mode_test(): compare PRE vs POST digest instead of PRE vs PRE. Features: recovery Signed-off-by: Cedric Koch-Hofer <[email protected]>
24d96e6 to
f81f3be
Compare
| func SetCString(out **C.char, s string) func() { | ||
| cstr := C.CString(s) | ||
| *out = cstr | ||
| type DdbAPI interface { |
There was a problem hiding this comment.
https://go-proverbs.github.io/ "the bigger the interface, the weaker the abstraction", we definitely haven't always followed this advice in DAOS but it's worth noting
There was a problem hiding this comment.
Agreed with Tom, but seeing how it's used, this seems like an acceptable intermediate step to enable better unit testing.
There was a problem hiding this comment.
Agreed on the fact thata large interface weakens the abstraction.
However, since each sub-command maps to one method, the interface cannot easily be split further without losing the ability to stub the entire VOS layer in tests.
As kjacque noted, this is an intentional intermediate step: the follow-up PR #18086 shows exactly how the interface is used in unit tests.
tanabarr
left a comment
There was a problem hiding this comment.
LGTM, not sure if we really need the large interface and there might be a better way of achieving the goal but that's not a blocking comment
kjacque
left a comment
There was a problem hiding this comment.
This is a big patch doing several different things. I would like to see this split into 3 different PRs, one for each of the categories referenced by the description. For a refactor I would not expect to see any changes in tests, for example--we want to ensure the refactor did NOT change behavior. And I would like the C API improvements to be separated from the bugfixes if possible.
| func SetCString(out **C.char, s string) func() { | ||
| cstr := C.CString(s) | ||
| *out = cstr | ||
| type DdbAPI interface { |
There was a problem hiding this comment.
Agreed with Tom, but seeing how it's used, this seems like an acceptable intermediate step to enable better unit testing.
| /* printing a recx works */ | ||
| dvt_fake_print_called = 0; | ||
| opt.path = "/[0]/[0]/[0]/[0]/[0]"; | ||
| opt.path = "/[0]/[0]/[0]/[1]/[0]"; |
There was a problem hiding this comment.
Why are we changing behavior here? IMO it is best to do the refactor and any desired behavior changes separately to ensure the refactor is transparent.
There was a problem hiding this comment.
I agree with you. While writing this PR I also found some bugs and fixed them along the way, which is why they ended up in the same patch.
However, the following changes are tightly coupled to the Go refactoring and cannot be split without leaving the code or tests in a broken intermediate state.
Removing dc_pool_path/dc_db_path from ddb_ctx is required by the Go layer which now passes paths explicitly; this forces the dv_pool_open / dv_pool_destroy signature change.
The signature change in turn requires adding vf_vos_file_path and raising path size constants in ddb_parse, which then cascades into:
ddb_commands.csimplifications.- The
dtx_statmessage change (the old message printedctx->dc_pool_pathwhich no longer exists). - All test call-site updates, including the path index corrections (caused by moving
dvt_vos_insert_2_records_with_dtxtodcv_suit_setupas part of the test setup rewrite).
The following parts are standalone bug fixes that could go in a separate PR:
-
ddb_run_featureout-label guard (ddb_commands.c):if (close)braces preventingdc_poh/dc_write_modefrom being reset when the pool was not opened by this call. -
assert_memory_equalcomparing PRE vs PRE (ddb_vos_tests.c):read_only_vs_write_mode_testwas comparing the pre-digest against itself instead of the post-digest. -
tctx->dvt_pohnot restored (ddb_vos_tests.c):helper_stat_open_modify_close_statwas leaving the pool handle in an invalid state for the rest of the test suite.
Does it sounds reasonable to you ?
There was a problem hiding this comment.
If some bugfixes must be left in, could you separate them out into a different commit in the same PR? That would make it easier to review. Thanks.
|
Additionally, any bugfixes touching Go code post-refactor provide you with a great opportunity to write new Go unit tests. |
A first set of Go unit tests could be found in the follow-up PR #18086. The
|
Cool, good to know there's another PR in the works with test coverage. I'd still like this PR split to separate the transparent refactoring from the behavior changes though. |
I will do my best to make split this PR in smaller ones as small as possible. |
| /* Test with root path */ | ||
| rc = parse_vos_file_parts("/" MOCKED_POOL_UUID_STR "/vos-0", NULL, &parts); | ||
| assert_rc_equal(rc, DER_SUCCESS); | ||
| assert_string_equal("/" MOCKED_POOL_UUID_STR "/vos-0", parts.vf_vos_file_path); |
There was a problem hiding this comment.
Please apply everywhere Ctrl+F will find a match.
| assert_string_equal("/" MOCKED_POOL_UUID_STR "/vos-0", parts.vf_vos_file_path); | |
| assert_string_equal(MOCKED_VOS_PATH_STR, parts.vf_vos_file_path); |
| D_ERROR("VOS path '%s' too long: got=%zu, max=%d\n", vos_path, vos_path_len, | ||
| VOS_PATH_SIZE - 1); | ||
| rc = -DER_EXCEEDS_PATH_LEN; | ||
| goto out_preg; |
There was a problem hiding this comment.
Since you are using out_preg label for both vos_path and db_path now it seems you have a memory leek when vos_path passes the test and get copied into the vfp_tmp.
| helper_stat_open_modify_close_stat(tctx, fs, false /** read-only */); | ||
| assert_int_equal(fs[FILE_STATE_PRE].stat.st_mtime, fs[FILE_STATE_POST].stat.st_mtime); | ||
| assert_memory_equal(fs[FILE_STATE_PRE].digest, fs[FILE_STATE_PRE].digest, | ||
| assert_memory_equal(fs[FILE_STATE_PRE].digest, fs[FILE_STATE_POST].digest, |
There was a problem hiding this comment.
This is interesting. I assume it changed after #17533 has landed.
How come this test has not failed previously?
There was a problem hiding this comment.
Hard to fail when comparing the same memory segments ;-)
Hopefully it is not failing with POST.
| noAutoOpen := map[string]bool{ | ||
| "feature": true, | ||
| "open": true, | ||
| "close": true, | ||
| "prov_mem": true, | ||
| "smd_sync": true, | ||
| "rm_pool": true, | ||
| "dev_list": true, | ||
| "dev_replace": true, | ||
| } | ||
| if !noAutoOpen[opts.Args.RunCmd] { |
There was a problem hiding this comment.
Since there is no record with false value and it looks like not all commands actually have records in the noAutoOpen map, it seems a list would be a better solution. What do you think?
| noAutoOpen := map[string]bool{ | |
| "feature": true, | |
| "open": true, | |
| "close": true, | |
| "prov_mem": true, | |
| "smd_sync": true, | |
| "rm_pool": true, | |
| "dev_list": true, | |
| "dev_replace": true, | |
| } | |
| if !noAutoOpen[opts.Args.RunCmd] { | |
| noAutoOpen := []string{ | |
| "feature", | |
| "open", | |
| "close", | |
| "prov_mem", | |
| "smd_sync", | |
| "rm_pool", | |
| "dev_list", | |
| "dev_replace", | |
| } | |
| if !slices.Contains(noAutoOpen, opts.Args.RunCmd) { |
There was a problem hiding this comment.
Agree with you that using a dictionary is overkill in this situation and probably less efficient with this number of elements.
- Use slice instead of dictionary
Description
Overview
Preliminary refactoring to enable Go unit test coverage for the ddb command layer.
The actual tests are added in the follow-up PR #18086.
A
DdbAPIGo interface is introduced to decouple the command layer from the C VOSimplementation, so that tests can use a stub without requiring a live VOS environment.
This patch is also solving the ticket DAOS-18581
Go Refactoring
DdbAPIinterface: allddbXxx()free functions are converted to(ctx *DdbContext) Xxx()methods;DdbAPIis propagated throughaddAppCommands(),createGrumbleApp()andparseOpts().DdbContextlifecycle:Init()becomes a method on a caller-allocatedDdbContext, giving the caller (or a test stub) full control over the object.dc_pool_path/dc_db_pathfromddb_ctx: paths are nowpassed as explicit function arguments, removing the need for the
SetCString()helper.
parseOpts()testability:os.Exit()calls are removed; all errors arereturned up to
main(), makingparseOpts()fully unit-testable.HasPrefixloop with anoAutoOpenmap for exact command-name matching.
C API Simplification
dv_pool_open/dv_pool_destroy: signatures simplified from(path, path_parts, poh, flags, write_mode)to(path, db_path, ctx, flags);vos_file_partsallocation is moved into a new internalcreate_vos_file_parts()helper.DB_PATH_SIZEandVOS_PATH_SIZEraised from 256 toPATH_MAX; the original VOS path is now stored invf_vos_file_pathand itslength is validated in
parse_vos_file_parts().Bug Fixes
ddb_run_feature():dc_poh/dc_write_modestate reset is now guardedby the
closeflag, preventing corruption when the pool was already open;add missing error message on
dv_pool_open()failure.runCmdStr()andrunFileCmds()are nowpropagated instead of silently discarded.
--version: prints directly instead of synthesising a fake command.--db_pathwithout--vos_path: returns an explicit error instead ofbeing silently ignored.
closePoolIfOpen()helper withdefer.Feature(): fix memory leak on enable/disable C strings.DtxStat(): fixdefer freeString()ordering.unknownCmdErrorstruct for type-safedetection;
exitWithError()now prints the command list on unknown commandsand fixes the duplicated
ERROR:prefix in fault resolution messages.rm_pool: make--vos_pathmandatory; passdb_pathexplicitly.ls,value_dumpandilog_dumptests; updatedtx_statmessage; restore pool handle inhelper_stat_open_modify_close_stat()after close; fixassert_memory_equalin
read_only_vs_write_mode_test()to compare PRE vs POST digest.Steps for the author:
After all prior steps are complete: