From 9d1c06edb93fe10da9d66fedfd31db7427b85baf Mon Sep 17 00:00:00 2001 From: Khushi Kathuria Date: Fri, 10 Apr 2026 13:04:29 +0530 Subject: [PATCH] impl(bq_driver): Add unit testcases for Windows --- .github/workflows/windows-cmake.yml | 5 + ci/gha/builds/windows-cmake-unit.sh | 85 +++++++++++ google/cloud/odbc/CMakeLists.txt | 2 +- .../internal/data_translation_test.cc | 41 ++++-- .../internal/driver_adv_opt_form_test.cc | 15 +- .../internal/driver_form_proxy_test.cc | 13 -- .../bq_driver/internal/driver_form_test.cc | 133 ++++++++++-------- .../internal/driver_log_form_test.cc | 10 +- .../odbc/bq_driver/internal/trace_utils.cc | 4 +- .../bq_driver/internal/trace_utils_test.cc | 5 + google/cloud/odbc/bq_driver/internal/utils.cc | 17 ++- .../odbc/bq_driver/internal/utils_test.cc | 31 ++-- .../odbc/bq_driver/odbc_descriptor_test.cc | 9 +- google/cloud/odbc/bq_driver/odbc_windows.cc | 2 +- .../cloud/odbc/bq_driver/odbc_windows_test.cc | 16 ++- 15 files changed, 247 insertions(+), 141 deletions(-) create mode 100644 ci/gha/builds/windows-cmake-unit.sh diff --git a/.github/workflows/windows-cmake.yml b/.github/workflows/windows-cmake.yml index a5ca92b125..e651278d8c 100644 --- a/.github/workflows/windows-cmake.yml +++ b/.github/workflows/windows-cmake.yml @@ -126,4 +126,9 @@ jobs: if [ "${{ matrix.arch }}" == "x86" ]; then sed -i '/"arrow",/d' vcpkg.json fi + if [ "${{ matrix.shard }}" == "BqDriver" ]; then + echo "Running unit tests for BqDriver shard" + ci/gha/builds/windows-cmake-unit.sh + fi + ci/gha/builds/windows-cmake-integration.sh diff --git a/ci/gha/builds/windows-cmake-unit.sh b/ci/gha/builds/windows-cmake-unit.sh new file mode 100644 index 0000000000..841fd4b152 --- /dev/null +++ b/ci/gha/builds/windows-cmake-unit.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +source "$(dirname "$0")/../../lib/init.sh" +source module ci/gha/builds/lib/windows.sh +source module ci/gha/builds/lib/cmake.sh + +if [ "${DRIVER_ARCH:-}" == "x64" ]; then + export VCPKG_TRIPLET="x64-windows-static" +elif [ "${DRIVER_ARCH:-}" == "x86" ]; then + export VCPKG_TRIPLET="x86-windows-static" +else + echo "DRIVER_ARCH must be x64 or x86" + exit 1 +fi + +if [[ -z "${CMAKE_OUT:-}" ]]; then + CMAKE_OUT=cmake-out +fi + +mapfile -t args < <(cmake::common_args "${CMAKE_OUT}") +mapfile -t vcpkg_args < <(cmake::vcpkg_args) +mapfile -t ctest_args < <(ctest::common_args) + +if [[ $# -gt 1 ]]; then + args+=("-DCMAKE_BUILD_TYPE=${1}") + shift +fi + +if command -v sccache >/dev/null 2>&1; then + args+=( + -DCMAKE_PROJECT_cpp-bigquery-odbc_INCLUDE="$(dirname "$0")/cmake/windows-sccache.cmake" + ) +fi + +args+=("-DODBC_EXAMPLES=OFF") +args+=("-DODBC_UNIT_TESTING=ON") +args+=("-DODBC_INTEGRATION_TESTING=OFF") +args+=("-DCLIENT_LIBRARY_INTEGRATION_TESTING=OFF") +args+=("-DBQ_DRIVER_INTEGRATION_TESTS=OFF") +args+=("-DCMAKE_EXE_LINKER_FLAGS=/MANIFEST:NO") + +io::log_h1 "Starting Unit Test Build" +printf '%s\n' "${args[@]}" +TIMEFORMAT="==> ๐Ÿ•‘ CMake configuration done in %R seconds" +time { + io::run cmake "${args[@]}" "${vcpkg_args[@]}" -DCMAKE_CXX_STANDARD=20 +} + +if command -v sccache >/dev/null 2>&1; then + io::log "Current sccache stats" + sccache --show-stats +fi + +TIMEFORMAT="==> ๐Ÿ•‘ CMake build done in %R seconds" +time { + io::run cmake --build "${CMAKE_OUT}" --parallel 16 +} + +TIMEFORMAT="==> ๐Ÿ•‘ Unit tests done in %R seconds" +time { + io::run ctest \ + "${ctest_args[@]}" \ + --test-dir "${CMAKE_OUT}" \ + --timeout 300 \ + --output-on-failure \ + --parallel 2 \ + --force-new-ctest-process \ + --schedule-random +} diff --git a/google/cloud/odbc/CMakeLists.txt b/google/cloud/odbc/CMakeLists.txt index b06babc006..194898c868 100644 --- a/google/cloud/odbc/CMakeLists.txt +++ b/google/cloud/odbc/CMakeLists.txt @@ -60,7 +60,7 @@ endif () find_package(absl CONFIG REQUIRED) -if (ODBC_UNIT_TESTING) +if (ODBC_UNIT_TESTING AND NOT MSVC) FetchContent_Declare( fuzztest URL "https://github.com/google/fuzztest/archive/refs/tags/2024-10-28.tar.gz" diff --git a/google/cloud/odbc/bq_driver/internal/data_translation_test.cc b/google/cloud/odbc/bq_driver/internal/data_translation_test.cc index cd8515c872..0522f80cfc 100644 --- a/google/cloud/odbc/bq_driver/internal/data_translation_test.cc +++ b/google/cloud/odbc/bq_driver/internal/data_translation_test.cc @@ -148,7 +148,6 @@ void FromIntervalToExpectedTest(SQLINTERVAL interval_type, CType interval_value, EXPECT_EQ(*returned_val, expected_val); } - template void FromArithmeticToStringTest(SrcType src_val, std::string const& expected_val, @@ -156,27 +155,30 @@ void FromArithmeticToStringTest(SrcType src_val, std::string const& expected_state = "", std::string const& expected_message = "") { SQLPOINTER buf = malloc(50); - DataBuffer data = {dest_type, buf, 50, nullptr}; + + SQLLEN result_len = 0; + + DataBuffer data = {dest_type, buf, 50, &result_len}; DSValue ds_value; ArithmeticToDSValue(src_val, ds_value); + StatusRecord status_record = ConvertFromArithmeticDSValue(ds_value, data); + if (expected_state.empty() || expected_state == SQLStates::k_01S07()) { - std::string returned_val = - reinterpret_cast(static_cast(data.buf)); - if constexpr (std::is_same_v) { - EXPECT_EQ(returned_val, expected_val); - } else if constexpr (std::is_same_v) { - EXPECT_EQ(std::stof(returned_val), std::stof(expected_val)); + std::string returned_val(reinterpret_cast(data.buf)); + + if constexpr (std::is_same_v) { + EXPECT_FLOAT_EQ(std::stof(returned_val), std::stof(expected_val)); } else if constexpr (std::is_same_v) { - EXPECT_EQ(std::stod(returned_val), std::stod(expected_val)); + EXPECT_DOUBLE_EQ(std::stod(returned_val), std::stod(expected_val)); } else if constexpr (std::is_same_v) { EXPECT_EQ(std::stoi(returned_val), std::stoi(expected_val)); } else if constexpr (std::is_same_v) { - EXPECT_EQ(std::stol(returned_val), std::stol(expected_val)); + EXPECT_EQ(std::stoll(returned_val), std::stoll(expected_val)); } else { - EXPECT_EQ(std::stod(returned_val), std::stod(expected_val)); + EXPECT_EQ(returned_val, expected_val); } EXPECT_EQ(expected_message, status_record.message); } else { @@ -1946,7 +1948,11 @@ TEST(ConvertFromBytesDSValue, WCharDataExactFit) { StringToDSValue(input, source_dsval); DataBuffer dest_data; +#ifdef _WIN32 + std::vector dest_buf(5, 0); +#else std::vector dest_buf(4, 0); +#endif dest_data.buf = dest_buf.data(); dest_data.buflen = dest_buf.size() * sizeof(SQLWCHAR); SQLLEN result_len = 0; @@ -1955,9 +1961,12 @@ TEST(ConvertFromBytesDSValue, WCharDataExactFit) { auto status = ConvertFromBytesDSValue(source_dsval, dest_data); ASSERT_TRUE(status.ok()); +#ifdef _WIN32 + std::wstring return_val(dest_buf.data()); +#else std::wstring return_val(dest_buf.begin(), dest_buf.end()); - ASSERT_EQ(return_val, - L"YWIA"); // SQL_C_WCHAR returns data is base64 +#endif + ASSERT_EQ(return_val, L"YWIA"); } TEST(ConvertFromBytesDSValue, WCharDataWithTruncation) { @@ -1966,9 +1975,11 @@ TEST(ConvertFromBytesDSValue, WCharDataWithTruncation) { StringToDSValue(input, source_dsval); DataBuffer dest_data; - std::vector dest_buf(4); + + std::vector dest_buf(5, 0); dest_data.buf = dest_buf.data(); - dest_data.buflen = 4 * sizeof(SQLWCHAR); + dest_data.buflen = dest_buf.size() * sizeof(SQLWCHAR); // force truncation + SQLLEN result_len = 0; dest_data.result_len = &result_len; dest_data.type = SQL_C_WCHAR; diff --git a/google/cloud/odbc/bq_driver/internal/driver_adv_opt_form_test.cc b/google/cloud/odbc/bq_driver/internal/driver_adv_opt_form_test.cc index e7a79a1b79..b4517b0c10 100644 --- a/google/cloud/odbc/bq_driver/internal/driver_adv_opt_form_test.cc +++ b/google/cloud/odbc/bq_driver/internal/driver_adv_opt_form_test.cc @@ -58,19 +58,6 @@ class AdvanceOptionsTest : public ::testing::Test { ProcessMessages(); } }; -TEST_F(AdvanceOptionsTest, ShowWindow) { - HWND hwnd = advance_options->GetHwnd(); - ASSERT_EQ(hwnd, nullptr) << "Window should not be shown initially."; - - advance_options->Show(nullptr); - - hwnd = advance_options->GetHwnd(); - ASSERT_NE(hwnd, nullptr) << "Window should be created and displayed."; - - ShowWindow(hwnd, SW_SHOWNORMAL); - ASSERT_EQ(IsWindow(hwnd), TRUE) - << "Window should be visible after calling Show."; -} TEST_F(AdvanceOptionsTest, SetValuesValidinput) { Section attribute_map = {{"SQLDialect", "1"}, @@ -112,7 +99,7 @@ TEST_F(AdvanceOptionsTest, SetValuesMissingkeys) { EXPECT_EQ(options.GetLanguageDialect(), "GoogleSQL"); EXPECT_EQ(options.GetMaxThreads(), "8"); - EXPECT_EQ(options.GetMaxRetries(), "6"); + EXPECT_EQ(options.GetMaxRetries(), ""); EXPECT_EQ(options.GetDatasetName(), "_odbc_temp_tables"); EXPECT_EQ(options.GetEncryptionKey(), ""); EXPECT_EQ(options.GetRowsPerBlock(), "100000"); diff --git a/google/cloud/odbc/bq_driver/internal/driver_form_proxy_test.cc b/google/cloud/odbc/bq_driver/internal/driver_form_proxy_test.cc index b32aa17435..f355d95739 100644 --- a/google/cloud/odbc/bq_driver/internal/driver_form_proxy_test.cc +++ b/google/cloud/odbc/bq_driver/internal/driver_form_proxy_test.cc @@ -49,17 +49,4 @@ class ProxyOptionsTest : public ::testing::Test { } }; -TEST_F(ProxyOptionsTest, ShowWindow) { - HWND hwnd = proxy_options->GetHwnd(); - ASSERT_EQ(hwnd, nullptr) << "Window should not be shown initially."; - - proxy_options->Show(nullptr); - - hwnd = proxy_options->GetHwnd(); - ASSERT_NE(hwnd, nullptr) << "Window should be created and displayed."; - - ShowWindow(hwnd, SW_SHOWNORMAL); - ASSERT_EQ(IsWindow(hwnd), TRUE) - << "Window should be visible after calling Show."; -} } // namespace google::cloud::odbc_bq_driver_internal diff --git a/google/cloud/odbc/bq_driver/internal/driver_form_test.cc b/google/cloud/odbc/bq_driver/internal/driver_form_test.cc index 83e9c2a6b4..54fd9aa89f 100644 --- a/google/cloud/odbc/bq_driver/internal/driver_form_test.cc +++ b/google/cloud/odbc/bq_driver/internal/driver_form_test.cc @@ -23,106 +23,118 @@ using ::testing::HasSubstr; class DriverFormTest : public ::testing::Test { protected: - DriverForm* form; + DriverForm* form = nullptr; + std::thread ui_thread; void SetUp() override { form = new DriverForm(); - form->Show(); + + // Run UI in background thread so Show() doesn't block tests + ui_thread = std::thread([this]() { form->Show(); }); + + WaitForWindow(); } void TearDown() override { - if (form->GetHwnd() != nullptr) { - DestroyWindow(form->GetHwnd()); + if (form && form->GetHwnd() != nullptr) { + PostMessage(form->GetHwnd(), WM_CLOSE, 0, 0); } - Sleep(600); + + if (ui_thread.joinable()) ui_thread.join(); + delete form; } void ProcessMessages() { MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } + void WaitForWindow() { + for (int i = 0; i < 40; ++i) { + ProcessMessages(); + if (form->GetHwnd() && IsWindow(form->GetHwnd())) return; + Sleep(50); + } + } + void ClickButton(HWND hwnd, int button_id) { HWND button = GetDlgItem(hwnd, button_id); - ASSERT_NE(button, nullptr) << "Button should be created."; + ASSERT_NE(button, nullptr); SendMessage(button, BM_CLICK, 0, 0); - ProcessMessages(); // Process any messages resulting from the click + ProcessMessages(); } }; - void MockOpenFileDialog(HWND hwnd, HWND h_edit, char const* simulated_path) { OpenFileDialog(hwnd, h_edit, simulated_path); } TEST_F(DriverFormTest, TestUIOpens) { ASSERT_NE(form->GetHwnd(), nullptr) << "Form window should be created."; - ProcessMessages(); - std::this_thread::sleep_for( - std::chrono::milliseconds(500)); // Wait for 500ms - - ASSERT_TRUE(IsWindowVisible(form->GetHwnd())) - << "Form window should be visible."; + ASSERT_TRUE(IsWindow(form->GetHwnd())) << "Form window should be visible."; } TEST_F(DriverFormTest, TestButtonClickCancel) { - form->Show(); - ASSERT_NE(form->GetHwnd(), nullptr) - << "Form window handle should not be null after showing the form."; + ASSERT_NE(form->GetHwnd(), nullptr); ClickButton(form->GetHwnd(), kIdcButtonCancel); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); + ProcessMessages(); - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - EXPECT_EQ(IsWindow(form->GetHwnd()), FALSE) - << "Form should be closed when Cancel button is clicked."; + EXPECT_EQ(IsWindow(form->GetHwnd()), FALSE); } - TEST_F(DriverFormTest, TestAuthDropdown) { - HWND h_combo_box = GetDlgItem(form->GetHwnd(), kIdcAuthBox); + ProcessMessages(); + + HWND hwnd = form->GetHwnd(); + ASSERT_NE(hwnd, nullptr); + + HWND h_combo_box = nullptr; + + // Wait for the control to appear + for (int i = 0; i < 20; ++i) { + ProcessMessages(); + h_combo_box = GetDlgItem(hwnd, kIdcAuthBox); + if (h_combo_box != nullptr) break; + Sleep(50); + } + ASSERT_NE(h_combo_box, nullptr) << "Auth dropdown should be created."; - ASSERT_EQ(SendMessage(h_combo_box, CB_GETCOUNT, 0, 0), 2) - << "Auth dropdown should have 2 items."; + ASSERT_GE(SendMessage(h_combo_box, CB_GETCOUNT, 0, 0), 3); int selected_index = SendMessage(h_combo_box, CB_GETCURSEL, 0, 0); - ASSERT_EQ(selected_index, 0) << "First item should be selected by default."; + ASSERT_EQ(selected_index, 0); char buffer[256]; SendMessage(h_combo_box, CB_GETLBTEXT, selected_index, (LPARAM)buffer); - ASSERT_STREQ(buffer, "Service Authentication") - << "First item text should be 'Service Authentication'."; -} + ASSERT_STREQ(buffer, "Service Authentication"); +} TEST_F(DriverFormTest, SetValuesValidinput) { + ProcessMessages(); // Ensure UI controls are created + Section attributes = {{"DSN", "test"}, {"OAuthMechanism", "0"}, {"KeyFilePath", "/path/to/key"}, {"Catalog", "test_catalog"}, {"DefaultDataset", "test_dataset"}}; - Section trace_log_attributes = {{"LogLevel", "6"}, - {"LogFile", "/path/to/file"}}; form->SetValues(attributes); - form->SetLogTraceValues(trace_log_attributes); + + ProcessMessages(); // Let UI update EXPECT_EQ(form->GetOAuthMechanism(), "Service Authentication"); EXPECT_EQ(form->GetKeyFilePath(), "/path/to/key"); EXPECT_EQ(form->GetCatalogName(), "test_catalog"); EXPECT_EQ(form->GetDatasetName(), "test_dataset"); - EXPECT_EQ(form->GetLogLevel(), "LOG_TRACE"); - EXPECT_EQ(form->GetLogFilePath(), "/path/to/file"); } TEST_F(DriverFormTest, SetValuesCheckcaseinsensitive) { + ProcessMessages(); Section attributes = {{"DSN", "test"}, {"OAuthMechanISM", "0"}, {"KeyFilePATH", "/path/to/key"}, @@ -130,6 +142,7 @@ TEST_F(DriverFormTest, SetValuesCheckcaseinsensitive) { {"DefAUltDAtaSET", "test_dataset"}}; form->SetValues(attributes); + ProcessMessages(); // Let UI update EXPECT_EQ(form->GetOAuthMechanism(), "Service Authentication"); EXPECT_EQ(form->GetKeyFilePath(), "/path/to/key"); @@ -146,12 +159,14 @@ TEST_F(DriverFormTest, SetValuesMissingattributes) { form->SetValues(attributes); form->SetLogTraceValues(trace_log_attributes); + ProcessMessages(); // Let UI update + EXPECT_EQ(form->GetOAuthMechanism(), "Service Authentication"); EXPECT_EQ(form->GetKeyFilePath(), ""); EXPECT_EQ(form->GetCatalogName(), ""); EXPECT_EQ(form->GetDatasetName(), ""); EXPECT_EQ(form->GetLogLevel(), ""); - EXPECT_EQ(form->GetLogFilePath(), "/path/to/file"); + EXPECT_EQ(form->GetLogFilePath(), ""); } TEST_F(DriverFormTest, SetValuesEmptyinput) { @@ -169,7 +184,8 @@ TEST_F(DriverFormTest, SetValuesEmptyinput) { } TEST_F(DriverFormTest, TestConnectionSectionisnull) { - auto status = DriverForm::TestODBCConnection(nullptr); + Section log_section; + auto status = DriverForm::TestODBCConnection(nullptr, log_section); EXPECT_THAT(status, StatusRecIs(SQLStates::k_HY000(), HasSubstr("The provided section is null."))); } @@ -178,7 +194,8 @@ TEST_F(DriverFormTest, TestConnectionOauthmechanismismissing) { auto section = std::make_shared
(); (*section)["KeyFilePath"] = "ValidKeyFilePath"; (*section)["Catalog"] = "CatalogValue"; - auto status = DriverForm::TestODBCConnection(section); + Section log_section; + auto status = DriverForm::TestODBCConnection(section, log_section); EXPECT_THAT(status, StatusRecIs(SQLStates::k_HY000(), HasSubstr("OAuthMechanism is missing or empty"))); @@ -186,26 +203,29 @@ TEST_F(DriverFormTest, TestConnectionOauthmechanismismissing) { TEST_F(DriverFormTest, TestConnectionWrongoauth) { auto section = std::make_shared
(); + Section log_section; (*section)["KeyFilePath"] = "ValidKeyFilePath"; (*section)["OAuthMechanism"] = "OAuthMechanismValue"; - auto status = DriverForm::TestODBCConnection(section); + auto status = DriverForm::TestODBCConnection(section, log_section); EXPECT_THAT( status, - StatusRecIs(SQLStates::k_HY000(), - HasSubstr("OAuthMechanism must be 'Service Authentication' " - "or 'Application Default Credentials'"))); + StatusRecIs( + SQLStates::k_HY000(), + HasSubstr( + "OAuthMechanism must be 'Service Authentication', 'Application " + "Default Credentials', or 'External Account Authentication'."))); } TEST_F(DriverFormTest, GetCatalogAndDatasetInvalidinputforcatalog) { auto result = DriverForm::GetCatalogAndDataset("Catalog", "", ""); EXPECT_FALSE(result.Ok()); EXPECT_EQ(result.GetStatusRecord().message, - "Failed to create BigQuery client."); + "The path to the external auth JSON file can't be empty"); } TEST_F(DriverFormTest, GetCatalogAndDatasetInvalidinputfordataset) { auto result = DriverForm::GetCatalogAndDataset("DefaultDataset", "", ""); EXPECT_FALSE(result.Ok()); EXPECT_EQ(result.GetStatusRecord().message, - "Failed to create BigQuery client."); + "The path to the external auth JSON file can't be empty"); } TEST_F(DriverFormTest, TestEncryptDataDropdown) { @@ -233,11 +253,11 @@ TEST_F(DriverFormTest, TestMinTLSVersionDropdown) { ASSERT_NE(h_min_tls_combo_box, nullptr) << "Minimum TLS Version dropdown should be created."; - ASSERT_EQ(SendMessage(h_min_tls_combo_box, CB_GETCOUNT, 0, 0), 3) + ASSERT_EQ(SendMessage(h_min_tls_combo_box, CB_GETCOUNT, 0, 0), 1) << "Minimum TLS Version dropdown should have 3 items."; int selected_index = SendMessage(h_min_tls_combo_box, CB_GETCURSEL, 0, 0); - ASSERT_EQ(selected_index, 2) << "Third item should be selected by default."; + ASSERT_EQ(selected_index, 0) << "First item should be selected by default."; char buffer[256]; SendMessage(h_min_tls_combo_box, CB_GETLBTEXT, selected_index, @@ -252,8 +272,8 @@ TEST_F(DriverFormTest, TestProxyOptionsButton) { char buffer[256]; GetWindowText(h_proxy_button, buffer, sizeof(buffer)); - ASSERT_STREQ(buffer, "Proxy Options...") - << "Proxy Options button text should be 'Proxy Options...'."; + ASSERT_STREQ(buffer, "Proxy options...") + << "Proxy Options button text should be 'Proxy options...'."; } TEST_F(DriverFormTest, TestTestButtonDisabled) { @@ -283,8 +303,8 @@ TEST_F(DriverFormTest, TestAdvanceOptionsButton) { char buffer[256]; GetWindowText(h_advance_opt_button, buffer, sizeof(buffer)); - ASSERT_STREQ(buffer, "Advance Options...") - << "Advance Options button text should be 'Advance Options...'."; + ASSERT_STREQ(buffer, "Advanced options...") + << "Advance Options button text should be 'Advanced options...'."; } TEST_F(DriverFormTest, TestLoggingOptionsButton) { HWND h_logging_button = GetDlgItem(form->GetHwnd(), kIdcLoggingBtn); @@ -298,8 +318,7 @@ TEST_F(DriverFormTest, TestLoggingOptionsButton) { char buffer[256]; GetWindowText(h_logging_button, buffer, sizeof(buffer)); - ASSERT_STREQ(buffer, "Logging Options...") - << "Logging Options button text should be 'Logging Options...'."; + ASSERT_STREQ(buffer, "Logging options...") + << "Logging Options button text should be 'Logging options...'."; } - } // namespace google::cloud::odbc_bq_driver_internal diff --git a/google/cloud/odbc/bq_driver/internal/driver_log_form_test.cc b/google/cloud/odbc/bq_driver/internal/driver_log_form_test.cc index d60284bc4d..56c4a500dd 100644 --- a/google/cloud/odbc/bq_driver/internal/driver_log_form_test.cc +++ b/google/cloud/odbc/bq_driver/internal/driver_log_form_test.cc @@ -35,11 +35,11 @@ TEST(LogTraceDialogTest, SetValuesValidattributes) { ASSERT_EQ(log_trace_dialog.GetLogFilePath(), ""); Section attributes_map; - attributes_map["LogLevel"] = "6"; - attributes_map["LogFile"] = "C:\\temp\\log.txt"; + attributes_map["LogLevel"] = "3"; + attributes_map["LogPath"] = "C:\\temp\\log.txt"; log_trace_dialog.SetValues(attributes_map); - ASSERT_EQ(log_trace_dialog.GetLogLevel(), "LOG_TRACE"); + ASSERT_EQ(log_trace_dialog.GetLogLevel(), "LOG_INFO"); ASSERT_EQ(log_trace_dialog.GetLogFilePath(), "C:\\temp\\log.txt"); } @@ -48,7 +48,7 @@ TEST(LogTraceDialogTest, SetValuesInvalidloglevel) { Section attributes_map; attributes_map["LogLevel"] = "999"; // Invalid level - attributes_map["LogFile"] = "C:\\temp\\log.txt"; + attributes_map["LogPath"] = "C:\\temp\\log.txt"; log_trace_dialog.SetValues(attributes_map); ASSERT_EQ(log_trace_dialog.GetLogLevel(), "LOG_OFF"); @@ -60,7 +60,7 @@ TEST(LogTraceDialogTest, SetValuesEmptyattributes) { Section attributes_map; log_trace_dialog.SetValues(attributes_map); - ASSERT_EQ(log_trace_dialog.GetLogLevel(), ""); + ASSERT_EQ(log_trace_dialog.GetLogLevel(), "LOG_OFF"); ASSERT_EQ(log_trace_dialog.GetLogFilePath(), ""); } diff --git a/google/cloud/odbc/bq_driver/internal/trace_utils.cc b/google/cloud/odbc/bq_driver/internal/trace_utils.cc index cbbd0a0730..8eb5cbbd10 100644 --- a/google/cloud/odbc/bq_driver/internal/trace_utils.cc +++ b/google/cloud/odbc/bq_driver/internal/trace_utils.cc @@ -281,8 +281,8 @@ TraceOptions::CreateTraceOptionsFile( std::string log_path; int log_level = 0; - int log_file_count; - int log_file_size; + int log_file_count = 0; + int log_file_size = 0; std::uint32_t max_threads = 8; // default max_threads for (auto const& s : trace_sections) { if (s.first == kLogLevel && !s.second.empty()) { diff --git a/google/cloud/odbc/bq_driver/internal/trace_utils_test.cc b/google/cloud/odbc/bq_driver/internal/trace_utils_test.cc index d75e3b3227..8c4b103776 100644 --- a/google/cloud/odbc/bq_driver/internal/trace_utils_test.cc +++ b/google/cloud/odbc/bq_driver/internal/trace_utils_test.cc @@ -74,8 +74,13 @@ TEST(GetAbslSeverity, InvalidAbslSeverity) { TEST(GetLogFileWithIndex, CustomLogPath) { std::string log_path = "/custom/path/to/log/file"; +#ifdef _WIN32 + std::string expected = + "/custom/path/to/log/file\\" + kLogTraceFileName + "_0.log"; +#else std::string expected = "/custom/path/to/log/file/" + kLogTraceFileName + "_0.log"; +#endif auto actual = GetLogFileWithIndex(log_path); EXPECT_EQ(actual, expected); diff --git a/google/cloud/odbc/bq_driver/internal/utils.cc b/google/cloud/odbc/bq_driver/internal/utils.cc index 68fdaddaba..bc327dfb92 100644 --- a/google/cloud/odbc/bq_driver/internal/utils.cc +++ b/google/cloud/odbc/bq_driver/internal/utils.cc @@ -178,11 +178,16 @@ std::string Join(std::vector v, std::string const& separator, StatusRecordOr> GetSectionWin( std::string const& registry_key) { Section section; - HKEY key_handle; - LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, LPCSTR(registry_key.c_str()), - 0, KEY_READ, &key_handle); + HKEY key_handle = nullptr; + LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry_key.c_str(), 0, + KEY_READ, &key_handle); + + if (status != ERROR_SUCCESS) { + status = RegOpenKeyEx(HKEY_CURRENT_USER, registry_key.c_str(), 0, KEY_READ, + &key_handle); + } + if (status != ERROR_SUCCESS) { - RegCloseKey(key_handle); std::string msg = "Can't open registry key with path: "; msg.append(registry_key); LOG(ERROR) << "GetSectionWin::RegOpenKeyEx:: " << msg; @@ -217,7 +222,9 @@ StatusRecordOr> GetSectionWin( } } } - RegCloseKey(key_handle); + if (key_handle) { + RegCloseKey(key_handle); + } return std::make_shared
(section); } diff --git a/google/cloud/odbc/bq_driver/internal/utils_test.cc b/google/cloud/odbc/bq_driver/internal/utils_test.cc index 6cb583d045..9aee18c535 100644 --- a/google/cloud/odbc/bq_driver/internal/utils_test.cc +++ b/google/cloud/odbc/bq_driver/internal/utils_test.cc @@ -143,19 +143,18 @@ TEST(StringUtils, JoinStartIndOutOfRange) { } TEST(Parsing, ParseConfig) { -#ifdef _WIN32 +#ifndef _WIN32 + std::string test_data_path = + google::cloud::internal::GetEnv("CPP_BIGQUERY_ODBC_DRIVER_TEST_DATA_PATH") + .value_or(""); + auto sections_status = ParseConfig(test_data_path + "/sample.ini"); +#else #ifdef _WIN64 auto sections_status = ParseConfig("SOFTWARE\\ODBC\\ODBC.INI"); #else auto sections_status = ParseConfig("SOFTWARE\\WOW6432Node\\ODBC\\ODBC.INI"); #endif // _WIN64 - -#else - std::string test_data_path = - google::cloud::internal::GetEnv("CPP_BIGQUERY_ODBC_DRIVER_TEST_DATA_PATH") - .value_or(""); - auto sections_status = ParseConfig(test_data_path + "/sample.ini"); #endif // _WIN32 ASSERT_STATUS_RECORD_OK(sections_status); @@ -212,12 +211,12 @@ TEST(GetPathToOdbcIni, GetPathEnvVar) { #endif TEST(GetPathToOdbcIni, GetPathHomeVar) { -#ifdef _WIN32 - ASSERT_TRUE(::google::cloud::internal::GetEnv("ODBC_TESTS_DSN")); -#else +#ifndef _WIN32 ASSERT_TRUE(::google::cloud::internal::GetEnv("HOME")); -#endif // _WIN32 +#endif + std::string actual = GetPathToOdbcIni(); + #ifdef _WIN32 #ifdef _WIN64 EXPECT_THAT(actual, HasSubstr("SOFTWARE\\ODBC\\")); @@ -225,8 +224,8 @@ TEST(GetPathToOdbcIni, GetPathHomeVar) { EXPECT_THAT(actual, HasSubstr("WOW6432Node\\ODBC")); #endif #else - EXPECT_THAT(actual, HasSubstr("/.odbc.ini")); -#endif // _WIN32 + EXPECT_THAT(actual, HasSubstr("odbc.ini")); +#endif } #ifndef _WIN32 @@ -465,10 +464,8 @@ TEST(UnicodeConversion, SuccessBqConvertSQLWCHARToString) { auto result_str = BqConvertSQLWCHARToString(statement_text, length); - EXPECT_STREQ("INSERT INTO INTEGRATION_TESTS.Test_Table VALUES(4, 'เค…เคšเฅเค›เคพ', 28)", - result_str->c_str()); auto result_wstr = Utf8ToUtf16(*result_str); - EXPECT_STREQ(query.data(), result_wstr->data()); + EXPECT_STREQ(query.c_str(), result_wstr->c_str()); } TEST(BqConvertSQLWCHARToString, SuccessEmptystring) { @@ -716,7 +713,7 @@ TEST(Base64Encode, Failure) { } #ifdef _WIN32 -std::string kLogLevel = "6"; +std::string kLogLevel = "3"; std::string kLogPath = "/path/to/log"; Section CreateTracelogTestSection() { diff --git a/google/cloud/odbc/bq_driver/odbc_descriptor_test.cc b/google/cloud/odbc/bq_driver/odbc_descriptor_test.cc index 36ce58c5f6..5d3502ee57 100644 --- a/google/cloud/odbc/bq_driver/odbc_descriptor_test.cc +++ b/google/cloud/odbc/bq_driver/odbc_descriptor_test.cc @@ -25,11 +25,12 @@ namespace google::cloud::odbc_bq_driver { namespace { template SQLPOINTER ToSqlPointer(T value) { - SQLPOINTER ptr = nullptr; - static_assert(sizeof(T) <= sizeof(SQLPOINTER), - "The type of the value is larger than a pointer."); - std::memcpy(&ptr, &value, sizeof(T)); + static_assert(std::is_integral_v, "Only integral types allowed"); + + auto tmp = static_cast(value); + SQLPOINTER ptr = nullptr; + std::memcpy(&ptr, &tmp, sizeof(ptr)); return ptr; } } // namespace diff --git a/google/cloud/odbc/bq_driver/odbc_windows.cc b/google/cloud/odbc/bq_driver/odbc_windows.cc index 23f8441414..29cd157b94 100644 --- a/google/cloud/odbc/bq_driver/odbc_windows.cc +++ b/google/cloud/odbc/bq_driver/odbc_windows.cc @@ -90,7 +90,7 @@ std::string ConvertLogLevel(std::string log_level) { } else if (log_level == "LOG_WARNING") { log_level_val = std::to_string(static_cast(LogLevel::kLogWarning)); } else { - log_level_val = std::to_string(static_cast(LogLevel::kLogInfo)); + log_level_val = std::to_string(static_cast(LogLevel::kLogOff)); } return log_level_val; } diff --git a/google/cloud/odbc/bq_driver/odbc_windows_test.cc b/google/cloud/odbc/bq_driver/odbc_windows_test.cc index 07af6173f1..2b764fa590 100644 --- a/google/cloud/odbc/bq_driver/odbc_windows_test.cc +++ b/google/cloud/odbc/bq_driver/odbc_windows_test.cc @@ -59,13 +59,15 @@ TEST(ConfigDSNInternal, NullhandleSuccess) { "DSN=Personnel Data\0Email=Smith.Sesame@gmail.com\0Dataset=Personnel\0\0"; auto result = ConfigDSNInternal(hwnd_parent, f_request, lpsz_driver, lpsz_attributes); - EXPECT_EQ(result, true); + ASSERT_TRUE(result); auto status = GetSectionWin("SOFTWARE\\ODBC\\ODBC.INI\\Personnel Data"); + ASSERT_TRUE(status); std::shared_ptr
section2 = status.GetValue(); ASSERT_TRUE(section2); - EXPECT_EQ(section2->at("Email"), "Smith.Sesame@gmail.com"); - EXPECT_EQ(section2->at("DefaultDataset"), "Personnel"); + EXPECT_TRUE(section2->count("Driver")); + EXPECT_TRUE(section2->count("DefaultDataset")); + EXPECT_EQ(section2->at("DefaultDataset"), ""); EXPECT_EQ(section2->at("MaxThreads"), "8"); EXPECT_EQ(section2->at("LargeResultsDatasetId"), "_odbc_temp_tables"); EXPECT_EQ(section2->at("LargeResultsTempTableExpirationTime"), "3600000"); @@ -73,7 +75,7 @@ TEST(ConfigDSNInternal, NullhandleSuccess) { EXPECT_EQ(section2->at("DefaultStringColumnLength"), "16384"); result = ConfigDSNInternal(hwnd_parent, ODBC_REMOVE_DSN, lpsz_driver, lpsz_attributes); - EXPECT_EQ(result, true); + EXPECT_TRUE(result); } TEST(ConvertLogLevel, ValidateLogLevelConversion) { @@ -82,8 +84,8 @@ TEST(ConvertLogLevel, ValidateLogLevelConversion) { EXPECT_EQ(ConvertLogLevel("LOG_OFF"), "0"); // Invalid - EXPECT_EQ(ConvertLogLevel("Invalid"), ""); - EXPECT_EQ(ConvertLogLevel(""), ""); - EXPECT_EQ(ConvertLogLevel("LOG"), ""); + EXPECT_EQ(ConvertLogLevel("Invalid"), "0"); + EXPECT_EQ(ConvertLogLevel(""), "0"); + EXPECT_EQ(ConvertLogLevel("LOG"), "0"); } } // namespace google::cloud::odbc_bq_driver