feat(gmock): GoogleMock wrap + mock/gtest autogeneration for C++ interfaces#255
feat(gmock): GoogleMock wrap + mock/gtest autogeneration for C++ interfaces#255Ulrond wants to merge 4 commits into
Conversation
ut-core wrapped gtest but not gmock, so there was no supported way to mock a C++ interface under test. The pinned googletest 1.15.2 is the combined distribution and already ships googlemock, so wiring is wrap-only: - include/ut_gmock.h: UT_MOCK_METHOD / UT_EXPECT_CALL / UT_ON_CALL, matcher (UT_ANY/UT_EQ/UT_GE/UT_STR_EQ/...), action (UT_RETURN/UT_INVOKE/...), cardinality (UT_EXACTLY/UT_AT_LEAST/UT_ANY_NUMBER) and strictness (UT_NICE_MOCK/...) wrappers, plus UT_VERIFY_AND_CLEAR. Pulled in by ut.h on the C++ path. - src/cpp_source/ut_gtest.cpp: runner now calls ::testing::InitGoogleMock (also inits gtest) so unmet EXPECT_CALLs fail the run. - Makefile: add googlemock/include to INC_DIRS and -lgmock to the link line. - tests/src/cpp_source/ut_test_gmock.cpp: worked interface-mock example (matchers, actions, NiceMock; a DISABLED_ negative case for the unmet expectation). - AGENTS.md / README.md: document the mocking macros and CPP gmock linkage. Verified: standalone compile+run of the wrappers against real gmock, and a full CPP build (build.sh builds libgmock by default) + run — the UTGMockL1 suite passes with no regression to the existing gtest suites.
…aces (#254) Extends ut-core autogeneration to the CPP/gtest path. scripts/autogenerate_gmock.sh parses the pure-virtual methods of a C++ interface header and emits: - mock_<class>.h : a mock class with one UT_MOCK_METHOD per virtual method (return type, name, params and const/override recovered correctly, including pointer/reference return types). - test_<class>.cpp: a gtest test skeleton (UTCore fixture, UT_ADD_TEST_TO_GROUP, a UT_ADD_TEST stub per method) using the UT_ macros from #253. Handles standard single-line `virtual ... = 0;` declarations. GoogleMock's own gmock_gen.py was removed from googletest by 1.15.2, so generation is provided by ut-core rather than the upstream script. - scripts/test_autogenerate_script.sh: adds a self-contained (no-network) case that generates from a sample interface and asserts the mock/skeleton content. - AGENTS.md: documents the generator alongside the gmock wrappers. Consolidates the CPP/gtest autogeneration tracked by #174 and #161. Verified: generated mock + skeleton compile, link and run in the ut-core CPP build (a 4-method interface produced a passing TestISample suite).
|
b'## Copyright scan failure |
There was a problem hiding this comment.
Pull request overview
Adds first-class GoogleMock (gmock) support to ut-core’s CPP/GTest variant and introduces a new generator to emit gmock mocks + gtest skeletons from C++ interface headers, aligning the CPP developer workflow with existing C autogeneration.
Changes:
- Add
UT_-prefixed GoogleMock wrapper macros and initialize gmock in the CPP runner. - Add
autogenerate_gmock.shplus a new self-test case for the generator output. - Update build wiring and documentation to reflect gmock availability in the CPP variant.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/src/cpp_source/ut_test_gmock.cpp | Adds a worked example suite demonstrating the new UT_ gmock wrappers. |
| src/cpp_source/ut_gtest.cpp | Switches runner init from InitGoogleTest to InitGoogleMock. |
| scripts/test_autogenerate_script.sh | Adds a self-contained generator test that asserts emitted mock/skeleton content. |
| scripts/autogenerate_gmock.sh | New script to generate mock_<class>.h + test_<class>.cpp from a C++ interface header. |
| README.md | Documents that CPP variant links GoogleMock and points to the example. |
| Makefile | Adds googlemock include path and links gmock for CPP builds. |
| include/ut.h | Pulls in ut_gmock.h automatically on the CPP path. |
| include/ut_gmock.h | New UT_ wrapper macros over common gmock APIs (matchers/actions/cardinalities/etc.). |
| AGENTS.md | Updates the CPP API docs to include gmock wrappers and the generator. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| INC_DIRS += $(GTEST_SRC)/googletest/include $(GTEST_SRC)/googlemock/include $(UT_CORE_DIR)/src/cpp_source $(UT_CORE_DIR)/src | ||
| TEST_LIB_DIR = $(UT_CORE_DIR)/build/$(TARGET)/cpp_libs/lib/ | ||
| XLDFLAGS += $(YLDFLAGS) $(LDFLAGS) -L$(UT_CONTROL)/build/$(TARGET)/lib -L$(TEST_LIB_DIR) -lgtest_main -lgtest -lut_control -lpthread -lm | ||
| XLDFLAGS += $(YLDFLAGS) $(LDFLAGS) -L$(UT_CONTROL)/build/$(TARGET)/lib -L$(TEST_LIB_DIR) -lgmock -lgtest_main -lgtest -lut_control -lpthread -lm |
| echo "/* Auto-generated GoogleMock for ${AGT_CLASS} (from ${HEADER_BASENAME}). */" | ||
| echo "#ifndef __MOCK_${CLASS_LOWER^^}_H" | ||
| echo "#define __MOCK_${CLASS_LOWER^^}_H" | ||
| echo "" | ||
| echo "#include <ut.h>" | ||
| echo "#include \"${HEADER_BASENAME}\"" | ||
| echo "" | ||
| echo "class ${MOCK_CLASS} : public ${AGT_CLASS}" | ||
| echo "{" | ||
| echo "public:" | ||
| for decl in "${PURE_VIRTUALS[@]}"; do | ||
| AGT_gmock_method_line "${decl}" | ||
| done | ||
| echo "};" | ||
| echo "" | ||
| echo "#endif /* __MOCK_${CLASS_LOWER^^}_H */" |
| HEADER_BASENAME="$(basename "${AGT_HEADER}")" | ||
| CLASS_LOWER="$(echo "${AGT_CLASS}" | tr '[:upper:]' '[:lower:]')" | ||
| MOCK_CLASS="Mock${AGT_CLASS}" |
| if [ -z "${AGT_CLASS}" ]; then | ||
| AGT_CLASS="$(AGT_gmock_first_class "${AGT_HEADER}")" | ||
| fi | ||
| if [ -z "${AGT_CLASS}" ]; then | ||
| echo "Error: no class found in ${AGT_HEADER}; specify one with -c." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
|
b'## WARNING: A Blackduck scan failure has been waived A prior failure has been upvoted
|
…#253, #254) Naming: every macro in ut_gmock.h is now UT_MOCK_* (UT_LT -> UT_MOCK_LT, UT_RETURN -> UT_MOCK_RETURN, UT_EXPECT_CALL -> UT_MOCK_EXPECT_CALL, ...). This makes the mock vocabulary unmistakable next to the UT_ASSERT_* assertions -- a gmock matcher (UT_MOCK_LT) is a different role from an assertion (UT_ASSERT_LESS), and the prefix now says so. Copilot review fixes: - Makefile: link gmock_main (not gtest_main) as the fallback main(), so a downstream project without its own main() still gets InitGoogleMock. - autogenerate_gmock.sh: emit a non-reserved include guard (MOCK_<X>_H_ rather than the reserved __MOCK_...); copy the interface header into the output dir so the generated mock compiles when -o differs from the header's directory; validate that the -c class name is a plain identifier before using it in paths/generated code. Verified: full CPP build + UTGMockL1 suite pass; generated mock+skeleton compile in the CPP build; autogenerate self-test asserts pass.
|
Thanks @copilot — addressed in bb8e846:
Separately, and prompted by a maintainer question about whether the matchers duplicate the assertions: every macro in `ut_gmock.h` is now `UT_MOCK_*` (e.g. `UT_LT`→`UT_MOCK_LT`, `UT_RETURN`→`UT_MOCK_RETURN`, `UT_EXPECT_CALL`→`UT_MOCK_EXPECT_CALL`). This makes the mock vocabulary unmistakable next to the `UT_ASSERT_*` assertions — a matcher ( Re-verified: full CPP build + |
| /** @brief Returns the default-constructed return value. */ | ||
| #define UT_MOCK_RETURN_DEFAULT ::testing::Return() |
| # Collect the pure-virtual declarations of the target class. | ||
| mapfile -t PURE_VIRTUALS < <(AGT_gmock_class_body "${AGT_HEADER}" "${AGT_CLASS}" \ | ||
| | grep -E 'virtual' | grep -E '=[[:space:]]*0[[:space:]]*;') |
…ctors (#253, #254) Two further Copilot findings: - UT_MOCK_RETURN_DEFAULT expanded to ::testing::Return() (the void-return action), which is wrong for non-void mocked methods. Replaced with UT_MOCK_DO_DEFAULT -> ::testing::DoDefault() (a real action); a mocked method with no action already returns a default-constructed value automatically. - autogenerate_gmock.sh collected any 'virtual ... = 0;' line, which would also match a pure-virtual destructor (virtual ~Foo() = 0;) and emit invalid MOCK_METHOD. Destructors are now excluded from mock generation. Verified: CPP build + suite pass; a pure-virtual-destructor interface now generates only its mockable methods.
|
Two further Copilot findings addressed in 6bca917:
That covers all 6 review comments (the earlier 4 were fixed in bb8e846). Re-verified: CPP build + |
| #ifndef __UT_GMOCK_H | ||
| #define __UT_GMOCK_H |
| */ | ||
| #define UT_MOCK_VERIFY_AND_CLEAR(mock) ::testing::Mock::VerifyAndClearExpectations(&(mock)) | ||
|
|
||
| #endif /* UT -> GMOCK - Wrapper */ |
Closes #253. Closes #254. Consolidates the CPP/gtest autogeneration tracked by #174 and #161.
What
ut-core already wraps the gtest backend but not GoogleMock, and its autogeneration only emitted C tests. This adds mocking support for C++ interfaces and the tooling to generate it.
1. GoogleMock wrapper (#253)
include/ut_gmock.h—UT_-prefixed wrappers over gmock:UT_MOCK_METHOD,UT_EXPECT_CALL,UT_ON_CALL, matchers (UT_ANY/UT_EQ/UT_GE/UT_STR_EQ/…), actions (UT_RETURN/UT_INVOKE/UT_SET_ARG_POINTEE/…), cardinalities (UT_EXACTLY/UT_AT_LEAST/UT_ANY_NUMBER), strictness (UT_NICE_MOCK/…) andUT_VERIFY_AND_CLEAR. Pulled in byut.hon the C++ path.src/cpp_source/ut_gtest.cpp— the runner now calls::testing::InitGoogleMock(which also inits gtest), so unmetUT_EXPECT_CALLs fail the run.Makefile— addsgooglemock/includeand links-lgmock. The pinned googletest 1.15.2 is the combined distribution and buildslibgmockby default, sobuild.shis unchanged.tests/src/cpp_source/ut_test_gmock.cpp— worked interface-mock example (matchers, actions,NiceMock; aDISABLED_negative case for the unmet-expectation).2. Mock + test autogeneration (#254)
scripts/autogenerate_gmock.sh -f <interface.h> [-c <Class>] [-o <dir>]— parses the pure-virtual methods of a C++ interface header and emitsmock_<class>.h(oneUT_MOCK_METHODper virtual, with return type / const / override / pointer-return recovered correctly) and atest_<class>.cppgtest skeleton using theUT_macros.scripts/test_autogenerate_script.sh— adds a self-contained (no-network) case that generates from a sample interface and asserts the output.3. Docs
AGENTS.md— mocking macro table (§5), the generator, and CPP gmock linkage.README.md— CPP variant now links gmock; pointer to the example.Usage
Testing
UT_wrappers against real gmock.build.sh+make -C tests VARIANT=CPP): theUTGMockL1example suite passes; no regression to the existing gtest suites (the 3 pre-existingUT_FAIL_*self-tests still fail by design).TestISamplesuite); the new autogenerate self-test asserts the generated content.Scope / limitations
virtual … = 0;declarations; wrap comma-bearing template return types in a typedef. Multi-line declarations are out of scope for this first cut.