Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 26 additions & 34 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,42 +120,34 @@ endif()

# Build test executables
if (BUILD_TESTS)
# Create test executable
add_executable(sqlEngine_tests tests/cloudSQL_tests.cpp)
target_link_libraries(sqlEngine_tests PRIVATE sqlEngineCore)

# Create lock manager unit tests
add_executable(lock_manager_tests tests/lock_manager_tests.cpp)
target_link_libraries(lock_manager_tests PRIVATE sqlEngineCore)

# Create server unit tests
add_executable(server_tests tests/server_tests.cpp)
target_link_libraries(server_tests PRIVATE sqlEngineCore)

# Create transaction manager unit tests
add_executable(transaction_manager_tests tests/transaction_manager_tests.cpp)
target_link_libraries(transaction_manager_tests PRIVATE sqlEngineCore)

# Create statement unit tests
add_executable(statement_tests tests/statement_tests.cpp)
target_link_libraries(statement_tests PRIVATE sqlEngineCore)

# Create recovery unit tests
add_executable(recovery_tests tests/recovery_tests.cpp)
target_link_libraries(recovery_tests PRIVATE sqlEngineCore)

# Check if GoogleTest is available
find_package(GTest QUIET)
if (GTest_FOUND)
target_link_libraries(sqlEngine_tests PRIVATE GTest::GTest GTest::GTestMain)
include(GoogleTest)
gtest_discover_tests(sqlEngine_tests)
endif()
enable_testing()
include(FetchContent)
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)
Comment on lines +125 to +131
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.


macro(add_cloudsql_test name source)
add_executable(${name} ${source})
target_link_libraries(${name} PRIVATE sqlEngineCore GTest::gtest_main)
add_test(NAME ${name} COMMAND ${name})
endmacro()

add_cloudsql_test(sqlEngine_tests tests/cloudSQL_tests.cpp)
add_cloudsql_test(lock_manager_tests tests/lock_manager_tests.cpp)
add_cloudsql_test(server_tests tests/server_tests.cpp)
add_cloudsql_test(transaction_manager_tests tests/transaction_manager_tests.cpp)
add_cloudsql_test(statement_tests tests/statement_tests.cpp)
add_cloudsql_test(recovery_tests tests/recovery_tests.cpp)
add_cloudsql_test(recovery_manager_tests tests/recovery_manager_tests.cpp)
add_cloudsql_test(buffer_pool_tests tests/buffer_pool_tests.cpp)

add_custom_target(run-tests
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/sqlEngine_tests
DEPENDS sqlEngine_tests
COMMENT "Running cloudSQL tests")
COMMAND ${CMAKE_CTEST_COMMAND}
COMMENT "Running all tests via CTest")
endif()

# Installation
Expand Down
176 changes: 33 additions & 143 deletions tests/buffer_pool_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,170 +3,60 @@
* @brief Unit tests for Buffer Pool Manager
*/

#include <gtest/gtest.h>

#include <cstdint>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>

#include "storage/buffer_pool_manager.hpp"
#include "storage/lru_replacer.hpp"
#include "storage/page.hpp"
#include "storage/storage_manager.hpp"
#include "test_utils.hpp"

using namespace cloudsql::storage;

