Skip to content

chore: migrate custom testing framework to GoogleTest#2

Merged
poyrazK merged 11 commits intomainfrom
chore/gtest-migration
Feb 27, 2026
Merged

chore: migrate custom testing framework to GoogleTest#2
poyrazK merged 11 commits intomainfrom
chore/gtest-migration

Conversation

@poyrazK
Copy link
Copy Markdown
Owner

@poyrazK poyrazK commented Feb 27, 2026

This PR migrates the existing custom testing framework to GoogleTest (GTest). Key changes: - Updated CMakeLists.txt to fetch and configure GTest using FetchContent. - Converted all 8 test suites to standard GTest syntax and macros. - Refactored test_utils.hpp to remove redundant macros. - Addressed various linting and compilation issues across tests.

Summary by CodeRabbit

  • Tests

    • Migrated test suite from custom framework to GoogleTest for standardized testing across all modules
    • Unified test execution through CTest infrastructure
    • Simplified test utilities and conventions across all test suites
  • Chores

    • Consolidated testing configuration and utilities for easier maintenance

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 27, 2026

Warning

Rate limit exceeded

@poyrazK has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 2fc667e and e96a862.

📒 Files selected for processing (9)
  • tests/buffer_pool_tests.cpp
  • tests/cloudSQL_tests.cpp
  • tests/lock_manager_tests.cpp
  • tests/recovery_manager_tests.cpp
  • tests/recovery_tests.cpp
  • tests/server_tests.cpp
  • tests/statement_tests.cpp
  • tests/test_utils.hpp
  • tests/transaction_manager_tests.cpp
📝 Walkthrough

Walkthrough

The pull request migrates the entire test infrastructure from a custom test harness to GoogleTest. Changes include updating CMakeLists.txt to fetch googletest and define a macro for creating test targets, refactoring 8 test files to use GoogleTest's TEST() macros and assertions, and removing custom test utilities while introducing GoogleTest-based alternatives.

Changes

Cohort / File(s) Summary
Build Configuration
CMakeLists.txt
Introduces FetchContent-based googletest integration, adds add_cloudsql_test macro for unified test target creation, replaces run-tests target with CTest execution, and registers 8 test targets (sqlEngine_tests, lock_manager_tests, server_tests, transaction_manager_tests, statement_tests, recovery_tests, recovery_manager_tests, buffer_pool_tests).
Test Infrastructure
tests/test_utils.hpp
Removes custom test counters and assertion macros (EXPECT_TRUE, EXPECT_EQ, etc.), introduces EXPECT_PTR_EQ macro for pointer comparison, simplifies RUN_TEST to no-op, and shifts from exception-based framework to GoogleTest compatibility layer.
Test Suites Refactoring
tests/buffer_pool_tests.cpp, tests/cloudSQL_tests.cpp, tests/lock_manager_tests.cpp, tests/recovery_manager_tests.cpp, tests/recovery_tests.cpp, tests/server_tests.cpp, tests/statement_tests.cpp, tests/transaction_manager_tests.cpp
Migrates all tests from custom main-based harness to GoogleTest TEST() macros, renames test suites to follow Suite::Case pattern, removes manual test runners, replaces custom assertions with GoogleTest equivalents (ASSERT_EQ, EXPECT_EQ, FAIL()), and simplifies test logic with reduced/focused test coverage in most suites.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hop-hop, the tests now shine so bright!
GoogleTest framework makes them right.
No more custom helpers, old and worn—
A testing renaissance is born! ✨
bounces with joy 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main change: migrating from a custom testing framework to GoogleTest, which is the primary objective across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/gtest-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/test_utils.hpp (1)

15-26: ⚠️ Potential issue | 🟡 Minor

Remove unused to_string_safe helper function.

