[transport] Add a transport test suite for promise based transports (#35476)
Implemented for inproc & chaotic-good
Closes #35476
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35476 from ctiller:v3-svall 5358538d54
PiperOrigin-RevId: 599701775
pull/32487/head^2
parent
f41de0825c
commit
1751f1043e
41 changed files with 2846 additions and 166 deletions
@ -0,0 +1,165 @@ |
||||
# 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. |
||||
|
||||
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", |
||||
"//src/core:grpc_transport_inproc", |
||||
], |
||||
alwayslink = 1, |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "chaotic_good_fixture", |
||||
testonly = 1, |
||||
srcs = ["chaotic_good_fixture.cc"], |
||||
external_deps = ["gtest"], |
||||
deps = [ |
||||
"fixture", |
||||
"//src/core:chaotic_good_client_transport", |
||||
"//src/core:chaotic_good_server_transport", |
||||
"//src/core:event_engine_memory_allocator_factory", |
||||
"//src/core:event_engine_tcp_socket_utils", |
||||
"//src/core:grpc_promise_endpoint", |
||||
"//src/core:resource_quota", |
||||
], |
||||
alwayslink = 1, |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "test", |
||||
testonly = 1, |
||||
srcs = ["test.cc"], |
||||
hdrs = ["test.h"], |
||||
external_deps = [ |
||||
"absl/functional:any_invocable", |
||||
"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/util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "call_content", |
||||
testonly = 1, |
||||
srcs = ["call_content.cc"], |
||||
external_deps = ["gtest"], |
||||
deps = ["test"], |
||||
alwayslink = 1, |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "call_shapes", |
||||
testonly = 1, |
||||
srcs = ["call_shapes.cc"], |
||||
deps = ["test"], |
||||
alwayslink = 1, |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "no_op", |
||||
testonly = 1, |
||||
srcs = ["no_op.cc"], |
||||
deps = ["test"], |
||||
alwayslink = 1, |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "stress", |
||||
testonly = 1, |
||||
srcs = ["stress.cc"], |
||||
external_deps = ["absl/random"], |
||||
deps = ["test"], |
||||
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/util:fuzz_config_vars_proto", |
||||
], |
||||
) |
||||
|
||||
grpc_transport_test( |
||||
name = "inproc", |
||||
deps = [ |
||||
":call_content", |
||||
":call_shapes", |
||||
":inproc_fixture", |
||||
":no_op", |
||||
":stress", |
||||
], |
||||
) |
||||
|
||||
grpc_transport_test( |
||||
name = "chaotic_good", |
||||
deps = [ |
||||
":call_content", |
||||
":call_shapes", |
||||
":chaotic_good_fixture", |
||||
":no_op", |
||||
":stress", |
||||
], |
||||
) |
@ -0,0 +1,151 @@ |
||||
// 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 "gmock/gmock.h" |
||||
|
||||
#include "test/core/transport/test_suite/test.h" |
||||
|
||||
using testing::UnorderedElementsAreArray; |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace { |
||||
class LoweringEncoder { |
||||
public: |
||||
std::vector<std::pair<std::string, std::string>> Take() { |
||||
return std::move(metadata_); |
||||
} |
||||
|
||||
void Encode(const Slice& key, const Slice& value) { |
||||
metadata_.emplace_back(key.as_string_view(), value.as_string_view()); |
||||
} |
||||
|
||||
template <typename Which> |
||||
void Encode(Which, const typename Which::ValueType& value) { |
||||
metadata_.emplace_back(Which::key(), Which::Encode(value).as_string_view()); |
||||
} |
||||
|
||||
private: |
||||
std::vector<std::pair<std::string, std::string>> metadata_; |
||||
}; |
||||
|
||||
std::vector<std::pair<std::string, std::string>> LowerMetadata( |
||||
const grpc_metadata_batch& metadata) { |
||||
LoweringEncoder encoder; |
||||
metadata.Encode(&encoder); |
||||
return encoder.Take(); |
||||
} |
||||
|
||||
void FillMetadata(const std::vector<std::pair<std::string, std::string>>& md, |
||||
grpc_metadata_batch& out) { |
||||
for (const auto& p : md) { |
||||
out.Append(p.first, Slice::FromCopiedString(p.second), |
||||
[&](absl::string_view error, const Slice& value) { |
||||
Crash(absl::StrCat( |
||||
"Failed to parse metadata for '", p.first, "': ", error, |
||||
" value=", absl::CEscape(value.as_string_view()))); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
TRANSPORT_TEST(UnaryWithSomeContent) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
const auto client_initial_metadata = RandomMetadata(); |
||||
const auto server_initial_metadata = RandomMetadata(); |
||||
const auto server_trailing_metadata = RandomMetadata(); |
||||
const auto client_payload = RandomMessage(); |
||||
const auto server_payload = RandomMessage(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
FillMetadata(client_initial_metadata, *md); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString(client_payload)), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
initiator.FinishSends(); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_THAT(LowerMetadata(***md), |
||||
UnorderedElementsAreArray(server_initial_metadata)); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), server_payload); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_THAT(LowerMetadata(**md), |
||||
UnorderedElementsAreArray(server_trailing_metadata)); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_THAT(LowerMetadata(**md), |
||||
UnorderedElementsAreArray(client_initial_metadata)); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), client_payload); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
FillMetadata(server_initial_metadata, *md); |
||||
return handler.PushServerInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString(server_payload)), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
FillMetadata(server_trailing_metadata, *md); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,681 @@ |
||||
// 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/test.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TRANSPORT_TEST(MetadataOnlyRequest) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
initiator.FinishSends(); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(ContentTypeMetadata(), ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(MetadataOnlyRequestServerAbortsAfterInitialMetadata) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
// We don't close the sending stream here.
|
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> got_md) { |
||||
EXPECT_TRUE(got_md.ok()); |
||||
EXPECT_EQ( |
||||
got_md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
// Don't wait for end of stream for client->server messages, just
|
||||
// publish initial then trailing metadata.
|
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(ContentTypeMetadata(), ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(MetadataOnlyRequestServerAbortsImmediately) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
// We don't close the sending stream here.
|
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_FALSE(md.value().has_value()); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> got_md) { |
||||
EXPECT_TRUE(got_md.ok()); |
||||
EXPECT_EQ( |
||||
got_md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
// Don't wait for end of stream for client->server messages, just
|
||||
// and don't send initial metadata - just trailing metadata.
|
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(CanCreateCallThenAbandonIt) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "start-call", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq(initiator, "end-call", [&]() { |
||||
initiator.Cancel(); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(UnaryRequest) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
initiator.FinishSends(); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(ContentTypeMetadata(), ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(UnaryRequestOmitCheckEndOfStream) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
initiator.FinishSends(); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor"); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world"); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(ContentTypeMetadata(), ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(UnaryRequestWaitForServerInitialMetadataBeforeSendingPayload) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
initiator.FinishSends(); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
auto md_out = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md_out->Set(ContentTypeMetadata(), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md_out)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(ClientStreamingRequest) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world (2)")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world (3)")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world (4)")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("hello world (5)")), 0)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
initiator.FinishSends(); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
auto md_out = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md_out->Set(ContentTypeMetadata(), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md_out)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world (2)"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world (3)"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world (4)"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), "hello world (5)"); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
TRANSPORT_TEST(ServerStreamingRequest) { |
||||
SetServerAcceptor(); |
||||
auto initiator = CreateCall(); |
||||
SpawnTestSeq( |
||||
initiator, "initiator", |
||||
[&]() { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), Slice::FromExternalString("/foo/bar")); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<absl::optional<ServerMetadataHandle>> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
initiator.FinishSends(); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor (2)"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor (3)"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor (4)"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor (5)"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
"why hello neighbor (6)"); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
auto handler = TickUntilServerCall(); |
||||
SpawnTestSeq( |
||||
handler, "handler", [&] { return handler.PullClientInitialMetadata(); }, |
||||
[&](ValueOrFailure<ServerMetadataHandle> md) { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
"/foo/bar"); |
||||
auto md_out = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md_out->Set(ContentTypeMetadata(), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md_out)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[&](NextResult<MessageHandle> msg) { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor (2)")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor (3)")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor (4)")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor (5)")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString("why hello neighbor (6)")), 0)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[&](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,117 @@ |
||||
// 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 <memory> |
||||
|
||||
#include "gmock/gmock.h" |
||||
|
||||
#include "src/core/ext/transport/chaotic_good/client_transport.h" |
||||
#include "src/core/ext/transport/chaotic_good/server_transport.h" |
||||
#include "src/core/lib/event_engine/memory_allocator_factory.h" |
||||
#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" |
||||
|
||||
using grpc_event_engine::experimental::EndpointConfig; |
||||
using grpc_event_engine::experimental::EventEngine; |
||||
using grpc_event_engine::experimental::FuzzingEventEngine; |
||||
using grpc_event_engine::experimental::MemoryQuotaBasedMemoryAllocatorFactory; |
||||
using grpc_event_engine::experimental::URIToResolvedAddress; |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace { |
||||
|
||||
class MockEndpointConfig : public EndpointConfig { |
||||
public: |
||||
MOCK_METHOD(absl::optional<int>, GetInt, (absl::string_view key), |
||||
(const, override)); |
||||
MOCK_METHOD(absl::optional<absl::string_view>, GetString, |
||||
(absl::string_view key), (const, override)); |
||||
MOCK_METHOD(void*, GetVoidPointer, (absl::string_view key), |
||||
(const, override)); |
||||
}; |
||||
|
||||
struct EndpointPair { |
||||
std::unique_ptr<PromiseEndpoint> client; |
||||
std::unique_ptr<PromiseEndpoint> server; |
||||
}; |
||||
|
||||
EndpointPair CreateEndpointPair( |
||||
grpc_event_engine::experimental::FuzzingEventEngine* event_engine, |
||||
ResourceQuotaRefPtr resource_quota, int port) { |
||||
std::unique_ptr<EventEngine::Endpoint> client_endpoint; |
||||
std::unique_ptr<EventEngine::Endpoint> server_endpoint; |
||||
|
||||
const auto resolved_address = |
||||
URIToResolvedAddress(absl::StrCat("ipv4:127.0.0.1:", port)).value(); |
||||
|
||||
::testing::StrictMock<MockEndpointConfig> endpoint_config; |
||||
auto listener = *event_engine->CreateListener( |
||||
[&server_endpoint](std::unique_ptr<EventEngine::Endpoint> endpoint, |
||||
MemoryAllocator) { |
||||
server_endpoint = std::move(endpoint); |
||||
}, |
||||
[](absl::Status) {}, endpoint_config, |
||||
std::make_unique<MemoryQuotaBasedMemoryAllocatorFactory>( |
||||
resource_quota->memory_quota())); |
||||
GPR_ASSERT(listener->Bind(resolved_address).ok()); |
||||
GPR_ASSERT(listener->Start().ok()); |
||||
|
||||
event_engine->Connect( |
||||
[&client_endpoint]( |
||||
absl::StatusOr<std::unique_ptr<EventEngine::Endpoint>> endpoint) { |
||||
GPR_ASSERT(endpoint.ok()); |
||||
client_endpoint = std::move(endpoint).value(); |
||||
}, |
||||
resolved_address, endpoint_config, |
||||
resource_quota->memory_quota()->CreateMemoryAllocator("client"), |
||||
Duration::Hours(3)); |
||||
|
||||
while (client_endpoint == nullptr || server_endpoint == nullptr) { |
||||
event_engine->Tick(); |
||||
} |
||||
|
||||
return EndpointPair{std::make_unique<PromiseEndpoint>( |
||||
std::move(client_endpoint), SliceBuffer()), |
||||
std::make_unique<PromiseEndpoint>( |
||||
std::move(server_endpoint), SliceBuffer())}; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
TRANSPORT_FIXTURE(ChaoticGood) { |
||||
auto resource_quota = MakeResourceQuota("test"); |
||||
EndpointPair control_endpoints = |
||||
CreateEndpointPair(event_engine.get(), resource_quota, 1234); |
||||
EndpointPair data_endpoints = |
||||
CreateEndpointPair(event_engine.get(), resource_quota, 4321); |
||||
auto channel_args = |
||||
ChannelArgs() |
||||
.SetObject(resource_quota) |
||||
.SetObject(std::static_pointer_cast<EventEngine>(event_engine)); |
||||
auto client_transport = |
||||
MakeOrphanable<chaotic_good::ChaoticGoodClientTransport>( |
||||
std::move(control_endpoints.client), std::move(data_endpoints.client), |
||||
event_engine); |
||||
auto server_transport = |
||||
MakeOrphanable<chaotic_good::ChaoticGoodServerTransport>( |
||||
channel_args, std::move(control_endpoints.server), |
||||
std::move(data_endpoints.server), event_engine); |
||||
return ClientAndServerTransportPair{std::move(client_transport), |
||||
std::move(server_transport)}; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1 @@ |
||||
|
@ -0,0 +1,29 @@ |
||||
// 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<TransportFixture*() const> create) { |
||||
fixtures_.push_back({name, std::move(create)}); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,77 @@ |
||||
// 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<Transport> client; |
||||
OrphanablePtr<Transport> server; |
||||
}; |
||||
virtual ~TransportFixture() = default; |
||||
virtual ClientAndServerTransportPair CreateTransportPair( |
||||
std::shared_ptr<grpc_event_engine::experimental::FuzzingEventEngine> |
||||
event_engine) = 0; |
||||
}; |
||||
|
||||
class TransportFixtureRegistry { |
||||
public: |
||||
static TransportFixtureRegistry& Get(); |
||||
void RegisterFixture(absl::string_view name, |
||||
absl::AnyInvocable<TransportFixture*() const> create); |
||||
|
||||
struct Fixture { |
||||
absl::string_view name; |
||||
absl::AnyInvocable<TransportFixture*() const> create; |
||||
}; |
||||
|
||||
const std::vector<Fixture>& fixtures() const { return fixtures_; } |
||||
|
||||
private: |
||||
std::vector<Fixture> fixtures_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#define TRANSPORT_FIXTURE(name) \ |
||||
class TransportFixture_##name : public grpc_core::TransportFixture { \
|
||||
public: \
|
||||
using TransportFixture::TransportFixture; \
|
||||
ClientAndServerTransportPair CreateTransportPair( \
|
||||
std::shared_ptr<grpc_event_engine::experimental::FuzzingEventEngine> \
|
||||
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<grpc_event_engine::experimental::FuzzingEventEngine> \
|
||||
event_engine GRPC_UNUSED) |
||||
|
||||
#endif // GRPC_TEST_CORE_TRANSPORT_TEST_SUITE_FIXTURE_H
|
@ -0,0 +1,29 @@ |
||||
// 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. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package transport_test_suite; |
||||
|
||||
import "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.proto"; |
||||
import "test/core/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; |
||||
repeated uint64 rng = 12; |
||||
} |
@ -0,0 +1,72 @@ |
||||
// 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 <stdio.h> |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/event_engine/event_engine.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
#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/event_engine/fuzzing_event_engine/fuzzing_event_engine.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 "test/core/util/fuzz_config_vars.h" |
||||
#include "test/core/util/proto_bit_gen.h" |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
extern bool g_event_engine_supports_fd; |
||||
} |
||||
} // namespace grpc_event_engine
|
||||
|
||||
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(); |
||||
GPR_ASSERT(!tests.empty()); |
||||
GPR_ASSERT(!fixtures.empty()); |
||||
const int test_id = msg.test_id() % tests.size(); |
||||
const int fixture_id = msg.fixture_id() % fixtures.size(); |
||||
|
||||
if (squelch && !grpc_core::GetEnv("GRPC_TRACE_FUZZER").has_value()) { |
||||
gpr_set_log_function(dont_log); |
||||
} |
||||
|
||||
grpc_core::ConfigVars::Overrides overrides = |
||||
grpc_core::OverridesFromFuzzConfigVars(msg.config_vars()); |
||||
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()); |
||||
} |
||||
grpc_core::ProtoBitGen bitgen(msg.rng()); |
||||
auto test = |
||||
tests[test_id].create(std::unique_ptr<grpc_core::TransportFixture>( |
||||
fixtures[fixture_id].create()), |
||||
msg.event_engine_actions(), bitgen); |
||||
test->RunTest(); |
||||
delete test; |
||||
GPR_ASSERT(!::testing::Test::HasFailure()); |
||||
} |
@ -0,0 +1,58 @@ |
||||
# 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. |
||||
|
||||
""" |
||||
Generate one transport test & associated fuzzer |
||||
""" |
||||
|
||||
load("//bazel:grpc_build_system.bzl", "grpc_cc_test") |
||||
load("//test/core/util:grpc_fuzzer.bzl", "grpc_proto_fuzzer") |
||||
|
||||
def grpc_transport_test(name, deps): |
||||
grpc_cc_test( |
||||
name = name + "_test", |
||||
srcs = [], |
||||
tags = ["no_windows", "no_mac"], |
||||
deps = [ |
||||
":test_main", |
||||
] + deps, |
||||
) |
||||
|
||||
grpc_proto_fuzzer( |
||||
name = name + "_fuzzer", |
||||
srcs = ["fuzzer_main.cc"], |
||||
tags = ["no_windows", "no_mac"], |
||||
external_deps = [ |
||||
"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/util:fuzz_config_vars", |
||||
"//test/core/util:proto_bit_gen", |
||||
] + deps, |
||||
corpus = "corpus/%s" % name, |
||||
proto = None, |
||||
) |
@ -0,0 +1,25 @@ |
||||
// 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 "src/core/ext/transport/inproc/inproc_transport.h" |
||||
#include "test/core/transport/test_suite/fixture.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TRANSPORT_FIXTURE(Inproc) { |
||||
auto transports = MakeInProcessTransportPair(); |
||||
return {std::move(transports.first), std::move(transports.second)}; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,28 @@ |
||||
// 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/test.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TRANSPORT_TEST(NoOp) {} |
||||
|
||||
TRANSPORT_TEST(WaitForAllPendingWork) { WaitForAllPendingWork(); } |
||||
|
||||
TRANSPORT_TEST(SetServerAcceptorAndFinish) { |
||||
SetServerAcceptor(); |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,131 @@ |
||||
// 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 "absl/random/random.h" |
||||
|
||||
#include "test/core/transport/test_suite/test.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TRANSPORT_TEST(ManyUnaryRequests) { |
||||
SetServerAcceptor(); |
||||
const int kNumRequests = absl::LogUniform<int>(rng(), 10, 100); |
||||
std::list<std::string> call_names; |
||||
auto make_call_name = [&call_names](int i, |
||||
absl::string_view suffix) -> const char* { |
||||
call_names.emplace_back(absl::StrCat("call-", i, "-", suffix)); |
||||
return call_names.back().c_str(); |
||||
}; |
||||
std::map<int, std::string> client_messages; |
||||
std::map<int, std::string> server_messages; |
||||
for (int i = 0; i < kNumRequests; i++) { |
||||
auto initiator = CreateCall(); |
||||
client_messages[i] = RandomMessage(); |
||||
server_messages[i] = RandomMessage(); |
||||
SpawnTestSeq( |
||||
initiator, make_call_name(i, "initiator"), |
||||
[initiator, i]() mutable { |
||||
auto md = Arena::MakePooled<ClientMetadata>(GetContext<Arena>()); |
||||
md->Set(HttpPathMetadata(), |
||||
Slice::FromCopiedString(std::to_string(i))); |
||||
return initiator.PushClientInitialMetadata(std::move(md)); |
||||
}, |
||||
[initiator, i, &client_messages](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
return initiator.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer(Slice::FromCopiedString(client_messages[i])), 0)); |
||||
}, |
||||
[initiator](StatusFlag status) mutable { |
||||
EXPECT_TRUE(status.ok()); |
||||
initiator.FinishSends(); |
||||
return initiator.PullServerInitialMetadata(); |
||||
}, |
||||
[initiator]( |
||||
ValueOrFailure<absl::optional<ServerMetadataHandle>> md) mutable { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(md.value().has_value()); |
||||
EXPECT_EQ(*md.value().value()->get_pointer(ContentTypeMetadata()), |
||||
ContentTypeMetadata::kApplicationGrpc); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[initiator, i, |
||||
&server_messages](NextResult<MessageHandle> msg) mutable { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
server_messages[i]); |
||||
return initiator.PullMessage(); |
||||
}, |
||||
[initiator](NextResult<MessageHandle> msg) mutable { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
return initiator.PullServerTrailingMetadata(); |
||||
}, |
||||
[initiator](ValueOrFailure<ServerMetadataHandle> md) mutable { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_EQ(*md.value()->get_pointer(GrpcStatusMetadata()), |
||||
GRPC_STATUS_UNIMPLEMENTED); |
||||
return Empty{}; |
||||
}); |
||||
} |
||||
for (int i = 0; i < kNumRequests; i++) { |
||||
auto handler = TickUntilServerCall(); |
||||
auto this_call_index = std::make_shared<int>(-1); |
||||
SpawnTestSeq( |
||||
handler, make_call_name(i, "handler"), |
||||
[handler]() mutable { return handler.PullClientInitialMetadata(); }, |
||||
[handler, |
||||
this_call_index](ValueOrFailure<ServerMetadataHandle> md) mutable { |
||||
EXPECT_TRUE(md.ok()); |
||||
EXPECT_TRUE(absl::SimpleAtoi( |
||||
md.value()->get_pointer(HttpPathMetadata())->as_string_view(), |
||||
&*this_call_index)); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[handler, this_call_index, |
||||
&client_messages](NextResult<MessageHandle> msg) mutable { |
||||
EXPECT_TRUE(msg.has_value()); |
||||
EXPECT_EQ(msg.value()->payload()->JoinIntoString(), |
||||
client_messages[*this_call_index]); |
||||
return handler.PullMessage(); |
||||
}, |
||||
[handler](NextResult<MessageHandle> msg) mutable { |
||||
EXPECT_FALSE(msg.has_value()); |
||||
EXPECT_FALSE(msg.cancelled()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(ContentTypeMetadata(), ContentTypeMetadata::kApplicationGrpc); |
||||
return handler.PushServerInitialMetadata(std::move(md)); |
||||
}, |
||||
[handler, this_call_index, |
||||
&server_messages](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return handler.PushMessage(Arena::MakePooled<Message>( |
||||
SliceBuffer( |
||||
Slice::FromCopiedString(server_messages[*this_call_index])), |
||||
0)); |
||||
}, |
||||
[handler](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
auto md = Arena::MakePooled<ServerMetadata>(GetContext<Arena>()); |
||||
md->Set(GrpcStatusMetadata(), GRPC_STATUS_UNIMPLEMENTED); |
||||
return handler.PushServerTrailingMetadata(std::move(md)); |
||||
}, |
||||
[handler](StatusFlag result) mutable { |
||||
EXPECT_TRUE(result.ok()); |
||||
return Empty{}; |
||||
}); |
||||
} |
||||
WaitForAllPendingWork(); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,271 @@ |
||||
// 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/test.h" |
||||
|
||||
#include <initializer_list> |
||||
|
||||
#include "absl/random/random.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TransportTestRegistry
|
||||
|
||||
TransportTestRegistry& TransportTestRegistry::Get() { |
||||
static TransportTestRegistry* registry = new TransportTestRegistry(); |
||||
return *registry; |
||||
} |
||||
|
||||
void TransportTestRegistry::RegisterTest( |
||||
absl::string_view name, |
||||
absl::AnyInvocable<TransportTest*(std::unique_ptr<TransportFixture>, |
||||
const fuzzing_event_engine::Actions&, |
||||
absl::BitGenRef) const> |
||||
create) { |
||||
if (absl::StartsWith(name, "DISABLED_")) return; |
||||
tests_.push_back({name, std::move(create)}); |
||||
} |
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TransportTest
|
||||
|
||||
void TransportTest::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(); |
||||
event_engine_->TickUntilIdle(); |
||||
event_engine_->UnsetGlobalHooks(); |
||||
} |
||||
|
||||
void TransportTest::SetServerAcceptor() { |
||||
transport_pair_.server->server_transport()->SetAcceptor(&acceptor_); |
||||
} |
||||
|
||||
CallInitiator TransportTest::CreateCall() { |
||||
auto call = MakeCall(event_engine_.get(), Arena::Create(1024, &allocator_)); |
||||
call.handler.SpawnInfallible("start-call", [this, handler = call.handler]() { |
||||
transport_pair_.client->client_transport()->StartCall(handler); |
||||
return Empty{}; |
||||
}); |
||||
return std::move(call.initiator); |
||||
} |
||||
|
||||
CallHandler TransportTest::TickUntilServerCall() { |
||||
WatchDog watchdog(this); |
||||
for (;;) { |
||||
auto handler = acceptor_.PopHandler(); |
||||
if (handler.has_value()) return std::move(*handler); |
||||
event_engine_->Tick(); |
||||
} |
||||
} |
||||
|
||||
void TransportTest::WaitForAllPendingWork() { |
||||
WatchDog watchdog(this); |
||||
while (!pending_actions_.empty()) { |
||||
if (pending_actions_.front()->IsDone()) { |
||||
pending_actions_.pop(); |
||||
continue; |
||||
} |
||||
event_engine_->Tick(); |
||||
} |
||||
} |
||||
|
||||
void TransportTest::Timeout() { |
||||
std::vector<std::string> lines; |
||||
lines.emplace_back("Timeout waiting for pending actions to complete"); |
||||
while (!pending_actions_.empty()) { |
||||
auto action = std::move(pending_actions_.front()); |
||||
pending_actions_.pop(); |
||||
if (action->IsDone()) continue; |
||||
absl::string_view state_name = |
||||
transport_test_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) { |
||||
file_name = file_name.substr(pos + 1); |
||||
} |
||||
lines.emplace_back(absl::StrCat(" ", state_name, " ", action->name(), " [", |
||||
action->step(), "]: ", file_name, ":", |
||||
action->line())); |
||||
} |
||||
Crash(absl::StrJoin(lines, "\n")); |
||||
} |
||||
|
||||
std::string TransportTest::RandomString(int min_length, int max_length, |
||||
absl::string_view character_set) { |
||||
std::string out; |
||||
int length = absl::LogUniform<int>(rng_, min_length, max_length + 1); |
||||
for (int i = 0; i < length; ++i) { |
||||
out.push_back( |
||||
character_set[absl::Uniform<uint8_t>(rng_, 0, character_set.size())]); |
||||
} |
||||
return out; |
||||
} |
||||
|
||||
std::string TransportTest::RandomStringFrom( |
||||
std::initializer_list<absl::string_view> choices) { |
||||
size_t idx = absl::Uniform<size_t>(rng_, 0, choices.size()); |
||||
auto it = choices.begin(); |
||||
for (size_t i = 0; i < idx; ++i) ++it; |
||||
return std::string(*it); |
||||
} |
||||
|
||||
std::string TransportTest::RandomMetadataKey() { |
||||
if (absl::Bernoulli(rng_, 0.1)) { |
||||
return RandomStringFrom({ |
||||
":path", |
||||
":method", |
||||
":status", |
||||
":authority", |
||||
":scheme", |
||||
}); |
||||
} |
||||
std::string out; |
||||
do { |
||||
out = RandomString(1, 128, "abcdefghijklmnopqrstuvwxyz-_"); |
||||
} while (absl::EndsWith(out, "-bin")); |
||||
return out; |
||||
} |
||||
|
||||
std::string TransportTest::RandomMetadataValue(absl::string_view key) { |
||||
if (key == ":method") { |
||||
return RandomStringFrom({"GET", "POST", "PUT"}); |
||||
} |
||||
if (key == ":status") { |
||||
return absl::StrCat(absl::Uniform<int>(rng_, 100, 600)); |
||||
} |
||||
if (key == ":scheme") { |
||||
return RandomStringFrom({"http", "https"}); |
||||
} |
||||
if (key == "te") { |
||||
return "trailers"; |
||||
} |
||||
static const NoDestruct<std::string> kChars{[]() { |
||||
std::string out; |
||||
for (char c = 32; c < 127; c++) out.push_back(c); |
||||
return out; |
||||
}()}; |
||||
return RandomString(0, 128, *kChars); |
||||
} |
||||
|
||||
std::string TransportTest::RandomMetadataBinaryKey() { |
||||
return RandomString(1, 128, "abcdefghijklmnopqrstuvwxyz-_") + "-bin"; |
||||
} |
||||
|
||||
std::string TransportTest::RandomMetadataBinaryValue() { |
||||
static const NoDestruct<std::string> kChars{[]() { |
||||
std::string out; |
||||
for (int c = 0; c < 256; c++) { |
||||
out.push_back(static_cast<char>(static_cast<uint8_t>(c))); |
||||
} |
||||
return out; |
||||
}()}; |
||||
return RandomString(0, 4096, *kChars); |
||||
} |
||||
|
||||
std::vector<std::pair<std::string, std::string>> |
||||
TransportTest::RandomMetadata() { |
||||
size_t size = 0; |
||||
const size_t max_size = absl::LogUniform<size_t>(rng_, 64, 8000); |
||||
std::vector<std::pair<std::string, std::string>> out; |
||||
for (;;) { |
||||
std::string key; |
||||
std::string value; |
||||
if (absl::Bernoulli(rng_, 0.1)) { |
||||
key = RandomMetadataBinaryKey(); |
||||
value = RandomMetadataBinaryValue(); |
||||
} else { |
||||
key = RandomMetadataKey(); |
||||
value = RandomMetadataValue(key); |
||||
} |
||||
bool include = true; |
||||
for (size_t i = 0; i < out.size(); ++i) { |
||||
if (out[i].first == key) { |
||||
include = false; |
||||
break; |
||||
} |
||||
} |
||||
if (!include) continue; |
||||
size_t this_size = 32 + key.size() + value.size(); |
||||
if (size + this_size > max_size) { |
||||
if (out.empty()) continue; |
||||
break; |
||||
} |
||||
size += this_size; |
||||
out.emplace_back(std::move(key), std::move(value)); |
||||
} |
||||
return out; |
||||
} |
||||
|
||||
std::string TransportTest::RandomMessage() { |
||||
static const NoDestruct<std::string> kChars{[]() { |
||||
std::string out; |
||||
for (int c = 0; c < 256; c++) { |
||||
out.push_back(static_cast<char>(static_cast<uint8_t>(c))); |
||||
} |
||||
return out; |
||||
}()}; |
||||
return RandomString(0, 1024 * 1024, *kChars); |
||||
} |
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TransportTest::Acceptor
|
||||
|
||||
Arena* TransportTest::Acceptor::CreateArena() { |
||||
return Arena::Create(1024, allocator_); |
||||
} |
||||
|
||||
absl::StatusOr<CallInitiator> TransportTest::Acceptor::CreateCall( |
||||
ClientMetadata&, Arena* arena) { |
||||
auto call = MakeCall(event_engine_, arena); |
||||
handlers_.push(std::move(call.handler)); |
||||
return std::move(call.initiator); |
||||
} |
||||
|
||||
absl::optional<CallHandler> TransportTest::Acceptor::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
|
@ -0,0 +1,364 @@ |
||||
// 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 <initializer_list> |
||||
#include <memory> |
||||
#include <queue> |
||||
|
||||
#include "absl/functional/any_invocable.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/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) { |
||||
gpr_log(GPR_INFO, "%s", |
||||
absl::StrCat(StateString(state), " ", name(), " [", step(), "] ", |
||||
file(), ":", line()) |
||||
.c_str()); |
||||
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> state_; |
||||
}; |
||||
|
||||
using PromiseSpawner = std::function<void(absl::string_view, Promise<Empty>)>; |
||||
using ActionStateFactory = |
||||
absl::FunctionRef<std::shared_ptr<ActionState>(NameAndLocation)>; |
||||
|
||||
template <typename Context> |
||||
PromiseSpawner SpawnerForContext( |
||||
Context context, |
||||
grpc_event_engine::experimental::EventEngine* event_engine) { |
||||
return [context = std::move(context), event_engine]( |
||||
absl::string_view name, Promise<Empty> 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 <typename Arg> |
||||
using NextSpawner = absl::AnyInvocable<void(Arg)>; |
||||
|
||||
template <typename R> |
||||
Promise<Empty> WrapPromiseAndNext(std::shared_ptr<ActionState> action_state, |
||||
Promise<R> promise, NextSpawner<R> next) { |
||||
return Promise<Empty>(OnCancel( |
||||
[action_state, promise = std::move(promise), |
||||
next = std::move(next)]() mutable -> Poll<Empty> { |
||||
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 <typename Arg> |
||||
NextSpawner<Arg> WrapFollowUps(NameAndLocation, ActionStateFactory, |
||||
PromiseSpawner) { |
||||
return [](Empty) {}; |
||||
} |
||||
|
||||
template <typename Arg, typename FirstFollowUp, typename... FollowUps> |
||||
NextSpawner<Arg> WrapFollowUps(NameAndLocation loc, |
||||
ActionStateFactory action_state_factory, |
||||
PromiseSpawner spawner, FirstFollowUp first, |
||||
FollowUps... follow_ups) { |
||||
using Factory = promise_detail::OncePromiseFactory<Arg, FirstFollowUp>; |
||||
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<Result>(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), |
||||
Promise<Result>(factory.Make(std::move(arg))), |
||||
std::move(next))); |
||||
}; |
||||
} |
||||
|
||||
template <typename First, typename... FollowUps> |
||||
void StartSeq(NameAndLocation loc, ActionStateFactory action_state_factory, |
||||
PromiseSpawner spawner, First first, FollowUps... followups) { |
||||
using Factory = promise_detail::OncePromiseFactory<void, First>; |
||||
using FactoryPromise = typename Factory::Promise; |
||||
using Result = typename FactoryPromise::Result; |
||||
auto action_state = action_state_factory(loc); |
||||
auto next = WrapFollowUps<Result>(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); |
||||
spawner(name, WrapPromiseAndNext(std::move(action_state), |
||||
Promise<Result>(first.Make()), |
||||
std::move(next))); |
||||
return Empty{}; |
||||
}); |
||||
} |
||||
|
||||
}; // namespace transport_test_detail
|
||||
|
||||
class TransportTest : public ::testing::Test { |
||||
public: |
||||
void RunTest(); |
||||
|
||||
protected: |
||||
TransportTest(std::unique_ptr<TransportFixture> 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 SetServerAcceptor(); |
||||
CallInitiator CreateCall(); |
||||
|
||||
std::string RandomString(int min_length, int max_length, |
||||
absl::string_view character_set); |
||||
std::string RandomStringFrom( |
||||
std::initializer_list<absl::string_view> choices); |
||||
std::string RandomMetadataKey(); |
||||
std::string RandomMetadataValue(absl::string_view key); |
||||
std::string RandomMetadataBinaryKey(); |
||||
std::string RandomMetadataBinaryValue(); |
||||
std::vector<std::pair<std::string, std::string>> RandomMetadata(); |
||||
std::string RandomMessage(); |
||||
absl::BitGenRef rng() { return rng_; } |
||||
|
||||
CallHandler TickUntilServerCall(); |
||||
void WaitForAllPendingWork(); |
||||
|
||||
// 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 <typename Context, typename... Actions> |
||||
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<transport_test_detail::ActionState>( |
||||
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 Acceptor final : public ServerTransport::Acceptor { |
||||
public: |
||||
Acceptor(grpc_event_engine::experimental::EventEngine* event_engine, |
||||
MemoryAllocator* allocator) |
||||
: event_engine_(event_engine), allocator_(allocator) {} |
||||
|
||||
Arena* CreateArena() override; |
||||
absl::StatusOr<CallInitiator> CreateCall( |
||||
ClientMetadata& client_initial_metadata, Arena* arena) override; |
||||
absl::optional<CallHandler> PopHandler(); |
||||
|
||||
private: |
||||
std::queue<CallHandler> handlers_; |
||||
grpc_event_engine::experimental::EventEngine* const event_engine_; |
||||
MemoryAllocator* const allocator_; |
||||
}; |
||||
|
||||
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(); })}; |
||||
}; |
||||
|
||||
std::shared_ptr<grpc_event_engine::experimental::FuzzingEventEngine> |
||||
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; |
||||
}(), |
||||
fuzzing_event_engine::Actions())}; |
||||
std::unique_ptr<TransportFixture> fixture_; |
||||
MemoryAllocator allocator_ = MakeResourceQuota("test-quota") |
||||
->memory_quota() |
||||
->CreateMemoryAllocator("test-allocator"); |
||||
Acceptor acceptor_{event_engine_.get(), &allocator_}; |
||||
TransportFixture::ClientAndServerTransportPair transport_pair_ = |
||||
fixture_->CreateTransportPair(event_engine_); |
||||
std::queue<std::shared_ptr<transport_test_detail::ActionState>> |
||||
pending_actions_; |
||||
absl::BitGenRef rng_; |
||||
}; |
||||
|
||||
class TransportTestRegistry { |
||||
public: |
||||
static TransportTestRegistry& Get(); |
||||
void RegisterTest( |
||||
absl::string_view name, |
||||
absl::AnyInvocable<TransportTest*(std::unique_ptr<TransportFixture>, |
||||
const fuzzing_event_engine::Actions&, |
||||
absl::BitGenRef) const> |
||||
create); |
||||
|
||||
struct Test { |
||||
absl::string_view name; |
||||
absl::AnyInvocable<TransportTest*(std::unique_ptr<TransportFixture>, |
||||
const fuzzing_event_engine::Actions&, |
||||
absl::BitGenRef) const> |
||||
create; |
||||
}; |
||||
|
||||
const std::vector<Test>& tests() const { return tests_; } |
||||
|
||||
private: |
||||
std::vector<Test> 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<grpc_core::TransportFixture> 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
|
@ -0,0 +1,42 @@ |
||||
// 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 "absl/random/random.h" |
||||
|
||||
#include "src/core/lib/debug/trace.h" |
||||
#include "test/core/transport/test_suite/fixture.h" |
||||
#include "test/core/transport/test_suite/test.h" |
||||
#include "test/core/util/test_config.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<grpc_core::TransportFixture>(fixture->create()), |
||||
fuzzing_event_engine::Actions(), bitgen); |
||||
}); |
||||
} |
||||
} |
||||
grpc_tracer_init(); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue