Skip to content
Merged
1 change: 1 addition & 0 deletions doc/changes/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ n/a

## Internal

* #64: Added GoogleTest, GoogleMock, and Google Benchmark support for v2 tests
* #57: Defined and enforced public v2 C++ coding style
* #51: Added agent and contributor guidance for v1/v2 development, SLC workflows, CI testing, and PR conventions
* #56: Restructured the developer guide and synchronized agent guidance
Expand Down
13 changes: 13 additions & 0 deletions doc/developer_guide/v2/v2_build_and_test.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,18 @@ The test suite covers the FlatBuffers protocol, Arrow support, JSON schemas,
queue implementations, and fuzz-target regression tests. For fuzzing-specific
commands, see the [v2 fuzzing guide](v2_fuzzing.md).

## Benchmarks

Build and run the waitable-queue benchmark with:

```bash
cd udf-runner-cpp/v2
bazel run //:waitable_queue_benchmark
```

Benchmark results are diagnostic measurements. CPU frequency, scheduler
activity, build mode, and system load can affect the results, so benchmarks
must not be used as deterministic pass/fail tests.

For static analysis and formatting checks, see the [v2 code-quality
guide](v2_code_quality.md).
12 changes: 12 additions & 0 deletions doc/developer_guide/v2/v2_coding_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ the libFuzzer entry point `LLVMFuzzerTestOneInput` keeps its required spelling.
- Add or update tests when changing behavior, public interfaces, parsing,
serialization, concurrency, or dependency boundaries.
- Prefer small, focused tests that make failures easy to diagnose.
- Write functional unit tests with GoogleTest `TEST` or `TEST_F` cases. Use
`ASSERT_*` for prerequisites and `EXPECT_*` for independent checks; use
`@googletest//:gtest_main` instead of a hand-written `main()`.
- GoogleMock is available through the GoogleTest dependency. Use it only to
verify meaningful interactions with collaborators, callbacks, or failure
boundaries. Do not add production abstractions solely to create a mock.
- Keep custom entry points for ELF inspection, dynamic-loading, include-order,
and other specialized tests where they make the test's purpose clearer.
- Use Google Benchmark for performance tests. Exclude setup and cleanup from
measured regions when appropriate, use `benchmark::DoNotOptimize` for values
that must remain observable, and do not make benchmarks depend on fixed
timing thresholds or a particular machine.
- Run the v2 build and tests, then the `clang-format` and `clang-tidy` checks
described in the [code quality guide](v2_code_quality.md).
- Do not suppress a static-analysis warning without documenting the reason at
Expand Down
36 changes: 29 additions & 7 deletions udf-runner-cpp/v2/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ cc_test(
name = "udf_protocol_test",
srcs = ["udf_protocol_test.cc"],
copts = ["-std=c++20"],
deps = [":udf_protocol"],
deps = [
":udf_protocol",
"@googletest//:gtest_main",
],
)