The detail::to_string_safe template is dead code—it is not used anywhere in the codebase and appears to be a remnant from the old custom test framework. Since GoogleTest provides its own value-to-string conversion for assertion messages, this helper can be safely removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_utils.hpp` around lines 15 - 26, Remove the dead helper template
function to_string_safe: delete its full template definition (the to_string_safe
function and any forward declaration) from tests/test_utils.hpp and remove any
now-unused includes or using directives that were only required by that helper
(e.g., <string>, <type_traits>) if they become unused after removal; ensure no
other code references to_string_safe remain before committing.
tests/server_tests.cpp (1)

187-200: ⚠️ Potential issue | 🟠 Major

Fail fast when client setup/connect fails in protocol tests.

At Line 187+ / 218+ / 257+, these tests can exit without any assertion if socket creation or connect fails, which can produce false positives.

💡 Suggested fix (apply this pattern in all three tests)
-    const int sock = socket(AF_INET, SOCK_STREAM, 0);
-    if (sock >= 0) {
-        set_sock_timeout(sock);
-        if (connect(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == 0) { // NOLINT
+    const int sock = socket(AF_INET, SOCK_STREAM, 0);
+    ASSERT_GE(sock, 0) << "socket() failed";
+    set_sock_timeout(sock);
+    ASSERT_EQ(connect(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), 0)
+        << "connect() failed";
+    {
             ...
-        }
-        static_cast<void>(close(sock));
     }
+    static_cast<void>(close(sock));

Also applies to: 218-239, 257-278

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/server_tests.cpp` around lines 187 - 200, The test can silently pass
when socket creation or connect fails; replace the silent conditionals with
explicit test assertions: assert the socket descriptor is valid (e.g.,
ASSERT_GE(sock, 0)) immediately after calling socket(AF_INET, SOCK_STREAM, 0),
and assert that connect(...) returns 0 (e.g., ASSERT_EQ(connect(...), 0)) before
proceeding to send/recv; keep the close(sock) in a cleanup path (or use a scoped
RAII closer) so the socket is always closed after the assertions in the test
functions that use socket, connect, STARTUP_PKT_LEN, and EXPECT_LE.
🧹 Nitpick comments (2)
tests/transaction_manager_tests.cpp (1)

42-56: Consider expanding the Isolation test.

