From 621aa4e5ce5a1ac93a31439adcec00e301206072 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 24 May 2024 13:46:21 -0700 Subject: [PATCH] [call-v3] Yodel - a call testing framework (#36635) Introduce "Yodel" - a framework for testing things vaguely related to calls. This is breaking up some work I did for transport test suites - we've got a nice way of spawning test-only promises and tracking them visually, and support for setting up an environment that can run as a test or a fuzzer. I'm making that piece a little more reusable, and then rebasing the transport test suite atop that infrastructure. Closes #36635 COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/36635 from ctiller:transport-refs-6 843a9f4b7ebb5c3058c62ab9d931f0611fcca4ea PiperOrigin-RevId: 637022756 --- CMakeLists.txt | 12 +- bazel/grpc_build_system.bzl | 2 + build_autogenerated.yaml | 20 +- test/core/call/yodel/BUILD | 81 +++ test/core/call/yodel/README.md | 12 + .../test_suite => call/yodel}/fuzzer.proto | 1 - .../test_suite => call/yodel}/fuzzer_main.cc | 34 +- test/core/call/yodel/grpc_yodel_test.bzl | 59 +++ .../test_suite => call/yodel}/test_main.cc | 27 +- .../test.cc => call/yodel/yodel_test.cc} | 230 +++++---- test/core/call/yodel/yodel_test.h | 462 ++++++++++++++++++ .../fuzzing_event_engine.cc | 4 +- test/core/transport/test_suite/BUILD | 54 +- .../core/transport/test_suite/call_content.cc | 2 +- test/core/transport/test_suite/call_shapes.cc | 2 +- .../test_suite/chaotic_good_fixture.cc | 2 +- test/core/transport/test_suite/fixture.cc | 29 -- test/core/transport/test_suite/fixture.h | 77 --- .../test_suite/grpc_transport_test.bzl | 42 +- .../transport/test_suite/inproc_fixture.cc | 2 +- test/core/transport/test_suite/no_op.cc | 2 +- test/core/transport/test_suite/stress.cc | 2 +- test/core/transport/test_suite/test.h | 368 -------------- .../transport/test_suite/transport_test.cc | 69 +++ .../transport/test_suite/transport_test.h | 87 ++++ tools/distrib/fix_build_deps.py | 74 +-- 26 files changed, 1004 insertions(+), 752 deletions(-) create mode 100644 test/core/call/yodel/BUILD create mode 100644 test/core/call/yodel/README.md rename test/core/{transport/test_suite => call/yodel}/fuzzer.proto (97%) rename test/core/{transport/test_suite => call/yodel}/fuzzer_main.cc (67%) create mode 100644 test/core/call/yodel/grpc_yodel_test.bzl rename test/core/{transport/test_suite => call/yodel}/test_main.cc (55%) rename test/core/{transport/test_suite/test.cc => call/yodel/yodel_test.cc} (54%) create mode 100644 test/core/call/yodel/yodel_test.h delete mode 100644 test/core/transport/test_suite/fixture.cc delete mode 100644 test/core/transport/test_suite/fixture.h delete mode 100644 test/core/transport/test_suite/test.h create mode 100644 test/core/transport/test_suite/transport_test.cc create mode 100644 test/core/transport/test_suite/transport_test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2169f108009..f2d496b08b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17562,15 +17562,15 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) ${_gRPC_PROTO_GENS_DIR}/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h ${_gRPC_PROTO_GENS_DIR}/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.grpc.pb.h + test/core/call/yodel/test_main.cc + test/core/call/yodel/yodel_test.cc test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc test/core/transport/test_suite/call_content.cc test/core/transport/test_suite/call_shapes.cc - test/core/transport/test_suite/fixture.cc test/core/transport/test_suite/inproc_fixture.cc test/core/transport/test_suite/no_op.cc test/core/transport/test_suite/stress.cc - test/core/transport/test_suite/test.cc - test/core/transport/test_suite/test_main.cc + test/core/transport/test_suite/transport_test.cc ) if(WIN32 AND MSVC) if(BUILD_SHARED_LIBS) @@ -30294,15 +30294,15 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) src/core/ext/transport/chaotic_good/frame_header.cc src/core/ext/transport/chaotic_good/server_transport.cc src/core/lib/transport/promise_endpoint.cc + test/core/call/yodel/test_main.cc + test/core/call/yodel/yodel_test.cc test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc test/core/transport/test_suite/call_content.cc test/core/transport/test_suite/call_shapes.cc test/core/transport/test_suite/chaotic_good_fixture.cc - test/core/transport/test_suite/fixture.cc test/core/transport/test_suite/no_op.cc test/core/transport/test_suite/stress.cc - test/core/transport/test_suite/test.cc - test/core/transport/test_suite/test_main.cc + test/core/transport/test_suite/transport_test.cc ) if(WIN32 AND MSVC) if(BUILD_SHARED_LIBS) diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl index e26b1b34873..153002b7d57 100644 --- a/bazel/grpc_build_system.bzl +++ b/bazel/grpc_build_system.bzl @@ -235,6 +235,7 @@ def grpc_proto_library( name, srcs = [], deps = [], + visibility = None, well_known_protos = False, has_services = True, use_external = False, @@ -243,6 +244,7 @@ def grpc_proto_library( name = name, srcs = srcs, deps = deps, + visibility = visibility, well_known_protos = well_known_protos, proto_only = not has_services, use_external = use_external, diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index b505f641e0f..5dea7e76ab5 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -11453,20 +11453,20 @@ targets: build: test language: c++ headers: + - test/core/call/yodel/yodel_test.h - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h - - test/core/transport/test_suite/fixture.h - - test/core/transport/test_suite/test.h + - test/core/transport/test_suite/transport_test.h src: - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.proto + - test/core/call/yodel/test_main.cc + - test/core/call/yodel/yodel_test.cc - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc - test/core/transport/test_suite/call_content.cc - test/core/transport/test_suite/call_shapes.cc - - test/core/transport/test_suite/fixture.cc - test/core/transport/test_suite/inproc_fixture.cc - test/core/transport/test_suite/no_op.cc - test/core/transport/test_suite/stress.cc - - test/core/transport/test_suite/test.cc - - test/core/transport/test_suite/test_main.cc + - test/core/transport/test_suite/transport_test.cc deps: - gtest - protobuf @@ -19488,9 +19488,9 @@ targets: - src/core/lib/promise/switch.h - src/core/lib/promise/wait_set.h - src/core/lib/transport/promise_endpoint.h + - test/core/call/yodel/yodel_test.h - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h - - test/core/transport/test_suite/fixture.h - - test/core/transport/test_suite/test.h + - test/core/transport/test_suite/transport_test.h src: - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.proto - src/core/ext/transport/chaotic_good/chaotic_good_transport.cc @@ -19499,15 +19499,15 @@ targets: - src/core/ext/transport/chaotic_good/frame_header.cc - src/core/ext/transport/chaotic_good/server_transport.cc - src/core/lib/transport/promise_endpoint.cc + - test/core/call/yodel/test_main.cc + - test/core/call/yodel/yodel_test.cc - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc - test/core/transport/test_suite/call_content.cc - test/core/transport/test_suite/call_shapes.cc - test/core/transport/test_suite/chaotic_good_fixture.cc - - test/core/transport/test_suite/fixture.cc - test/core/transport/test_suite/no_op.cc - test/core/transport/test_suite/stress.cc - - test/core/transport/test_suite/test.cc - - test/core/transport/test_suite/test_main.cc + - test/core/transport/test_suite/transport_test.cc deps: - gtest - protobuf diff --git a/test/core/call/yodel/BUILD b/test/core/call/yodel/BUILD new file mode 100644 index 00000000000..cdf6fd85a8c --- /dev/null +++ b/test/core/call/yodel/BUILD @@ -0,0 +1,81 @@ +# Copyright 2024 gRPC authors. +# +# 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 +# +# http://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. + +load( + "//bazel:grpc_build_system.bzl", + "grpc_cc_library", + "grpc_package", + "grpc_proto_library", +) + +grpc_package(name = "test/core/call/yodel") + +exports_files( + ["fuzzer_main.cc"], + visibility = ["//test:__subpackages__"], +) + +grpc_cc_library( + name = "yodel_test", + testonly = 1, + srcs = ["yodel_test.cc"], + hdrs = ["yodel_test.h"], + external_deps = [ + "absl/functional:any_invocable", + "absl/log", + "absl/random", + "absl/random:bit_gen_ref", + "absl/strings", + "gtest", + ], + visibility = ["//test:__subpackages__"], + deps = [ + "//:debug_location", + "//:event_engine_base_hdrs", + "//:iomgr_timer", + "//:promise", + "//src/core:call_arena_allocator", + "//src/core:call_spine", + "//src/core:cancel_callback", + "//src/core:metadata", + "//src/core:promise_factory", + "//src/core:resource_quota", + "//test/core/event_engine/fuzzing_event_engine", + "//test/core/test_util:grpc_test_util", + ], +) + +grpc_cc_library( + name = "test_main", + testonly = 1, + srcs = ["test_main.cc"], + external_deps = ["absl/random"], + visibility = ["//test:__subpackages__"], + deps = [ + "yodel_test", + "//:grpc_trace", + "//test/core/test_util:grpc_test_util", + ], +) + +grpc_proto_library( + name = "fuzzer_proto", + srcs = ["fuzzer.proto"], + has_services = False, + visibility = ["//test:__subpackages__"], + deps = [ + "//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine_proto", + "//test/core/test_util:fuzz_config_vars_proto", + ], +) diff --git a/test/core/call/yodel/README.md b/test/core/call/yodel/README.md new file mode 100644 index 00000000000..7d1f3b12083 --- /dev/null +++ b/test/core/call/yodel/README.md @@ -0,0 +1,12 @@ +Yodel is a foundational test framework for unit testing parts of a call. + +It provides infrastructure to write tests around some call actor (the various +frameworks built atop of it specify what that actor is). It also provides +utilities to fill in various call details, interact with promises in a +way that's convenient to debug, and run as either a unit test or a fuzzer. + +Various frameworks are built atop it: +- transports use it as part of the transport test_suite + +Planned: +- interceptors & filters should also use this diff --git a/test/core/transport/test_suite/fuzzer.proto b/test/core/call/yodel/fuzzer.proto similarity index 97% rename from test/core/transport/test_suite/fuzzer.proto rename to test/core/call/yodel/fuzzer.proto index 83a12936196..1070c917071 100644 --- a/test/core/transport/test_suite/fuzzer.proto +++ b/test/core/call/yodel/fuzzer.proto @@ -21,7 +21,6 @@ import "test/core/test_util/fuzz_config_vars.proto"; message Msg { uint32 test_id = 1; - uint32 fixture_id = 2; fuzzing_event_engine.Actions event_engine_actions = 10; grpc.testing.FuzzConfigVars config_vars = 11; diff --git a/test/core/transport/test_suite/fuzzer_main.cc b/test/core/call/yodel/fuzzer_main.cc similarity index 67% rename from test/core/transport/test_suite/fuzzer_main.cc rename to test/core/call/yodel/fuzzer_main.cc index 23cc3470973..8eee8f8f5f1 100644 --- a/test/core/transport/test_suite/fuzzer_main.cc +++ b/test/core/call/yodel/fuzzer_main.cc @@ -12,37 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - -#include - -#include "absl/log/check.h" - #include #include +#include +#include #include "src/core/lib/config/config_vars.h" #include "src/core/lib/event_engine/default_event_engine.h" #include "src/core/lib/experiments/config.h" #include "src/core/lib/gprpp/env.h" #include "src/libfuzzer/libfuzzer_macro.h" +#include "test/core/call/yodel/fuzzer.pb.h" +#include "test/core/call/yodel/yodel_test.h" #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h" #include "test/core/test_util/fuzz_config_vars.h" #include "test/core/test_util/proto_bit_gen.h" -#include "test/core/transport/test_suite/fixture.h" -#include "test/core/transport/test_suite/fuzzer.pb.h" -#include "test/core/transport/test_suite/test.h" +#include "absl/log/check.h" bool squelch = true; static void dont_log(gpr_log_func_args* /*args*/) {} DEFINE_PROTO_FUZZER(const transport_test_suite::Msg& msg) { - const auto& tests = grpc_core::TransportTestRegistry::Get().tests(); - const auto& fixtures = grpc_core::TransportFixtureRegistry::Get().fixtures(); - CHECK(!tests.empty()); - CHECK(!fixtures.empty()); - const int test_id = msg.test_id() % tests.size(); - const int fixture_id = msg.fixture_id() % fixtures.size(); + static const grpc_core::NoDestruct< + std::vector> + tests{grpc_core::yodel_detail::TestRegistry::AllTests()}; + CHECK(!tests->empty()); + const int test_id = msg.test_id() % tests->size(); if (squelch && !grpc_core::GetEnv("GRPC_TRACE_FUZZER").has_value()) { gpr_set_log_function(dont_log); @@ -53,15 +48,10 @@ DEFINE_PROTO_FUZZER(const transport_test_suite::Msg& msg) { grpc_core::ConfigVars::SetOverrides(overrides); grpc_core::TestOnlyReloadExperimentsFromConfigVariables(); if (!squelch) { - fprintf(stderr, "RUN TEST '%s' with fixture '%s'\n", - std::string(tests[test_id].name).c_str(), - std::string(fixtures[fixture_id].name).c_str()); + LOG(INFO) << "RUN TEST '" << (*tests)[test_id].name << "'"; } grpc_core::ProtoBitGen bitgen(msg.rng()); - auto test = - tests[test_id].create(std::unique_ptr( - fixtures[fixture_id].create()), - msg.event_engine_actions(), bitgen); + auto test = (*tests)[test_id].make(msg.event_engine_actions(), bitgen); test->RunTest(); delete test; CHECK(!::testing::Test::HasFailure()); diff --git a/test/core/call/yodel/grpc_yodel_test.bzl b/test/core/call/yodel/grpc_yodel_test.bzl new file mode 100644 index 00000000000..ab43116e481 --- /dev/null +++ b/test/core/call/yodel/grpc_yodel_test.bzl @@ -0,0 +1,59 @@ +# Copyright 2024 gRPC authors. +# +# 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 +# +# http://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. + +""" +Generate one transport test & associated fuzzer +""" + +load("//bazel:grpc_build_system.bzl", "grpc_cc_test") +load("//test/core/test_util:grpc_fuzzer.bzl", "grpc_proto_fuzzer") + +def grpc_yodel_test(name, deps): + grpc_cc_test( + name = name + "_test", + srcs = [], + tags = ["no_windows", "no_mac"], + deps = [ + "//test/core/call/yodel:test_main", + ] + deps, + uses_polling = False, + ) + + grpc_proto_fuzzer( + name = name + "_fuzzer", + srcs = ["//test/core/call/yodel:fuzzer_main.cc"], + tags = ["no_windows", "no_mac"], + external_deps = [ + "absl/log:check", + "gtest", + ], + deps = [ + "//test/core/call/yodel:yodel_test", + "//test/core/call/yodel:fuzzer_proto", + "//:event_engine_base_hdrs", + "//:config_vars", + "//:exec_ctx", + "//:gpr", + "//:grpc_unsecure", + "//:iomgr_timer", + "//src/core:default_event_engine", + "//src/core:env", + "//src/core:experiments", + "//test/core/event_engine/fuzzing_event_engine", + "//test/core/test_util:fuzz_config_vars", + "//test/core/test_util:proto_bit_gen", + ] + deps, + corpus = "corpus/%s" % name, + proto = None, + ) diff --git a/test/core/transport/test_suite/test_main.cc b/test/core/call/yodel/test_main.cc similarity index 55% rename from test/core/transport/test_suite/test_main.cc rename to test/core/call/yodel/test_main.cc index cc51d9f4047..40066c94851 100644 --- a/test/core/transport/test_suite/test_main.cc +++ b/test/core/call/yodel/test_main.cc @@ -15,27 +15,24 @@ #include "absl/random/random.h" #include "src/core/lib/debug/trace.h" +#include "test/core/call/yodel/yodel_test.h" #include "test/core/test_util/test_config.h" -#include "test/core/transport/test_suite/fixture.h" -#include "test/core/transport/test_suite/test.h" int main(int argc, char** argv) { grpc::testing::TestEnvironment env(&argc, argv); absl::BitGen bitgen; ::testing::InitGoogleTest(&argc, argv); - for (const auto& test : grpc_core::TransportTestRegistry::Get().tests()) { - for (const auto& fixture : - grpc_core::TransportFixtureRegistry::Get().fixtures()) { - ::testing::RegisterTest( - "TransportTest", absl::StrCat(test.name, "/", fixture.name).c_str(), - nullptr, nullptr, __FILE__, __LINE__, - [test = &test, fixture = &fixture, - &bitgen]() -> grpc_core::TransportTest* { - return test->create( - std::unique_ptr(fixture->create()), - fuzzing_event_engine::Actions(), bitgen); - }); - } + static grpc_core::NoDestruct< + std::vector> + tests{grpc_core::yodel_detail::TestRegistry::AllTests()}; + CHECK(!tests->empty()); + for (const auto& test : *tests) { + CHECK(test.make != nullptr) << "test:" << test.name; + ::testing::RegisterTest( + test.test_type.c_str(), test.name.c_str(), nullptr, nullptr, __FILE__, + __LINE__, [test = &test, &bitgen]() -> grpc_core::YodelTest* { + return test->make(fuzzing_event_engine::Actions(), bitgen); + }); } grpc_tracer_init(); return RUN_ALL_TESTS(); diff --git a/test/core/transport/test_suite/test.cc b/test/core/call/yodel/yodel_test.cc similarity index 54% rename from test/core/transport/test_suite/test.cc rename to test/core/call/yodel/yodel_test.cc index c228196983e..88a3c4033ff 100644 --- a/test/core/transport/test_suite/test.cc +++ b/test/core/call/yodel/yodel_test.cc @@ -1,4 +1,4 @@ -// Copyright 2023 gRPC authors. +// Copyright 2024 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,75 +12,159 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "test/core/transport/test_suite/test.h" - -#include +#include "test/core/call/yodel/yodel_test.h" #include "absl/random/random.h" +#include "src/core/lib/iomgr/timer_manager.h" +#include "src/core/lib/resource_quota/resource_quota.h" + namespace grpc_core { +namespace yodel_detail { + +TestRegistry* TestRegistry::root_ = nullptr; + +/////////////////////////////////////////////////////////////////////////////// +// ActionState + +ActionState::ActionState(NameAndLocation name_and_location, int step) + : name_and_location_(name_and_location), step_(step), state_(kNotCreated) {} + +absl::string_view ActionState::StateString(State state) { + // We use emoji here to make it easier to visually scan the logs. + switch (state) { + case kNotCreated: + return "🚦"; + case kNotStarted: + return "⏰"; + case kStarted: + return "🚗"; + case kDone: + return "🏁"; + case kCancelled: + return "💥"; + } +} + +void ActionState::Set(State state, SourceLocation whence) { + LOG(INFO) << StateString(state) << " " << name() << " [" << step() << "] " + << file() << ":" << line() << " @ " << whence.file() << ":" + << whence.line(); + state_ = state; +} + +bool ActionState::IsDone() { + switch (state_) { + case kNotCreated: + case kNotStarted: + case kStarted: + return false; + case kDone: + case kCancelled: + return true; + } +} + /////////////////////////////////////////////////////////////////////////////// -// TransportTestRegistry +// TestRegistry -TransportTestRegistry& TransportTestRegistry::Get() { - static TransportTestRegistry* registry = new TransportTestRegistry(); - return *registry; +std::vector TestRegistry::AllTests() { + std::vector tests; + for (auto* r = root_; r; r = r->next_) { + r->ContributeTests(tests); + } + std::vector out; + for (auto& test : tests) { + if (absl::StartsWith(test.name, "DISABLED_")) continue; + out.emplace_back(std::move(test)); + } + std::stable_sort(out.begin(), out.end(), [](const Test& a, const Test& b) { + return std::make_tuple(a.file, a.line) < std::make_tuple(b.file, b.line); + }); + return out; } -void TransportTestRegistry::RegisterTest( +/////////////////////////////////////////////////////////////////////////////// +// SimpleTestRegistry + +void SimpleTestRegistry::RegisterTest( + absl::string_view file, int line, absl::string_view test_type, absl::string_view name, - absl::AnyInvocable, - const fuzzing_event_engine::Actions&, - absl::BitGenRef) const> + absl::AnyInvocable create) { - if (absl::StartsWith(name, "DISABLED_")) return; - tests_.push_back({name, std::move(create)}); + tests_.push_back({file, line, std::string(test_type), std::string(name), + std::move(create)}); +} + +void SimpleTestRegistry::ContributeTests(std::vector& tests) { + for (const auto& test : tests_) { + tests.push_back( + {test.file, test.line, test.test_type, test.name, + [test = &test](const fuzzing_event_engine::Actions& actions, + absl::BitGenRef rng) { + return test->make(actions, rng); + }}); + } } +} // namespace yodel_detail + +/////////////////////////////////////////////////////////////////////////////// +// YodelTest::WatchDog + +class YodelTest::WatchDog { + public: + explicit WatchDog(YodelTest* test) : test_(test) {} + ~WatchDog() { test_->event_engine_->Cancel(timer_); } + + private: + YodelTest* const test_; + grpc_event_engine::experimental::EventEngine::TaskHandle const timer_{ + test_->event_engine_->RunAfter(Duration::Minutes(5), + [this]() { test_->Timeout(); })}; +}; + /////////////////////////////////////////////////////////////////////////////// -// TransportTest +// YodelTest -void TransportTest::RunTest() { +YodelTest::YodelTest(const fuzzing_event_engine::Actions& actions, + absl::BitGenRef rng) + : rng_(rng), + event_engine_{ + std::make_shared( + []() { + grpc_timer_manager_set_threading(false); + grpc_event_engine::experimental::FuzzingEventEngine::Options + options; + return options; + }(), + actions)}, + call_arena_allocator_{MakeRefCounted( + MakeResourceQuota("test-quota") + ->memory_quota() + ->CreateMemoryAllocator("test-allocator"), + 1024)} {} + +void YodelTest::RunTest() { TestImpl(); EXPECT_EQ(pending_actions_.size(), 0) << "There are still pending actions: did you forget to call " "WaitForAllPendingWork()?"; - transport_pair_.client.reset(); - transport_pair_.server.reset(); + Shutdown(); event_engine_->TickUntilIdle(); event_engine_->UnsetGlobalHooks(); } -void TransportTest::SetServerCallDestination() { - transport_pair_.server->server_transport()->SetCallDestination( - server_call_destination_); -} - -CallInitiator TransportTest::CreateCall( - ClientMetadataHandle client_initial_metadata) { - auto call = MakeCall(std::move(client_initial_metadata)); - call.handler.SpawnInfallible( - "start-call", [this, handler = call.handler]() mutable { - transport_pair_.client->client_transport()->StartCall( - handler.V2HackToStartCallWithoutACallFilterStack()); - return Empty{}; - }); - return std::move(call.initiator); -} - -CallHandler TransportTest::TickUntilServerCall() { +void YodelTest::TickUntilTrue(absl::FunctionRef poll) { WatchDog watchdog(this); - for (;;) { - auto handler = server_call_destination_->PopHandler(); - if (handler.has_value()) { - return std::move(*handler); - } + while (!poll()) { event_engine_->Tick(); } } -void TransportTest::WaitForAllPendingWork() { +void YodelTest::WaitForAllPendingWork() { WatchDog watchdog(this); while (!pending_actions_.empty()) { if (pending_actions_.front()->IsDone()) { @@ -91,7 +175,7 @@ void TransportTest::WaitForAllPendingWork() { } } -void TransportTest::Timeout() { +void YodelTest::Timeout() { std::vector lines; lines.emplace_back("Timeout waiting for pending actions to complete"); while (!pending_actions_.empty()) { @@ -99,7 +183,7 @@ void TransportTest::Timeout() { pending_actions_.pop(); if (action->IsDone()) continue; absl::string_view state_name = - transport_test_detail::ActionState::StateString(action->Get()); + yodel_detail::ActionState::StateString(action->Get()); absl::string_view file_name = action->file(); auto pos = file_name.find_last_of('/'); if (pos != absl::string_view::npos) { @@ -112,8 +196,8 @@ void TransportTest::Timeout() { Crash(absl::StrJoin(lines, "\n")); } -std::string TransportTest::RandomString(int min_length, int max_length, - absl::string_view character_set) { +std::string YodelTest::RandomString(int min_length, int max_length, + absl::string_view character_set) { std::string out; int length = absl::LogUniform(rng_, min_length, max_length + 1); for (int i = 0; i < length; ++i) { @@ -123,7 +207,7 @@ std::string TransportTest::RandomString(int min_length, int max_length, return out; } -std::string TransportTest::RandomStringFrom( +std::string YodelTest::RandomStringFrom( std::initializer_list choices) { size_t idx = absl::Uniform(rng_, 0, choices.size()); auto it = choices.begin(); @@ -131,7 +215,7 @@ std::string TransportTest::RandomStringFrom( return std::string(*it); } -std::string TransportTest::RandomMetadataKey() { +std::string YodelTest::RandomMetadataKey() { if (absl::Bernoulli(rng_, 0.1)) { return RandomStringFrom({ ":path", @@ -148,7 +232,7 @@ std::string TransportTest::RandomMetadataKey() { return out; } -std::string TransportTest::RandomMetadataValue(absl::string_view key) { +std::string YodelTest::RandomMetadataValue(absl::string_view key) { if (key == ":method") { return RandomStringFrom({"GET", "POST", "PUT"}); } @@ -169,11 +253,11 @@ std::string TransportTest::RandomMetadataValue(absl::string_view key) { return RandomString(0, 128, *kChars); } -std::string TransportTest::RandomMetadataBinaryKey() { +std::string YodelTest::RandomMetadataBinaryKey() { return RandomString(1, 128, "abcdefghijklmnopqrstuvwxyz-_") + "-bin"; } -std::string TransportTest::RandomMetadataBinaryValue() { +std::string YodelTest::RandomMetadataBinaryValue() { static const NoDestruct kChars{[]() { std::string out; for (int c = 0; c < 256; c++) { @@ -184,8 +268,7 @@ std::string TransportTest::RandomMetadataBinaryValue() { return RandomString(0, 4096, *kChars); } -std::vector> -TransportTest::RandomMetadata() { +std::vector> YodelTest::RandomMetadata() { size_t size = 0; const size_t max_size = absl::LogUniform(rng_, 64, 8000); std::vector> out; @@ -218,7 +301,7 @@ TransportTest::RandomMetadata() { return out; } -std::string TransportTest::RandomMessage() { +std::string YodelTest::RandomMessage() { static const NoDestruct kChars{[]() { std::string out; for (int c = 0; c < 256; c++) { @@ -229,43 +312,4 @@ std::string TransportTest::RandomMessage() { return RandomString(0, 1024 * 1024, *kChars); } -/////////////////////////////////////////////////////////////////////////////// -// TransportTest::ServerCallDestination - -void TransportTest::ServerCallDestination::StartCall( - UnstartedCallHandler handler) { - handlers_.push(handler.V2HackToStartCallWithoutACallFilterStack()); -} - -absl::optional TransportTest::ServerCallDestination::PopHandler() { - if (!handlers_.empty()) { - auto handler = std::move(handlers_.front()); - handlers_.pop(); - return handler; - } - return absl::nullopt; -} - -/////////////////////////////////////////////////////////////////////////////// -// ActionState - -namespace transport_test_detail { - -ActionState::ActionState(NameAndLocation name_and_location) - : name_and_location_(name_and_location), state_(kNotCreated) {} - -bool ActionState::IsDone() { - switch (state_) { - case kNotCreated: - case kNotStarted: - case kStarted: - return false; - case kDone: - case kCancelled: - return true; - } -} - -} // namespace transport_test_detail - } // namespace grpc_core diff --git a/test/core/call/yodel/yodel_test.h b/test/core/call/yodel/yodel_test.h new file mode 100644 index 00000000000..bca587eff41 --- /dev/null +++ b/test/core/call/yodel/yodel_test.h @@ -0,0 +1,462 @@ +// Copyright 2024 gRPC authors. +// +// 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 +// +// http://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. + +#ifndef GRPC_TEST_CORE_CALL_YODEL_YODEL_TEST_H +#define GRPC_TEST_CORE_CALL_YODEL_YODEL_TEST_H + +#include "absl/functional/any_invocable.h" +#include "absl/log/log.h" +#include "absl/random/bit_gen_ref.h" +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" + +#include + +#include "src/core/lib/gprpp/debug_location.h" +#include "src/core/lib/promise/cancel_callback.h" +#include "src/core/lib/promise/detail/promise_factory.h" +#include "src/core/lib/promise/promise.h" +#include "src/core/lib/transport/call_arena_allocator.h" +#include "src/core/lib/transport/call_spine.h" +#include "src/core/lib/transport/metadata.h" +#include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h" +#include "test/core/test_util/test_config.h" + +namespace grpc_core { + +class YodelTest; + +namespace yodel_detail { + +// Capture the name and location of a test step. +class NameAndLocation { + public: + // Allow implicit construction from a string, to capture the start location + // from the variadic StartTestSeq name argument. + // NOLINTNEXTLINE + NameAndLocation(const char* name, SourceLocation location = {}) + : location_(location), name_(name) {} + + SourceLocation location() const { return location_; } + absl::string_view name() const { return name_; } + + private: + SourceLocation location_; + absl::string_view name_; +}; + +// Capture the state of a test step. +class ActionState { + public: + enum State : uint8_t { + // Initial state: construction of this step in the sequence has not been + // performed. + kNotCreated, + // The step has been created, but not yet started (the initial poll of the + // created promise has not occurred). + kNotStarted, + // The step has been polled, but it's not yet been completed. + kStarted, + // The step has been completed. + kDone, + // The step has been cancelled. + kCancelled, + }; + + // Generate a nice little prefix for log messages. + static absl::string_view StateString(State state); + + ActionState(NameAndLocation name_and_location, int step); + + State Get() const { return state_; } + void Set(State state, SourceLocation whence = {}); + const NameAndLocation& name_and_location() const { + return name_and_location_; + } + SourceLocation location() const { return name_and_location().location(); } + const char* file() const { return location().file(); } + int line() const { return location().line(); } + absl::string_view name() const { return name_and_location().name(); } + int step() const { return step_; } + bool IsDone(); + + private: + const NameAndLocation name_and_location_; + const int step_; + std::atomic state_; +}; + +class SequenceSpawner { + public: + SequenceSpawner( + NameAndLocation name_and_location, + absl::AnyInvocable)> + promise_spawner, + absl::FunctionRef(NameAndLocation, int)> + action_state_factory) + : name_and_location_(name_and_location), + promise_spawner_( + std::make_shared< + absl::AnyInvocable)>>( + std::move(promise_spawner))), + action_state_factory_(action_state_factory) {} + + template + void Start(First first, FollowUps... followups) { + using Factory = promise_detail::OncePromiseFactory; + using FactoryPromise = typename Factory::Promise; + using Result = typename FactoryPromise::Result; + auto action_state = action_state_factory_(name_and_location_, step_); + ++step_; + auto next = MakeNext(std::move(followups)...); + (*promise_spawner_)( + name_and_location_.name(), + [spawner = promise_spawner_, first = Factory(std::move(first)), + next = std::move(next), action_state = std::move(action_state), + name_and_location = name_and_location_]() mutable { + action_state->Set(ActionState::kNotStarted); + auto promise = first.Make(); + (*spawner)(name_and_location.name(), + WrapPromiseAndNext(std::move(action_state), + std::move(promise), std::move(next))); + return Empty{}; + }); + } + + private: + template + absl::AnyInvocable MakeNext(FirstFollowUp first, + FollowUps... followups) { + using Factory = promise_detail::OncePromiseFactory; + using FactoryPromise = typename Factory::Promise; + using Result = typename FactoryPromise::Result; + auto action_state = action_state_factory_(name_and_location_, step_); + ++step_; + auto next = MakeNext(std::move(followups)...); + return [spawner = promise_spawner_, factory = Factory(std::move(first)), + next = std::move(next), action_state = std::move(action_state), + name_and_location = name_and_location_](Arg arg) mutable { + action_state->Set(ActionState::kNotStarted); + (*spawner)( + name_and_location.name(), + WrapPromiseAndNext(std::move(action_state), + factory.Make(std::move(arg)), std::move(next))); + }; + } + + template + static Promise WrapPromiseAndNext( + std::shared_ptr action_state, P promise, + absl::AnyInvocable next) { + return Promise(OnCancel( + [action_state, promise = std::move(promise), + next = std::move(next)]() mutable -> Poll { + action_state->Set(ActionState::kStarted); + auto r = promise(); + if (auto* p = r.value_if_ready()) { + action_state->Set(ActionState::kDone); + next(std::move(*p)); + return Empty{}; + } else { + return Pending{}; + } + }, + [action_state]() { action_state->Set(ActionState::kCancelled); })); + } + + template + absl::AnyInvocable MakeNext() { + // Enforce last-arg is Empty so we don't drop things + return [](Empty) {}; + } + + NameAndLocation name_and_location_; + std::shared_ptr)>> + promise_spawner_; + absl::FunctionRef(NameAndLocation, int)> + action_state_factory_; + int step_ = 1; +}; + +template +auto SpawnerForContext( + Context context, + grpc_event_engine::experimental::EventEngine* event_engine) { + return [context = std::move(context), event_engine]( + absl::string_view name, Promise promise) mutable { + // Pass new promises via event engine to allow fuzzers to explore + // reorderings of possibly interleaved spawns. + event_engine->Run([name, context, promise = std::move(promise)]() mutable { + context.SpawnInfallible(name, std::move(promise)); + }); + }; +} + +class TestRegistry { + public: + TestRegistry() : next_(root_) { root_ = this; } + + struct Test { + absl::string_view file; + int line; + std::string test_type; + std::string name; + absl::AnyInvocable + make; + }; + + static std::vector AllTests(); + + protected: + ~TestRegistry() { + Crash("unreachable: TestRegistry should never be destroyed"); + } + + private: + virtual void ContributeTests(std::vector& tests) = 0; + + TestRegistry* next_; + static TestRegistry* root_; +}; + +class SimpleTestRegistry final : public TestRegistry { + public: + SimpleTestRegistry() {} + ~SimpleTestRegistry() = delete; + + void RegisterTest( + absl::string_view file, int line, absl::string_view test_type, + absl::string_view name, + absl::AnyInvocable + create); + + static SimpleTestRegistry& Get() { + static SimpleTestRegistry* const p = new SimpleTestRegistry; + return *p; + } + + private: + void ContributeTests(std::vector& tests) override; + + std::vector tests_; +}; + +template +class ParameterizedTestRegistry final : public TestRegistry { + public: + ParameterizedTestRegistry() {} + ~ParameterizedTestRegistry() = delete; + + void RegisterTest(absl::string_view file, int line, + absl::string_view test_type, absl::string_view name, + absl::AnyInvocable + make) { + tests_.push_back({file, line, test_type, name, std::move(make)}); + } + + void RegisterParameter(absl::string_view name, T value) { + parameters_.push_back({name, std::move(value)}); + } + + static ParameterizedTestRegistry& Get() { + static ParameterizedTestRegistry* const p = new ParameterizedTestRegistry; + return *p; + } + + private: + struct ParameterizedTest { + absl::string_view file; + int line; + absl::string_view test_type; + absl::string_view name; + absl::AnyInvocable + make; + }; + struct Parameter { + absl::string_view name; + T value; + }; + + void ContributeTests(std::vector& tests) override { + for (const auto& test : tests_) { + for (const auto& parameter : parameters_) { + tests.push_back({test.file, test.line, std::string(test.test_type), + absl::StrCat(test.name, "/", parameter.name), + [test = &test, parameter = ¶meter]( + const fuzzing_event_engine::Actions& actions, + absl::BitGenRef rng) { + return test->make(parameter->value, actions, rng); + }}); + } + } + } + + std::vector tests_; + std::vector parameters_; +}; + +} // namespace yodel_detail + +class YodelTest : public ::testing::Test { + public: + void RunTest(); + + protected: + YodelTest(const fuzzing_event_engine::Actions& actions, absl::BitGenRef rng); + + // Helpers to generate various random values. + // When we're fuzzing, delegates to the fuzzer input to generate this data. + std::string RandomString(int min_length, int max_length, + absl::string_view character_set); + std::string RandomStringFrom( + std::initializer_list choices); + std::string RandomMetadataKey(); + std::string RandomMetadataValue(absl::string_view key); + std::string RandomMetadataBinaryKey(); + std::string RandomMetadataBinaryValue(); + std::vector> RandomMetadata(); + std::string RandomMessage(); + absl::BitGenRef rng() { return rng_; } + + // Alternative for Seq for test driver code. + // Registers each step so that WaitForAllPendingWork() can report progress, + // and wait for completion... AND generate good failure messages when a + // sequence doesn't complete in a timely manner. + // Uses the `SpawnInfallible` method on `context` to provide an execution + // environment for each step. + // Initiates each step in a different event engine closure to maximize + // opportunities for fuzzers to reorder the steps, or thready-tsan to expose + // potential threading issues. + template + void SpawnTestSeq(Context context, + yodel_detail::NameAndLocation name_and_location, + Actions... actions) { + yodel_detail::SequenceSpawner( + name_and_location, + yodel_detail::SpawnerForContext(std::move(context), + event_engine_.get()), + [this](yodel_detail::NameAndLocation name_and_location, int step) { + auto action = std::make_shared( + name_and_location, step); + pending_actions_.push(action); + return action; + }) + .Start(std::move(actions)...); + } + + auto MakeCall(ClientMetadataHandle client_initial_metadata) { + auto* arena = call_arena_allocator_->MakeArena(); + return MakeCallPair(std::move(client_initial_metadata), event_engine_.get(), + arena, call_arena_allocator_, nullptr); + } + + void WaitForAllPendingWork(); + + template + T TickUntil(absl::FunctionRef()> poll) { + absl::optional result; + TickUntilTrue([poll, &result]() { + auto r = poll(); + if (auto* p = r.value_if_ready()) { + result = std::move(*p); + return true; + } + return false; + }); + return std::move(*result); + } + + const std::shared_ptr& + event_engine() { + return event_engine_; + } + + private: + class WatchDog; + + virtual void TestImpl() = 0; + + void Timeout(); + void TickUntilTrue(absl::FunctionRef poll); + + // Called after the test has run, but before the event engine is shut down. + virtual void Shutdown() {} + + grpc::testing::TestGrpcScope grpc_scope_; + absl::BitGenRef rng_; + const std::shared_ptr + event_engine_; + const RefCountedPtr call_arena_allocator_; + std::queue> pending_actions_; +}; + +} // namespace grpc_core + +#define YODEL_TEST(test_type, name) \ + class YodelTest_##name : public grpc_core::test_type { \ + public: \ + using test_type::test_type; \ + void TestBody() override { RunTest(); } \ + \ + private: \ + void TestImpl() override; \ + static grpc_core::YodelTest* Create( \ + const fuzzing_event_engine::Actions& actions, absl::BitGenRef rng) { \ + return new YodelTest_##name(actions, rng); \ + } \ + static int registered_; \ + }; \ + int YodelTest_##name::registered_ = \ + (grpc_core::yodel_detail::SimpleTestRegistry::Get().RegisterTest( \ + __FILE__, __LINE__, #test_type, #name, &Create), \ + 0); \ + void YodelTest_##name::TestImpl() + +// NOLINTBEGIN(bugprone-macro-parentheses) +#define YODEL_TEST_P(test_type, parameter_type, name) \ + class YodelTest_##name : public grpc_core::test_type { \ + public: \ + using test_type::test_type; \ + void TestBody() override { RunTest(); } \ + \ + private: \ + void TestImpl() override; \ + static grpc_core::YodelTest* Create( \ + const parameter_type& parameter, \ + const fuzzing_event_engine::Actions& actions, absl::BitGenRef rng) { \ + return new YodelTest_##name(parameter, actions, rng); \ + } \ + static int registered_; \ + }; \ + int YodelTest_##name::registered_ = \ + (grpc_core::yodel_detail::ParameterizedTestRegistry< \ + grpc_core::test_type, parameter_type>::Get() \ + .RegisterTest(__FILE__, __LINE__, #test_type, #name, &Create), \ + 0); \ + void YodelTest_##name::TestImpl() + +#define YODEL_TEST_PARAM(test_type, parameter_type, name, value) \ + int YodelTestParam_##name = \ + (grpc_core::yodel_detail::ParameterizedTestRegistry< \ + grpc_core::test_type, parameter_type>::Get() \ + .RegisterParameter(#name, value), \ + 0) +// NOLINTEND(bugprone-macro-parentheses) + +#endif // GRPC_TEST_CORE_CALL_YODEL_YODEL_TEST_H diff --git a/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc b/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc index b1771665d97..7a71288f0b5 100644 --- a/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc +++ b/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc @@ -579,7 +579,7 @@ EventEngine::TaskHandle FuzzingEventEngine::RunAfterLocked( } if (trace_timers.enabled()) { gpr_log(GPR_INFO, - "Schedule timer %" PRIx64 " @ %" PRIu64 " (now=%" PRIu64 + "Schedule timer %" PRIxPTR " @ %" PRIu64 " (now=%" PRIu64 "; delay=%" PRIu64 "; fuzzing_added=%" PRIu64 "; type=%d)", id, static_cast(final_time.time_since_epoch().count()), now.time_since_epoch().count(), when.count(), delay_taken.count(), @@ -600,7 +600,7 @@ bool FuzzingEventEngine::Cancel(TaskHandle handle) { return false; } if (trace_timers.enabled()) { - gpr_log(GPR_INFO, "Cancel timer %" PRIx64, id); + gpr_log(GPR_INFO, "Cancel timer %" PRIxPTR, id); } it->second->closure = nullptr; return true; diff --git a/test/core/transport/test_suite/BUILD b/test/core/transport/test_suite/BUILD index 5f1703fda25..3ba6cfde4b4 100644 --- a/test/core/transport/test_suite/BUILD +++ b/test/core/transport/test_suite/BUILD @@ -16,29 +16,17 @@ load( "//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_package", - "grpc_proto_library", ) load("grpc_transport_test.bzl", "grpc_transport_test") grpc_package(name = "test/core/transport/test_suite") -grpc_cc_library( - name = "fixture", - testonly = 1, - srcs = ["fixture.cc"], - hdrs = ["fixture.h"], - deps = [ - "//:grpc", - "//test/core/event_engine/fuzzing_event_engine", - ], -) - grpc_cc_library( name = "inproc_fixture", testonly = 1, srcs = ["inproc_fixture.cc"], deps = [ - "fixture", + "test", "//src/core:grpc_transport_inproc", ], alwayslink = 1, @@ -53,7 +41,7 @@ grpc_cc_library( "gtest", ], deps = [ - "fixture", + "test", "//src/core:chaotic_good_client_transport", "//src/core:chaotic_good_server_transport", "//src/core:event_engine_memory_allocator_factory", @@ -67,38 +55,16 @@ grpc_cc_library( grpc_cc_library( name = "test", testonly = 1, - srcs = ["test.cc"], - hdrs = ["test.h"], + srcs = ["transport_test.cc"], + hdrs = ["transport_test.h"], external_deps = [ - "absl/functional:any_invocable", "absl/log:log", "absl/random", "absl/random:bit_gen_ref", - "absl/strings", "gtest", ], deps = [ - "fixture", - "//:iomgr_timer", - "//:promise", - "//src/core:cancel_callback", - "//src/core:resource_quota", - "//src/core:time", - "//test/core/event_engine/fuzzing_event_engine", - "//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine_proto", - ], -) - -grpc_cc_library( - name = "test_main", - testonly = 1, - srcs = ["test_main.cc"], - external_deps = ["absl/random"], - deps = [ - "fixture", - "test", - "//:grpc_trace", - "//test/core/test_util:grpc_test_util", + "//test/core/call/yodel:yodel_test", ], ) @@ -136,16 +102,6 @@ grpc_cc_library( alwayslink = 1, ) -grpc_proto_library( - name = "fuzzer_proto", - srcs = ["fuzzer.proto"], - has_services = False, - deps = [ - "//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine_proto", - "//test/core/test_util:fuzz_config_vars_proto", - ], -) - grpc_transport_test( name = "inproc", deps = [ diff --git a/test/core/transport/test_suite/call_content.cc b/test/core/transport/test_suite/call_content.cc index cfcac0bf91b..5c393264c98 100644 --- a/test/core/transport/test_suite/call_content.cc +++ b/test/core/transport/test_suite/call_content.cc @@ -14,7 +14,7 @@ #include "gmock/gmock.h" -#include "test/core/transport/test_suite/test.h" +#include "test/core/transport/test_suite/transport_test.h" using testing::UnorderedElementsAreArray; diff --git a/test/core/transport/test_suite/call_shapes.cc b/test/core/transport/test_suite/call_shapes.cc index 89ee26ccd2f..fab954a676e 100644 --- a/test/core/transport/test_suite/call_shapes.cc +++ b/test/core/transport/test_suite/call_shapes.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "test/core/transport/test_suite/test.h" +#include "test/core/transport/test_suite/transport_test.h" namespace grpc_core { diff --git a/test/core/transport/test_suite/chaotic_good_fixture.cc b/test/core/transport/test_suite/chaotic_good_fixture.cc index e1e0baff5e9..0e87280c086 100644 --- a/test/core/transport/test_suite/chaotic_good_fixture.cc +++ b/test/core/transport/test_suite/chaotic_good_fixture.cc @@ -23,7 +23,7 @@ #include "src/core/lib/event_engine/tcp_socket_utils.h" #include "src/core/lib/resource_quota/resource_quota.h" #include "src/core/lib/transport/promise_endpoint.h" -#include "test/core/transport/test_suite/fixture.h" +#include "test/core/transport/test_suite/transport_test.h" using grpc_event_engine::experimental::EndpointConfig; using grpc_event_engine::experimental::EventEngine; diff --git a/test/core/transport/test_suite/fixture.cc b/test/core/transport/test_suite/fixture.cc deleted file mode 100644 index c1f26103693..00000000000 --- a/test/core/transport/test_suite/fixture.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2023 gRPC authors. -// -// 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 -// -// http://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. - -#include "test/core/transport/test_suite/fixture.h" - -namespace grpc_core { - -TransportFixtureRegistry& TransportFixtureRegistry::Get() { - static TransportFixtureRegistry* registry = new TransportFixtureRegistry(); - return *registry; -} -void TransportFixtureRegistry::RegisterFixture( - absl::string_view name, - absl::AnyInvocable create) { - fixtures_.push_back({name, std::move(create)}); -} - -} // namespace grpc_core diff --git a/test/core/transport/test_suite/fixture.h b/test/core/transport/test_suite/fixture.h deleted file mode 100644 index a9aad176510..00000000000 --- a/test/core/transport/test_suite/fixture.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2023 gRPC authors. -// -// 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 -// -// http://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. - -#ifndef GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_FIXTURE_H -#define GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_FIXTURE_H - -#include "src/core/lib/transport/transport.h" -#include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h" - -namespace grpc_core { - -class TransportFixture { - public: - struct ClientAndServerTransportPair { - OrphanablePtr client; - OrphanablePtr server; - }; - virtual ~TransportFixture() = default; - virtual ClientAndServerTransportPair CreateTransportPair( - std::shared_ptr - event_engine) = 0; -}; - -class TransportFixtureRegistry { - public: - static TransportFixtureRegistry& Get(); - void RegisterFixture(absl::string_view name, - absl::AnyInvocable create); - - struct Fixture { - absl::string_view name; - absl::AnyInvocable create; - }; - - const std::vector& fixtures() const { return fixtures_; } - - private: - std::vector fixtures_; -}; - -} // namespace grpc_core - -#define TRANSPORT_FIXTURE(name) \ - class TransportFixture_##name : public grpc_core::TransportFixture { \ - public: \ - using TransportFixture::TransportFixture; \ - ClientAndServerTransportPair CreateTransportPair( \ - std::shared_ptr \ - event_engine) override; \ - \ - private: \ - static grpc_core::TransportFixture* Create() { \ - return new TransportFixture_##name(); \ - } \ - static int registered_; \ - }; \ - int TransportFixture_##name::registered_ = \ - (grpc_core::TransportFixtureRegistry::Get().RegisterFixture( \ - #name, &TransportFixture_##name::Create), \ - 0); \ - grpc_core::TransportFixture::ClientAndServerTransportPair \ - TransportFixture_##name::CreateTransportPair( \ - std::shared_ptr \ - event_engine GRPC_UNUSED) - -#endif // GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_FIXTURE_H diff --git a/test/core/transport/test_suite/grpc_transport_test.bzl b/test/core/transport/test_suite/grpc_transport_test.bzl index f7c10d6e41d..43d444ac810 100644 --- a/test/core/transport/test_suite/grpc_transport_test.bzl +++ b/test/core/transport/test_suite/grpc_transport_test.bzl @@ -16,45 +16,7 @@ Generate one transport test & associated fuzzer """ -load("//bazel:grpc_build_system.bzl", "grpc_cc_test") -load("//test/core/test_util:grpc_fuzzer.bzl", "grpc_proto_fuzzer") +load("//test/core/call/yodel:grpc_yodel_test.bzl", "grpc_yodel_test") def grpc_transport_test(name, deps): - grpc_cc_test( - name = name + "_test", - srcs = [], - tags = ["no_windows", "no_mac"], - deps = [ - ":test_main", - ] + deps, - uses_polling = False, - ) - - grpc_proto_fuzzer( - name = name + "_fuzzer", - srcs = ["fuzzer_main.cc"], - tags = ["no_windows", "no_mac"], - external_deps = [ - "absl/log:check", - "gtest", - ], - deps = [ - ":test", - ":fixture", - ":fuzzer_proto", - "//:event_engine_base_hdrs", - "//:config_vars", - "//:exec_ctx", - "//:gpr", - "//:grpc_unsecure", - "//:iomgr_timer", - "//src/core:default_event_engine", - "//src/core:env", - "//src/core:experiments", - "//test/core/event_engine/fuzzing_event_engine", - "//test/core/test_util:fuzz_config_vars", - "//test/core/test_util:proto_bit_gen", - ] + deps, - corpus = "corpus/%s" % name, - proto = None, - ) + grpc_yodel_test(name, deps) diff --git a/test/core/transport/test_suite/inproc_fixture.cc b/test/core/transport/test_suite/inproc_fixture.cc index 65468229341..65a7d7f4889 100644 --- a/test/core/transport/test_suite/inproc_fixture.cc +++ b/test/core/transport/test_suite/inproc_fixture.cc @@ -14,7 +14,7 @@ #include "src/core/ext/transport/inproc/inproc_transport.h" #include "src/core/lib/config/core_configuration.h" -#include "test/core/transport/test_suite/fixture.h" +#include "test/core/transport/test_suite/transport_test.h" namespace grpc_core { diff --git a/test/core/transport/test_suite/no_op.cc b/test/core/transport/test_suite/no_op.cc index 26e21e44e32..2f6d38fa9e2 100644 --- a/test/core/transport/test_suite/no_op.cc +++ b/test/core/transport/test_suite/no_op.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "test/core/transport/test_suite/test.h" +#include "test/core/transport/test_suite/transport_test.h" namespace grpc_core { diff --git a/test/core/transport/test_suite/stress.cc b/test/core/transport/test_suite/stress.cc index e791bb18b8f..fc3de87dd84 100644 --- a/test/core/transport/test_suite/stress.cc +++ b/test/core/transport/test_suite/stress.cc @@ -14,7 +14,7 @@ #include "absl/random/random.h" -#include "test/core/transport/test_suite/test.h" +#include "test/core/transport/test_suite/transport_test.h" namespace grpc_core { diff --git a/test/core/transport/test_suite/test.h b/test/core/transport/test_suite/test.h deleted file mode 100644 index 3ce01212326..00000000000 --- a/test/core/transport/test_suite/test.h +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2023 gRPC authors. -// -// 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 -// -// http://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. - -#ifndef GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_TEST_H -#define GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_TEST_H - -#include -#include -#include - -#include "absl/functional/any_invocable.h" -#include "absl/log/log.h" -#include "absl/random/bit_gen_ref.h" -#include "absl/strings/string_view.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "src/core/lib/gprpp/time.h" -#include "src/core/lib/iomgr/timer_manager.h" -#include "src/core/lib/promise/cancel_callback.h" -#include "src/core/lib/promise/promise.h" -#include "src/core/lib/resource_quota/resource_quota.h" -#include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h" -#include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h" -#include "test/core/test_util/test_config.h" -#include "test/core/transport/test_suite/fixture.h" - -namespace grpc_core { - -namespace transport_test_detail { - -struct NameAndLocation { - // NOLINTNEXTLINE - NameAndLocation(const char* name, SourceLocation location = {}) - : location_(location), name_(name) {} - NameAndLocation Next() const { - return NameAndLocation(name_, location_, step_ + 1); - } - - SourceLocation location() const { return location_; } - absl::string_view name() const { return name_; } - int step() const { return step_; } - - private: - NameAndLocation(absl::string_view name, SourceLocation location, int step) - : location_(location), name_(name), step_(step) {} - SourceLocation location_; - absl::string_view name_; - int step_ = 1; -}; - -class ActionState { - public: - enum State : uint8_t { - kNotCreated, - kNotStarted, - kStarted, - kDone, - kCancelled, - }; - - static absl::string_view StateString(State state) { - switch (state) { - case kNotCreated: - return "🚦"; - case kNotStarted: - return "⏰"; - case kStarted: - return "🚗"; - case kDone: - return "🏁"; - case kCancelled: - return "💥"; - } - } - - explicit ActionState(NameAndLocation name_and_location); - - State Get() const { return state_; } - void Set(State state, SourceLocation whence = {}) { - LOG(INFO) << StateString(state) << " " << name() << " [" << step() << "] " - << file() << ":" << line() << " @ " << whence.file() << ":" - << whence.line(); - state_ = state; - } - const NameAndLocation& name_and_location() const { - return name_and_location_; - } - SourceLocation location() const { return name_and_location().location(); } - const char* file() const { return location().file(); } - int line() const { return location().line(); } - absl::string_view name() const { return name_and_location().name(); } - int step() const { return name_and_location().step(); } - bool IsDone(); - - private: - const NameAndLocation name_and_location_; - std::atomic state_; -}; - -using PromiseSpawner = std::function)>; -using ActionStateFactory = - absl::FunctionRef(NameAndLocation)>; - -template -PromiseSpawner SpawnerForContext( - Context context, - grpc_event_engine::experimental::EventEngine* event_engine) { - return [context = std::move(context), event_engine]( - absl::string_view name, Promise promise) mutable { - // Pass new promises via event engine to allow fuzzers to explore - // reorderings of possibly interleaved spawns. - event_engine->Run([name, context = std::move(context), - promise = std::move(promise)]() mutable { - context.SpawnInfallible(name, std::move(promise)); - }); - }; -} - -template -using NextSpawner = absl::AnyInvocable; - -template -Promise WrapPromiseAndNext(std::shared_ptr action_state, - P promise, NextSpawner next) { - return Promise(OnCancel( - [action_state, promise = std::move(promise), - next = std::move(next)]() mutable -> Poll { - action_state->Set(ActionState::kStarted); - auto r = promise(); - if (auto* p = r.value_if_ready()) { - action_state->Set(ActionState::kDone); - next(std::move(*p)); - return Empty{}; - } else { - return Pending{}; - } - }, - [action_state]() { action_state->Set(ActionState::kCancelled); })); -} - -template -NextSpawner WrapFollowUps(NameAndLocation, ActionStateFactory, - PromiseSpawner) { - return [](Empty) {}; -} - -template -NextSpawner WrapFollowUps(NameAndLocation loc, - ActionStateFactory action_state_factory, - PromiseSpawner spawner, FirstFollowUp first, - FollowUps... follow_ups) { - using Factory = promise_detail::OncePromiseFactory; - using FactoryPromise = typename Factory::Promise; - using Result = typename FactoryPromise::Result; - auto action_state = action_state_factory(loc); - return [spawner, factory = Factory(std::move(first)), - next = WrapFollowUps(loc.Next(), action_state_factory, - spawner, std::move(follow_ups)...), - action_state = std::move(action_state), - name = loc.name()](Arg arg) mutable { - action_state->Set(ActionState::kNotStarted); - spawner(name, - WrapPromiseAndNext(std::move(action_state), - factory.Make(std::move(arg)), std::move(next))); - }; -} - -template -void StartSeq(NameAndLocation loc, ActionStateFactory action_state_factory, - PromiseSpawner spawner, First first, FollowUps... followups) { - using Factory = promise_detail::OncePromiseFactory; - using FactoryPromise = typename Factory::Promise; - using Result = typename FactoryPromise::Result; - auto action_state = action_state_factory(loc); - auto next = WrapFollowUps(loc.Next(), action_state_factory, spawner, - std::move(followups)...); - spawner( - loc.name(), - [spawner, first = Factory(std::move(first)), next = std::move(next), - action_state = std::move(action_state), name = loc.name()]() mutable { - action_state->Set(ActionState::kNotStarted); - auto promise = first.Make(); - spawner(name, WrapPromiseAndNext(std::move(action_state), - std::move(promise), std::move(next))); - return Empty{}; - }); -} - -}; // namespace transport_test_detail - -class TransportTest : public ::testing::Test { - public: - void RunTest(); - - protected: - TransportTest(std::unique_ptr fixture, - const fuzzing_event_engine::Actions& actions, - absl::BitGenRef rng) - : event_engine_(std::make_shared< - grpc_event_engine::experimental::FuzzingEventEngine>( - []() { - grpc_timer_manager_set_threading(false); - grpc_event_engine::experimental::FuzzingEventEngine::Options - options; - return options; - }(), - actions)), - fixture_(std::move(fixture)), - rng_(rng) {} - - void SetServerCallDestination(); - CallInitiator CreateCall(ClientMetadataHandle client_initial_metadata); - - std::string RandomString(int min_length, int max_length, - absl::string_view character_set); - std::string RandomStringFrom( - std::initializer_list choices); - std::string RandomMetadataKey(); - std::string RandomMetadataValue(absl::string_view key); - std::string RandomMetadataBinaryKey(); - std::string RandomMetadataBinaryValue(); - std::vector> RandomMetadata(); - std::string RandomMessage(); - absl::BitGenRef rng() { return rng_; } - - CallHandler TickUntilServerCall(); - void WaitForAllPendingWork(); - - auto MakeCall(ClientMetadataHandle client_initial_metadata) { - auto* arena = call_arena_allocator_->MakeArena(); - return MakeCallPair(std::move(client_initial_metadata), event_engine_.get(), - arena, call_arena_allocator_, nullptr); - } - - // Alternative for Seq for test driver code. - // Registers each step so that WaitForAllPendingWork() can report progress, - // and wait for completion... AND generate good failure messages when a - // sequence doesn't complete in a timely manner. - template - void SpawnTestSeq(Context context, - transport_test_detail::NameAndLocation name_and_location, - Actions... actions) { - transport_test_detail::StartSeq( - name_and_location, - [this](transport_test_detail::NameAndLocation name_and_location) { - auto action = std::make_shared( - name_and_location); - pending_actions_.push(action); - return action; - }, - transport_test_detail::SpawnerForContext(std::move(context), - event_engine_.get()), - std::move(actions)...); - } - - private: - virtual void TestImpl() = 0; - - void Timeout(); - - class ServerCallDestination final : public UnstartedCallDestination { - public: - void StartCall(UnstartedCallHandler unstarted_call_handler) override; - void Orphaned() override {} - absl::optional PopHandler(); - - private: - std::queue handlers_; - }; - - class WatchDog { - public: - explicit WatchDog(TransportTest* test) : test_(test) {} - ~WatchDog() { test_->event_engine_->Cancel(timer_); } - - private: - TransportTest* const test_; - grpc_event_engine::experimental::EventEngine::TaskHandle const timer_{ - test_->event_engine_->RunAfter(Duration::Minutes(5), - [this]() { test_->Timeout(); })}; - }; - - grpc::testing::TestGrpcScope grpc_scope_; - std::shared_ptr - event_engine_{ - std::make_shared( - []() { - grpc_timer_manager_set_threading(false); - grpc_event_engine::experimental::FuzzingEventEngine::Options - options; - return options; - }(), - fuzzing_event_engine::Actions())}; - std::unique_ptr fixture_; - RefCountedPtr call_arena_allocator_{ - MakeRefCounted( - MakeResourceQuota("test-quota") - ->memory_quota() - ->CreateMemoryAllocator("test-allocator"), - 1024)}; - RefCountedPtr server_call_destination_ = - MakeRefCounted(); - TransportFixture::ClientAndServerTransportPair transport_pair_ = - fixture_->CreateTransportPair(event_engine_); - std::queue> - pending_actions_; - absl::BitGenRef rng_; -}; - -class TransportTestRegistry { - public: - static TransportTestRegistry& Get(); - void RegisterTest( - absl::string_view name, - absl::AnyInvocable, - const fuzzing_event_engine::Actions&, - absl::BitGenRef) const> - create); - - struct Test { - absl::string_view name; - absl::AnyInvocable, - const fuzzing_event_engine::Actions&, - absl::BitGenRef) const> - create; - }; - - const std::vector& tests() const { return tests_; } - - private: - std::vector tests_; -}; - -} // namespace grpc_core - -#define TRANSPORT_TEST(name) \ - class TransportTest_##name : public grpc_core::TransportTest { \ - public: \ - using TransportTest::TransportTest; \ - void TestBody() override { RunTest(); } \ - \ - private: \ - void TestImpl() override; \ - static grpc_core::TransportTest* Create( \ - std::unique_ptr fixture, \ - const fuzzing_event_engine::Actions& actions, absl::BitGenRef rng) { \ - return new TransportTest_##name(std::move(fixture), actions, rng); \ - } \ - static int registered_; \ - }; \ - int TransportTest_##name::registered_ = \ - (grpc_core::TransportTestRegistry::Get().RegisterTest(#name, &Create), \ - 0); \ - void TransportTest_##name::TestImpl() - -#endif // GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_TEST_H diff --git a/test/core/transport/test_suite/transport_test.cc b/test/core/transport/test_suite/transport_test.cc new file mode 100644 index 00000000000..1dd3d9db500 --- /dev/null +++ b/test/core/transport/test_suite/transport_test.cc @@ -0,0 +1,69 @@ +// Copyright 2023 gRPC authors. +// +// 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 +// +// http://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. + +#include "test/core/transport/test_suite/transport_test.h" + +#include + +#include "absl/random/random.h" + +namespace grpc_core { + +/////////////////////////////////////////////////////////////////////////////// +// TransportTest + +void TransportTest::SetServerCallDestination() { + transport_pair_.server->server_transport()->SetCallDestination( + server_call_destination_); +} + +CallInitiator TransportTest::CreateCall( + ClientMetadataHandle client_initial_metadata) { + auto call = MakeCall(std::move(client_initial_metadata)); + call.handler.SpawnInfallible( + "start-call", [this, handler = call.handler]() mutable { + transport_pair_.client->client_transport()->StartCall( + handler.V2HackToStartCallWithoutACallFilterStack()); + return Empty{}; + }); + return std::move(call.initiator); +} + +CallHandler TransportTest::TickUntilServerCall() { + auto poll = [this]() -> Poll { + auto handler = server_call_destination_->PopHandler(); + if (handler.has_value()) return std::move(*handler); + return Pending(); + }; + return TickUntil(absl::FunctionRef()>(poll)); +} + +/////////////////////////////////////////////////////////////////////////////// +// TransportTest::ServerCallDestination + +void TransportTest::ServerCallDestination::StartCall( + UnstartedCallHandler handler) { + handlers_.push(handler.V2HackToStartCallWithoutACallFilterStack()); +} + +absl::optional TransportTest::ServerCallDestination::PopHandler() { + if (!handlers_.empty()) { + auto handler = std::move(handlers_.front()); + handlers_.pop(); + return handler; + } + return absl::nullopt; +} + +} // namespace grpc_core diff --git a/test/core/transport/test_suite/transport_test.h b/test/core/transport/test_suite/transport_test.h new file mode 100644 index 00000000000..6122b42cf91 --- /dev/null +++ b/test/core/transport/test_suite/transport_test.h @@ -0,0 +1,87 @@ +// Copyright 2023 gRPC authors. +// +// 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 +// +// http://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. + +#ifndef GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_TRANSPORT_TEST_H +#define GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_TRANSPORT_TEST_H + +#include +#include + +#include "absl/random/bit_gen_ref.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "src/core/lib/transport/transport.h" +#include "test/core/call/yodel/yodel_test.h" +#include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h" + +namespace grpc_core { + +struct ClientAndServerTransportPair { + OrphanablePtr client; + OrphanablePtr server; +}; + +using TransportFixture = absl::AnyInvocable) + const>; + +class TransportTest : public YodelTest { + protected: + TransportTest(const TransportFixture& fixture, + const fuzzing_event_engine::Actions& actions, + absl::BitGenRef rng) + : YodelTest(actions, rng), transport_pair_(fixture(event_engine())) {} + + void SetServerCallDestination(); + CallInitiator CreateCall(ClientMetadataHandle client_initial_metadata); + + CallHandler TickUntilServerCall(); + + private: + class ServerCallDestination final : public UnstartedCallDestination { + public: + void StartCall(UnstartedCallHandler unstarted_call_handler) override; + void Orphaned() override {} + absl::optional PopHandler(); + + private: + std::queue handlers_; + }; + + void Shutdown() override { + transport_pair_.client.reset(); + transport_pair_.server.reset(); + } + + RefCountedPtr server_call_destination_ = + MakeRefCounted(); + ClientAndServerTransportPair transport_pair_; +}; + +} // namespace grpc_core + +#define TRANSPORT_TEST(name) YODEL_TEST_P(TransportTest, TransportFixture, name) + +#define TRANSPORT_FIXTURE(name) \ + static grpc_core::ClientAndServerTransportPair name( \ + std::shared_ptr \ + event_engine); \ + YODEL_TEST_PARAM(TransportTest, TransportFixture, name, name); \ + static grpc_core::ClientAndServerTransportPair name( \ + GRPC_UNUSED \ + std::shared_ptr \ + event_engine) + +#endif // GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_TRANSPORT_TEST_H diff --git a/tools/distrib/fix_build_deps.py b/tools/distrib/fix_build_deps.py index 0a114910e3e..d34a0c03f03 100755 --- a/tools/distrib/fix_build_deps.py +++ b/tools/distrib/fix_build_deps.py @@ -65,6 +65,7 @@ EXTERNAL_DEPS = { "absl/functional/function_ref.h": "absl/functional:function_ref", "absl/hash/hash.h": "absl/hash", "absl/log/check.h": "absl/log:check", + "absl/log/log.h": "absl/log", "absl/memory/memory.h": "absl/memory", "absl/meta/type_traits.h": "absl/meta:type_traits", "absl/numeric/int128.h": "absl/numeric:int128", @@ -261,40 +262,44 @@ def grpc_cc_library( global num_opted_out_cc_libraries global parsing_path assert parsing_path is not None - name = "//%s:%s" % (parsing_path, name) - num_cc_libraries += 1 - if select_deps or "nofixdeps" in tags: - if args.whats_left and not select_deps and "nofixdeps" not in tags: - num_opted_out_cc_libraries += 1 - print("Not opted in: {}".format(name)) - no_update.add(name) - scores[name] = len(public_hdrs + hdrs) - # avoid_dep is the internal way of saying prefer something else - # we add grpc_avoid_dep to allow internal grpc-only stuff to avoid each - # other, whilst not biasing dependent projects - if "avoid_dep" in tags or "grpc_avoid_dep" in tags: - avoidness[name] += 10 - if proto: - proto_hdr = "%s%s" % ( - (parsing_path + "/" if parsing_path else ""), - proto.replace(".proto", ".pb.h"), - ) - skip_headers[name].add(proto_hdr) - - for hdr in hdrs + public_hdrs: - vendors[_get_filename(hdr, parsing_path)].append(name) - inc = set() - original_deps[name] = frozenset(deps) - original_external_deps[name] = frozenset(external_deps) - for src in hdrs + public_hdrs + srcs: - for line in open(_get_filename(src, parsing_path)): - m = re.search(r"^#include <(.*)>", line) - if m: - inc.add(m.group(1)) - m = re.search(r'^#include "(.*)"', line) - if m: - inc.add(m.group(1)) - consumes[name] = list(inc) + try: + name = "//%s:%s" % (parsing_path, name) + num_cc_libraries += 1 + if select_deps or "nofixdeps" in tags: + if args.whats_left and not select_deps and "nofixdeps" not in tags: + num_opted_out_cc_libraries += 1 + print("Not opted in: {}".format(name)) + no_update.add(name) + scores[name] = len(public_hdrs + hdrs) + # avoid_dep is the internal way of saying prefer something else + # we add grpc_avoid_dep to allow internal grpc-only stuff to avoid each + # other, whilst not biasing dependent projects + if "avoid_dep" in tags or "grpc_avoid_dep" in tags: + avoidness[name] += 10 + if proto: + proto_hdr = "%s%s" % ( + (parsing_path + "/" if parsing_path else ""), + proto.replace(".proto", ".pb.h"), + ) + skip_headers[name].add(proto_hdr) + + for hdr in hdrs + public_hdrs: + vendors[_get_filename(hdr, parsing_path)].append(name) + inc = set() + original_deps[name] = frozenset(deps) + original_external_deps[name] = frozenset(external_deps) + for src in hdrs + public_hdrs + srcs: + for line in open(_get_filename(src, parsing_path)): + m = re.search(r"^#include <(.*)>", line) + if m: + inc.add(m.group(1)) + m = re.search(r'^#include "(.*)"', line) + if m: + inc.add(m.group(1)) + consumes[name] = list(inc) + except: + print("Error while parsing ", name) + raise def grpc_proto_library(name, srcs, **kwargs): @@ -394,6 +399,7 @@ for dirname in [ "src/cpp/ext/csm", "src/cpp/ext/otel", "test/core/backoff", + "test/core/call/yodel", "test/core/experiments", "test/core/uri", "test/core/test_util",