Skip to content

feat(gmock): GoogleMock wrap + mock/gtest autogeneration for C++ interfaces - #255

Open
Ulrond wants to merge 4 commits into
developfrom
feature/253-wrap-gmock-in-ut-core
Open

feat(gmock): GoogleMock wrap + mock/gtest autogeneration for C++ interfaces#255
Ulrond wants to merge 4 commits into
developfrom
feature/253-wrap-gmock-in-ut-core

Conversation

@Ulrond

@Ulrond Ulrond commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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.hUT_-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/…) and UT_VERIFY_AND_CLEAR. Pulled in by ut.h on the C++ path.
  • src/cpp_source/ut_gtest.cpp — the runner now calls ::testing::InitGoogleMock (which also inits gtest), so unmet UT_EXPECT_CALLs fail the run.
  • Makefile — adds googlemock/include and links -lgmock. The pinned googletest 1.15.2 is the combined distribution and builds libgmock by default, so build.sh is unchanged.
  • tests/src/cpp_source/ut_test_gmock.cpp — worked interface-mock example (matchers, actions, NiceMock; a DISABLED_ 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 emits mock_<class>.h (one UT_MOCK_METHOD per virtual, with return type / const / override / pointer-return recovered correctly) and a test_<class>.cpp gtest skeleton using the UT_ macros.
  • scripts/test_autogenerate_script.sh — adds a self-contained (no-network) case that generates from a sample interface and asserts the output.

GoogleMock's own gmock_gen.py was removed from googletest by 1.15.2 (present up to ~1.10), so generation is provided by ut-core rather than the upstream script.

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

class MockDriver : public IDriver {
public:
    UT_MOCK_METHOD(int, open, (const char *path), (override));
};

UT_ADD_TEST(MyFixture, opens) {
    MockDriver mock;
    UT_EXPECT_CALL(mock, open(UT_ANY)).WillOnce(UT_RETURN(3));
    UT_ASSERT_EQUAL(driver_start(mock), 0);
}
./scripts/autogenerate_gmock.sh -f IDriver.h -c IDriver -o generated/

Testing

  • Standalone compile+run of the UT_ wrappers against real gmock.
  • Full ut-core CPP build (build.sh + make -C tests VARIANT=CPP): the UTGMockL1 example suite passes; no regression to the existing gtest suites (the 3 pre-existing UT_FAIL_* self-tests still fail by design).
  • Generated mock + skeleton from a 4-method interface compile, link and run in the CPP build (passing TestISample suite); the new autogenerate self-test asserts the generated content.

Scope / limitations

  • The generator handles standard single-line virtual … = 0; declarations; wrap comma-bearing template return types in a typedef. Multi-line declarations are out of scope for this first cut.
  • Branch name predates the combined scope; it delivers both the gmock wrap and the autogeneration.

Ulrond added 2 commits July 21, 2026 08:50
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).
@Ulrond
Ulrond requested a review from a team as a code owner July 21, 2026 08:25
@Ulrond Ulrond added the enhancement New feature or request label Jul 21, 2026
Copilot AI review requested due to automatic review settings July 21, 2026 08:25
@rdkcmf-jenkins

Copy link
Copy Markdown
Contributor

b'## Copyright scan failure
Commit: ff63b76
Report detail: https://gist.github.com/rdkcmf-jenkins/009f4ce9d12681b955f9545820c02f02'

@rdkcmf-jenkins

Copy link
Copy Markdown
Contributor

b'## Blackduck scan failure details

Summary: 0 violations, 0 files pending approval, 1 file pending identification.

  • Protex Server Path: /home/blackduck/github/ut-core/255/rdkcentral/ut-core

  • Commit: ff63b76

Report detail: gist'

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.sh plus 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.

Comment thread Makefile Outdated
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
Comment thread scripts/autogenerate_gmock.sh Outdated
Comment on lines +175 to +190
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 */"
Comment on lines +158 to +160
HEADER_BASENAME="$(basename "${AGT_HEADER}")"
CLASS_LOWER="$(echo "${AGT_CLASS}" | tr '[:upper:]' '[:lower:]')"
MOCK_CLASS="Mock${AGT_CLASS}"
Comment on lines +149 to +156
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

@rdkcmf-jenkins

Copy link
Copy Markdown
Contributor

b'## WARNING: A Blackduck scan failure has been waived

A prior failure has been upvoted

  • Upvote reason: ok

  • Commit: ff63b76
    '

@Ulrond Ulrond self-assigned this Jul 22, 2026
@Ulrond Ulrond moved this from Todo to Review Requested in UT-Core Roadmap Jul 22, 2026
…#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.
Copilot AI review requested due to automatic review settings July 22, 2026 08:09
@Ulrond

Ulrond commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @copilot — addressed in bb8e846:

  1. Makefile fallback main — now links `gmock_main` instead of `gtest_main`, so a downstream project without its own `main()` still gets `InitGoogleMock` (gmock flags + verification), matching the runner.
  2. Reserved include guard — generated guard is now `MOCK_H` (no leading `__`).
  3. Header not copied into `-o` — the generator now copies the interface header alongside the generated files, so the emitted mock compiles regardless of where `-o` points.
  4. Unvalidated `-c` — the class name is now validated as a plain C++ identifier before it is used in paths/generated code (rejects `/..` etc.).

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 (UT_MOCK_LT) and an assertion (UT_ASSERT_LESS) are different roles, and the prefix now says so. There were no name collisions before, but this removes any ambiguity.

Re-verified: full CPP build + UTGMockL1 suite pass; generated mock+skeleton compile in the CPP build; the autogenerate self-test asserts pass.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread include/ut_gmock.h Outdated
Comment on lines +127 to +128
/** @brief Returns the default-constructed return value. */
#define UT_MOCK_RETURN_DEFAULT ::testing::Return()
Comment thread scripts/autogenerate_gmock.sh Outdated
Comment on lines +176 to +178
# 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.
Copilot AI review requested due to automatic review settings July 22, 2026 11:58
@Ulrond

Ulrond commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Two further Copilot findings addressed in 6bca917:

  • UT_MOCK_RETURN_DEFAULT expanded to ::testing::Return() (the void-return action), wrong for non-void methods → replaced with UT_MOCK_DO_DEFAULT (::testing::DoDefault()). A mocked method with no action already returns a default-constructed value automatically.
  • Pure-virtual destructor (virtual ~Foo() = 0;) was matched by the generator and would emit invalid MOCK_METHOD → destructors are now excluded.

That covers all 6 review comments (the earlier 4 were fixed in bb8e846). Re-verified: CPP build + UTGMockL1 suite pass; a pure-virtual-destructor interface now generates only its mockable methods.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread include/ut_gmock.h
Comment on lines +31 to +32
#ifndef __UT_GMOCK_H
#define __UT_GMOCK_H
Comment thread include/ut_gmock.h
*/
#define UT_MOCK_VERIFY_AND_CLEAR(mock) ::testing::Mock::VerifyAndClearExpectations(&(mock))

#endif /* UT -> GMOCK - Wrapper */
@Ulrond
Ulrond requested review from bhanucbp and kanjoe24 July 27, 2026 16:34
@Ulrond

Ulrond commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@bhanucbp @kanjoe24 — adding you as named reviewers; this has been sitting on the ut-core_codeowners team request alone. Review ask: the UT_MOCK_* wrapper surface in include/ut_gmock.h and whether scripts/autogenerate_gmock.sh output matches how you want C++ interface mocks generated. Copilot comments from the last three pushes are all addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Review Requested

3 participants