cc_binary(
Expand Down Expand Up @@ -179,7 +182,10 @@ cc_test(
name = "arrow_core_test",
srcs = ["arrow_core_test.cc"],
copts = ["-std=c++20"],
deps = [":arrow_core"],
deps = [
":arrow_core",
"@googletest//:gtest_main",
],
)

cc_test(
Expand All @@ -205,7 +211,10 @@ cc_test(
srcs = ["json_schema_validation_test.cc"],
data = ["//json_schema:all_schemas"],
copts = ["-std=c++17"],
deps = [":json_schema"],
deps = [
":json_schema",
"@googletest//:gtest_main",
],
)

cc_test(
Expand Down Expand Up @@ -288,7 +297,10 @@ cc_test(
name = "moodycamel_queues_test",
srcs = ["moodycamel_queues_test.cc"],
copts = ["-std=c++20"],
deps = [":moodycamel_queues"],
deps = [
":moodycamel_queues",
"@googletest//:gtest_main",
],
)

cc_binary(
Expand Down Expand Up @@ -321,7 +333,10 @@ cc_test(
name = "event_fd_test",
srcs = ["event_fd_test.cc"],
copts = ["-std=c++20"],
deps = [":event_fd"],
deps = [
":event_fd",
"@googletest//:gtest_main",
],
target_compatible_with = ["@platforms//os:linux"],
)

Expand All @@ -340,15 +355,22 @@ cc_test(
name = "waitable_queue_test",
srcs = ["waitable_queue_test.cc"],
copts = ["-std=c++20"],
deps = [":waitable_queue"],
deps = [
":waitable_queue",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
target_compatible_with = ["@platforms//os:linux"],
)

cc_test(
name = "waitable_queue_integration_test",
srcs = ["waitable_queue_integration_test.cc"],
copts = ["-std=c++20"],
deps = [":waitable_queue"],
deps = [
":waitable_queue",
"@googletest//:gtest_main",
],
tags = ["no-coverage"],
target_compatible_with = ["@platforms//os:linux"],
)
Expand Down
1 change: 1 addition & 0 deletions udf-runner-cpp/v2/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module(
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "flatbuffers", version = "25.2.10")
bazel_dep(name = "googletest", version = "1.15.0")
Comment thread
tkilias marked this conversation as resolved.
bazel_dep(name = "google_benchmark", version = "1.9.5")
bazel_dep(name = "rules_fuzzing", version = "0.8.0", dev_dependency = True)
bazel_dep(name = "bazel_sonarqube", version = "1.0.5")
Expand Down
10 changes: 5 additions & 5 deletions udf-runner-cpp/v2/arrow_core_test.cc
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#include <cassert>
#include <cstdint>
#include <memory>

#include <arrow/array/builder_primitive.h>
#include <gtest/gtest.h>

int main()
TEST(ArrowCoreTest, BuildsInt64Array)
{
arrow::Int64Builder builder;
assert(builder.Append(int64_t{42}).ok());
ASSERT_TRUE(builder.Append(int64_t{42}).ok());

std::shared_ptr<arrow::Array> array;
assert(builder.Finish(&array).ok());
assert(array->length() == 1);
ASSERT_TRUE(builder.Finish(&array).ok());
EXPECT_EQ(array->length(), 1);
}
103 changes: 48 additions & 55 deletions udf-runner-cpp/v2/event_fd_test.cc
Original file line number Diff line number Diff line change
@@ -1,36 +1,26 @@
#include <unistd.h>

#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <system_error>
#include <utility>

#include <exasol/udf/v2/event_fd.hpp>
#include <gtest/gtest.h>

namespace
{

void test_check(bool condition, const char* message)
{
if (!condition)
{
std::cerr << "event fd test failure: " << message << '\n';
std::abort();
}
}

template <typename Function>
void expect_system_error(Function&& function, std::errc expected, const char* message)
{
try
{
function();
test_check(false, message);
ADD_FAILURE() << message;
}
catch (const std::system_error& error)
{
test_check(error.code() == std::make_error_code(expected), "unexpected system error");
EXPECT_EQ(error.code(), std::make_error_code(expected));
}
}

Expand All @@ -44,52 +34,55 @@ void self_move_assign(Type& value)

} // namespace

int main()
TEST(EventFdTest, AccumulatesNotifications)
{
try
{
exasol::udf::v2::LinuxEventFd event_fd;
test_check(event_fd.native_handle() != -1, "eventfd construction failed");
exasol::udf::v2::LinuxEventFd event_fd;
ASSERT_NE(event_fd.native_handle(), -1);

event_fd.write_notification();
event_fd.write_notification();
test_check(event_fd.read_notification() == 2, "eventfd did not accumulate notifications");
expect_system_error([&event_fd] { event_fd.read_notification(); },
std::errc::resource_unavailable_try_again,
"empty eventfd read should report EAGAIN");
event_fd.write_notification();
event_fd.write_notification();
EXPECT_EQ(event_fd.read_notification(), 2);
}

const int moved_handle = event_fd.native_handle();
exasol::udf::v2::LinuxEventFd move_constructed(std::move(event_fd));
test_check(move_constructed.native_handle() == moved_handle,
"move construction changed the handle");
TEST(EventFdTest, RejectsReadWhenEmpty)
{
exasol::udf::v2::LinuxEventFd event_fd;
expect_system_error([&event_fd] { event_fd.read_notification(); },
std::errc::resource_unavailable_try_again,
"empty eventfd read should report EAGAIN");
}

exasol::udf::v2::LinuxEventFd move_assigned;
move_assigned = std::move(move_constructed);
test_check(move_assigned.native_handle() == moved_handle,
"move assignment changed the handle");
self_move_assign(move_assigned);
test_check(move_assigned.native_handle() == moved_handle,
"self move assignment changed the handle");
TEST(EventFdTest, SupportsMoveConstruction)
{
exasol::udf::v2::LinuxEventFd event_fd;
const int moved_handle = event_fd.native_handle();
exasol::udf::v2::LinuxEventFd move_constructed(std::move(event_fd));
EXPECT_EQ(move_constructed.native_handle(), moved_handle);
}

{
exasol::udf::v2::LinuxEventFd closed_event_fd;
::close(closed_event_fd.native_handle());
expect_system_error([&closed_event_fd] { closed_event_fd.read_notification(); },
std::errc::bad_file_descriptor,
"closed eventfd read should be rejected");
}
TEST(EventFdTest, SupportsMoveAssignmentAndSelfMove)
{
exasol::udf::v2::LinuxEventFd source;
const int moved_handle = source.native_handle();
exasol::udf::v2::LinuxEventFd move_assigned;
move_assigned = std::move(source);
EXPECT_EQ(move_assigned.native_handle(), moved_handle);
self_move_assign(move_assigned);
EXPECT_EQ(move_assigned.native_handle(), moved_handle);
}

{
exasol::udf::v2::LinuxEventFd closed_event_fd;
::close(closed_event_fd.native_handle());
expect_system_error([&closed_event_fd] { closed_event_fd.write_notification(); },
std::errc::bad_file_descriptor,
"closed eventfd write should be rejected");
}
}
catch (const std::exception& error)
{
std::cerr << "event fd test failure: " << error.what() << '\n';
return 1;
}
TEST(EventFdTest, RejectsReadOnClosedDescriptor)
{
exasol::udf::v2::LinuxEventFd closed_event_fd;
ASSERT_EQ(::close(closed_event_fd.native_handle()), 0);
expect_system_error([&closed_event_fd] { closed_event_fd.read_notification(); },
std::errc::bad_file_descriptor, "closed eventfd read should be rejected");
}

TEST(EventFdTest, RejectsWriteOnClosedDescriptor)
{
exasol::udf::v2::LinuxEventFd closed_event_fd;
ASSERT_EQ(::close(closed_event_fd.native_handle()), 0);
expect_system_error([&closed_event_fd] { closed_event_fd.write_notification(); },
std::errc::bad_file_descriptor, "closed eventfd write should be rejected");
}
Loading
Loading