namespace {

using cloudsql::tests::tests_failed;
using cloudsql::tests::tests_passed;

TEST(LRUReplacer_Basic) {
LRUReplacer replacer(3);
uint32_t victim_frame = 0;

replacer.unpin(1);
replacer.unpin(2);
replacer.unpin(3);
EXPECT_EQ(replacer.size(), 3u);

EXPECT_TRUE(replacer.victim(&victim_frame));
EXPECT_EQ(victim_frame, 1u);
EXPECT_EQ(replacer.size(), 2u);

replacer.unpin(4);
EXPECT_EQ(replacer.size(), 3u);

EXPECT_TRUE(replacer.victim(&victim_frame));
EXPECT_EQ(victim_frame, 2u);
EXPECT_EQ(replacer.size(), 2u);

replacer.pin(3);
EXPECT_EQ(replacer.size(), 1u);

replacer.unpin(3);
EXPECT_EQ(replacer.size(), 2u);

EXPECT_TRUE(replacer.victim(&victim_frame));
EXPECT_EQ(victim_frame, 4u);

EXPECT_TRUE(replacer.victim(&victim_frame));
EXPECT_EQ(victim_frame, 3u);
EXPECT_EQ(replacer.size(), 0u);

EXPECT_FALSE(replacer.victim(&victim_frame));
}

TEST(BufferPoolManager_Basic) {
static_cast<void>(std::remove("./test_data/bpm_test.db"));
StorageManager disk_manager("./test_data");
BufferPoolManager bpm(2, disk_manager);

const std::string file_name = "bpm_test.db";
uint32_t page_id0 = 0;
Page* page0 = bpm.new_page(file_name, &page_id0);
EXPECT_TRUE(page0 != nullptr);
EXPECT_EQ(page_id0, 0u);

EXPECT_TRUE(bpm.unpin_page(file_name, page_id0, true));

page0 = bpm.fetch_page(file_name, page_id0);
EXPECT_TRUE(page0 != nullptr);
EXPECT_TRUE(page0->is_dirty());
EXPECT_TRUE(bpm.unpin_page(file_name, page_id0, false));

uint32_t page_id1 = 1;
Page* page1 = bpm.new_page(file_name, &page_id1);
EXPECT_TRUE(page1 != nullptr);
constexpr size_t HELLO_LEN = 6;

uint32_t page_id2 = 2;
Page* page2 = bpm.new_page(file_name, &page_id2);
EXPECT_TRUE(page2 != nullptr);
TEST(BufferPoolTests, Basic) {
const std::string filename = "test.db";
static_cast<void>(std::remove(filename.c_str()));

uint32_t page_id3 = 3;
Page* page3 = bpm.new_page(file_name, &page_id3);
EXPECT_FALSE(page3 != nullptr);

bpm.unpin_page(file_name, page_id1, false);
bpm.unpin_page(file_name, page_id2, true);

page3 = bpm.new_page(file_name, &page_id3);
EXPECT_TRUE(page3 != nullptr);

bpm.flush_page(file_name, page_id3);
bpm.flush_all_pages();
bpm.unpin_page(file_name, page_id3, false);

bpm.delete_page(file_name, page_id2);
}

TEST(BufferPoolManager_Eviction) {
static_cast<void>(std::remove("./test_data/bpm_eviction.db"));
StorageManager disk_manager("./test_data");
StorageManager disk_manager(".");
BufferPoolManager bpm(3, disk_manager);
const std::string file = "bpm_eviction.db";

uint32_t id1 = 1, id2 = 2, id3 = 3, id4 = 4;
Page* p1 = bpm.new_page(file, &id1);
Page* p2 = bpm.new_page(file, &id2);
Page* p3 = bpm.new_page(file, &id3);

EXPECT_TRUE(p1 != nullptr);
EXPECT_TRUE(p2 != nullptr);
EXPECT_TRUE(p3 != nullptr);

bpm.unpin_page(file, id1, true);
bpm.unpin_page(file, id2, false);
bpm.unpin_page(file, id3, false);
EXPECT_TRUE(bpm.open_file(filename));

Page* p4 = bpm.new_page(file, &id4);
EXPECT_TRUE(p4 != nullptr);
const uint32_t page_id1 = 0;
Page* const page1 = bpm.new_page(filename, &page_id1);
ASSERT_NE(page1, nullptr);
EXPECT_EQ(page_id1, 0);

bpm.unpin_page(file, id4, false);
std::memcpy(page1->get_data(), "Hello", HELLO_LEN);
bpm.unpin_page(filename, page_id1, true);

p1 = bpm.fetch_page(file, id1);
EXPECT_TRUE(p1 != nullptr);
bpm.unpin_page(file, id1, false);
const uint32_t page_id2 = 1;
const Page* const page2 = bpm.new_page(filename, &page_id2);
ASSERT_NE(page2, nullptr);
EXPECT_EQ(page_id2, 1);
bpm.unpin_page(filename, page_id2, false);

bpm.delete_page(file, id1);
bpm.delete_page(file, id2);
bpm.delete_page(file, id3);
bpm.delete_page(file, id4);
}

TEST(BufferPoolManager_EdgeCases) {
static_cast<void>(std::remove("./test_data/bpm_edge.db"));
StorageManager disk_manager("./test_data");
BufferPoolManager bpm(1, disk_manager);
const std::string file = "bpm_edge.db";

EXPECT_FALSE(bpm.unpin_page(file, 999, false));
EXPECT_FALSE(bpm.flush_page(file, 999));
EXPECT_TRUE(bpm.delete_page(file, 999));

uint32_t id = 1;
Page* p = bpm.new_page(file, &id);
EXPECT_TRUE(p != nullptr);
EXPECT_FALSE(bpm.delete_page(file, id)); // Pinned
const uint32_t page_id3 = 2;
const Page* const page3 = bpm.new_page(filename, &page_id3);
ASSERT_NE(page3, nullptr);
EXPECT_EQ(page_id3, 2);
bpm.unpin_page(filename, page_id3, false);

// new page again with same ID
Page* p_dup = bpm.new_page(file, &id);
EXPECT_TRUE(p_dup == nullptr);
// Fetch page 1 again
Page* const page1_fetch = bpm.fetch_page(filename, page_id1);
ASSERT_NE(page1_fetch, nullptr);
EXPECT_STREQ(page1_fetch->get_data(), "Hello");
bpm.unpin_page(filename, page_id1, false);

bpm.unpin_page(file, id, false);
static_cast<void>(bpm.close_file(filename));
static_cast<void>(std::remove(filename.c_str()));
}

} // namespace

int main() {
std::cout << "Buffer Pool Unit Tests\n";
std::cout << "======================\n";

RUN_TEST(LRUReplacer_Basic);
RUN_TEST(BufferPoolManager_Basic);
RUN_TEST(BufferPoolManager_Eviction);
RUN_TEST(BufferPoolManager_EdgeCases);

std::cout << "\nResults: \n" << tests_passed << " passed, \n" << tests_failed << " failed\n";
return (tests_failed > 0);
}
Loading