The current test only verifies that transaction IDs are sequential. For a test named "Isolation", consider adding assertions that validate actual isolation properties (e.g., that uncommitted changes from one transaction are not visible to another).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transaction_manager_tests.cpp` around lines 42 - 56, The Isolation test
only checks ID ordering; expand it to assert actual isolation: in
TransactionManagerTests::Isolation use TransactionManager (tm.begin/tm.commit)
with Catalog/storage to create or pin a test record/key, have txn1 perform a
write/update (uncommitted) via the same storage API used elsewhere, then in txn2
attempt to read that record and assert it does not observe txn1's uncommitted
change, then commit txn1 and either read again from a new transaction (tm.begin)
or from txn2 after proper refresh and assert the committed value is visible; use
Transaction, TransactionManager::begin/commit, LockManager, Catalog and
BufferPoolManager instances already created to coordinate and add EXPECT_*
assertions for both pre-commit and post-commit visibility.
tests/lock_manager_tests.cpp (1)

94-120: Deadlock test acknowledges current limitations.

The comment on lines 109-111 appropriately documents that the implementation may not detect deadlocks. Consider creating a tracking issue if deadlock detection is a planned feature.

Would you like me to open an issue to track implementing deadlock detection in the LockManager?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/lock_manager_tests.cpp` around lines 94 - 120, The Deadlock test in
tests/lock_manager_tests.cpp documents that LockManager currently does not
detect deadlocks; if deadlock detection is a planned feature, open a tracking
issue describing the desired behavior for LockManager (e.g., cycle detection in
wait-for graph, victim selection/abort policy, or timeout semantics) and
reference the Deadlock test plus the relevant APIs (LockManager,
acquire_exclusive, unlock, Transaction) so implementers can link the test to the
feature work. Ensure the issue states acceptance criteria (how the Deadlock test
should change once detection is implemented) and any performance/compatibility
constraints.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CMakeLists.txt`:
- Around line 125-131: Replace the mutable main branch URL in the
FetchContent_Declare for googletest with the stable release tag (use URL
pointing to v1.17.0, e.g.
https://github.com/google/googletest/archive/refs/tags/v1.17.0.zip) and add
URL_HASH SHA256 with the official checksum to enable integrity verification;
update the FetchContent_Declare block (the lines around FetchContent_Declare,
URL and FetchContent_MakeAvailable and keep set(gtest_force_shared_crt ON CACHE
BOOL "" FORCE) unchanged) so the project fetches the pinned v1.17.0 archive with
the SHA256 check.

In `@tests/server_tests.cpp`:
- Around line 124-125: The tests currently drop recv() return values for
multi-byte frames (AUTH_OK_LEN, READY_LEN and the other two spots) and also
assign then discard n_d for a single-byte read; replace these fragile reads with
a small helper (e.g., recv_all or read_full) that loops calling recv() until the
requested length is accumulated or an error/EOF occurs, returning total bytes
read or -1 on error; use this helper everywhere the code now calls recv(sock,
buffer.data(), AUTH_OK_LEN, 0) / recv(..., READY_LEN, 0) and where n_d is
assigned so you actually check the return, and ensure failures cause the test to
fail (or abort) instead of silently proceeding.

---

Outside diff comments:
In `@tests/server_tests.cpp`:
- Around line 187-200: The test can silently pass when socket creation or
connect fails; replace the silent conditionals with explicit test assertions:
assert the socket descriptor is valid (e.g., ASSERT_GE(sock, 0)) immediately
after calling socket(AF_INET, SOCK_STREAM, 0), and assert that connect(...)
returns 0 (e.g., ASSERT_EQ(connect(...), 0)) before proceeding to send/recv;
keep the close(sock) in a cleanup path (or use a scoped RAII closer) so the
socket is always closed after the assertions in the test functions that use
socket, connect, STARTUP_PKT_LEN, and EXPECT_LE.

In `@tests/test_utils.hpp`:
- Around line 15-26: Remove the dead helper template function to_string_safe:
delete its full template definition (the to_string_safe function and any forward
declaration) from tests/test_utils.hpp and remove any now-unused includes or
using directives that were only required by that helper (e.g., <string>,
<type_traits>) if they become unused after removal; ensure no other code
references to_string_safe remain before committing.

---

Nitpick comments:
In `@tests/lock_manager_tests.cpp`:
- Around line 94-120: The Deadlock test in tests/lock_manager_tests.cpp
documents that LockManager currently does not detect deadlocks; if deadlock
detection is a planned feature, open a tracking issue describing the desired
behavior for LockManager (e.g., cycle detection in wait-for graph, victim
selection/abort policy, or timeout semantics) and reference the Deadlock test
plus the relevant APIs (LockManager, acquire_exclusive, unlock, Transaction) so
implementers can link the test to the feature work. Ensure the issue states
acceptance criteria (how the Deadlock test should change once detection is
implemented) and any performance/compatibility constraints.

In `@tests/transaction_manager_tests.cpp`:
- Around line 42-56: The Isolation test only checks ID ordering; expand it to
assert actual isolation: in TransactionManagerTests::Isolation use
TransactionManager (tm.begin/tm.commit) with Catalog/storage to create or pin a
test record/key, have txn1 perform a write/update (uncommitted) via the same
storage API used elsewhere, then in txn2 attempt to read that record and assert
it does not observe txn1's uncommitted change, then commit txn1 and either read
again from a new transaction (tm.begin) or from txn2 after proper refresh and
assert the committed value is visible; use Transaction,
TransactionManager::begin/commit, LockManager, Catalog and BufferPoolManager
instances already created to coordinate and add EXPECT_* assertions for both
pre-commit and post-commit visibility.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0365aeb and 2fc667e.

📒 Files selected for processing (10)
  • CMakeLists.txt
  • tests/buffer_pool_tests.cpp
  • tests/cloudSQL_tests.cpp
  • tests/lock_manager_tests.cpp
  • tests/recovery_manager_tests.cpp
  • tests/recovery_tests.cpp
  • tests/server_tests.cpp
  • tests/statement_tests.cpp
  • tests/test_utils.hpp
  • tests/transaction_manager_tests.cpp

Comment thread CMakeLists.txt
Comment on lines +125 to +131
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/heads/main.zip
)
# For Windows: prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

latest stable google/googletest release tag 2025 2026

💡 Result:

The latest stable google/googletest release tag (covering 2025–2026, as of Feb 27, 2026) is:

  • v1.17.0 — released April 30, 2025 (commit 52eb8108c5bdec04579160ae17225d66034bd723). [1][2]

No newer stable release tag than v1.17.0 is shown on the upstream releases page. [1]


🌐 Web query:

GoogleTest v1.14.0 SHA256 checksum source archive zip GitHub

💡 Result:

SHA256 for the GitHub source archive zip v1.14.0.zip:

  • URL: https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
  • SHA256: 1f357c27ca988c3f7c6b4bf68a9395005ac6761f034046e9dde0896e3aba00e4 [1]

🌐 Web query:

GoogleTest v1.17.0 SHA256 checksum github source archive zip

💡 Result:

SHA256 (GitHub source archive ZIP) for GoogleTest v1.17.0:

  • Archive: https://github.com/google/googletest/archive/v1.17.0.zip (redirects to the tag ZIP)
  • SHA256: 40d4ec942217dcc84a9ebe2a68584ada7d4a33a8ee958755763278ea1c5e18ff [1]

Pin GoogleTest to a stable release with checksum verification.

Using refs/heads/main.zip at line 127 is mutable and non-reproducible; builds may change unexpectedly and integrity is not verified. Use the latest stable release v1.17.0 with SHA256 checksum validation:

Suggested fix
     FetchContent_Declare(
       googletest
-      URL https://github.com/google/googletest/archive/refs/heads/main.zip
+      URL https://github.com/google/googletest/archive/refs/tags/v1.17.0.zip
+      URL_HASH SHA256=40d4ec942217dcc84a9ebe2a68584ada7d4a33a8ee958755763278ea1c5e18ff
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CMakeLists.txt` around lines 125 - 131, Replace the mutable main branch URL
in the FetchContent_Declare for googletest with the stable release tag (use URL
pointing to v1.17.0, e.g.
https://github.com/google/googletest/archive/refs/tags/v1.17.0.zip) and add
URL_HASH SHA256 with the official checksum to enable integrity verification;
update the FetchContent_Declare block (the lines around FetchContent_Declare,
URL and FetchContent_MakeAvailable and keep set(gtest_force_shared_crt ON CACHE
BOOL "" FORCE) unchanged) so the project fetches the pinned v1.17.0 archive with
the SHA256 check.

Comment thread tests/server_tests.cpp
Comment on lines +124 to +125
static_cast<void>(recv(sock, buffer.data(), AUTH_OK_LEN, 0));
static_cast<void>(recv(sock, buffer.data(), READY_LEN, 0));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and get its size
wc -l tests/server_tests.cpp

Repository: poyrazK/cloudSQL

Length of output: 86


🏁 Script executed:

# Extract the specific lines mentioned in the review
echo "=== Lines 124-125 ==="
sed -n '120,130p' tests/server_tests.cpp

echo -e "\n=== Lines 145-146 ==="
sed -n '140,150p' tests/server_tests.cpp

echo -e "\n=== Lines 227-228 ==="
sed -n '220,235p' tests/server_tests.cpp

Repository: poyrazK/cloudSQL

Length of output: 1967


🏁 Script executed:

# Search for all recv/send calls with discarded results in this file
echo "=== static_cast<void>(recv/send) calls ==="
rg -n 'static_cast<void>\((recv|send)\(' tests/server_tests.cpp

echo -e "\n=== ssize_t recv calls ==="
rg -n 'const ssize_t.*recv\(' tests/server_tests.cpp

echo -e "\n=== Other recv/send usage patterns ==="
rg -n -B2 -A2 'recv\(|send\(' tests/server_tests.cpp | head -100

Repository: poyrazK/cloudSQL

Length of output: 6857


Add validation for all recv() calls to handle partial reads in protocol parsing.

Lines 124, 125, 227, and 228 discard recv() return values without validating that the full frame was read in a single call. TCP can return partial reads; a multi-byte message may require multiple recv() calls. This causes these protocol tests to silently mis-parse responses under congested network conditions.

Additionally, line 144 assigns the result to a variable then discards it without checking (line 145: (void)n_d;), creating inconsistency with other single-byte reads that do validate (e.g., lines 134, 153).

Implement a loop-based helper that accumulates bytes until the full message length is received, and use it for all frame reads:

Suggested implementation
+bool recv_exact(int sock, void* buf, size_t len) {
+    auto* p = static_cast<char*>(buf);
+    size_t total = 0;
+    while (total < len) {
+        const ssize_t n = recv(sock, p + total, len - total, 0);
+        if (n <= 0) return false;
+        total += static_cast<size_t>(n);
+    }
+    return true;
+}
...
-        static_cast<void>(recv(sock, buffer.data(), AUTH_OK_LEN, 0));
-        static_cast<void>(recv(sock, buffer.data(), READY_LEN, 0));
+        ASSERT_TRUE(recv_exact(sock, buffer.data(), AUTH_OK_LEN));
+        ASSERT_TRUE(recv_exact(sock, buffer.data(), READY_LEN));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static_cast<void>(recv(sock, buffer.data(), AUTH_OK_LEN, 0));
static_cast<void>(recv(sock, buffer.data(), READY_LEN, 0));
bool recv_exact(int sock, void* buf, size_t len) {
auto* p = static_cast<char*>(buf);
size_t total = 0;
while (total < len) {
const ssize_t n = recv(sock, p + total, len - total, 0);
if (n <= 0) return false;
total += static_cast<size_t>(n);
}
return true;
}
ASSERT_TRUE(recv_exact(sock, buffer.data(), AUTH_OK_LEN));
ASSERT_TRUE(recv_exact(sock, buffer.data(), READY_LEN));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/server_tests.cpp` around lines 124 - 125, The tests currently drop
recv() return values for multi-byte frames (AUTH_OK_LEN, READY_LEN and the other
two spots) and also assign then discard n_d for a single-byte read; replace
these fragile reads with a small helper (e.g., recv_all or read_full) that loops
calling recv() until the requested length is accumulated or an error/EOF occurs,
returning total bytes read or -1 on error; use this helper everywhere the code
now calls recv(sock, buffer.data(), AUTH_OK_LEN, 0) / recv(..., READY_LEN, 0)
and where n_d is assigned so you actually check the return, and ensure failures
cause the test to fail (or abort) instead of silently proceeding.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant