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 */
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