diff --git a/BUILD b/BUILD
index d07df3ee332..27619bd7e7e 100644
--- a/BUILD
+++ b/BUILD
@@ -2557,6 +2557,7 @@ grpc_cc_library(
"src/core/lib/debug/stats.h",
"src/core/lib/debug/stats_data.h",
"src/core/lib/event_engine/channel_args_endpoint_config.h",
+ "src/core/lib/event_engine/promise.h",
"src/core/lib/iomgr/block_annotate.h",
"src/core/lib/iomgr/buffer_list.h",
"src/core/lib/iomgr/call_combiner.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dedbe0981e5..5f939e1944e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1093,6 +1093,9 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx mock_test)
add_dependencies(buildtests_cxx nonblocking_test)
add_dependencies(buildtests_cxx observable_test)
+ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+ add_dependencies(buildtests_cxx oracle_event_engine_posix_test)
+ endif()
add_dependencies(buildtests_cxx orca_service_end2end_test)
add_dependencies(buildtests_cxx orphanable_test)
add_dependencies(buildtests_cxx out_of_bounds_bad_client_test)
@@ -10852,6 +10855,7 @@ add_executable(fuzzing_event_engine_test
${_gRPC_PROTO_GENS_DIR}/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.grpc.pb.h
test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc
test/core/event_engine/test_suite/event_engine_test.cc
+ test/core/event_engine/test_suite/event_engine_test_utils.cc
test/core/event_engine/test_suite/fuzzing_event_engine_test.cc
test/core/event_engine/test_suite/timer_test.cc
third_party/googletest/googletest/src/gtest-all.cc
@@ -12653,6 +12657,7 @@ if(gRPC_BUILD_TESTS)
add_executable(iomgr_event_engine_test
test/core/event_engine/test_suite/event_engine_test.cc
+ test/core/event_engine/test_suite/event_engine_test_utils.cc
test/core/event_engine/test_suite/iomgr_event_engine_test.cc
test/core/event_engine/test_suite/timer_test.cc
third_party/googletest/googletest/src/gtest-all.cc
@@ -13583,6 +13588,47 @@ target_link_libraries(observable_test
)
+endif()
+if(gRPC_BUILD_TESTS)
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+
+ add_executable(oracle_event_engine_posix_test
+ test/core/event_engine/test_suite/client_test.cc
+ test/core/event_engine/test_suite/event_engine_test.cc
+ test/core/event_engine/test_suite/event_engine_test_utils.cc
+ test/core/event_engine/test_suite/oracle_event_engine_posix.cc
+ test/core/event_engine/test_suite/oracle_event_engine_posix_test.cc
+ third_party/googletest/googletest/src/gtest-all.cc
+ third_party/googletest/googlemock/src/gmock-all.cc
+ )
+
+ target_include_directories(oracle_event_engine_posix_test
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+ ${_gRPC_RE2_INCLUDE_DIR}
+ ${_gRPC_SSL_INCLUDE_DIR}
+ ${_gRPC_UPB_GENERATED_DIR}
+ ${_gRPC_UPB_GRPC_GENERATED_DIR}
+ ${_gRPC_UPB_INCLUDE_DIR}
+ ${_gRPC_XXHASH_INCLUDE_DIR}
+ ${_gRPC_ZLIB_INCLUDE_DIR}
+ third_party/googletest/googletest/include
+ third_party/googletest/googletest
+ third_party/googletest/googlemock/include
+ third_party/googletest/googlemock
+ ${_gRPC_PROTO_GENS_DIR}
+ )
+
+ target_link_libraries(oracle_event_engine_posix_test
+ ${_gRPC_PROTOBUF_LIBRARIES}
+ ${_gRPC_ALLTARGETS_LIBRARIES}
+ grpc_test_util
+ )
+
+
+endif()
endif()
if(gRPC_BUILD_TESTS)
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 1c6fb7a829c..c381e217d24 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -745,6 +745,7 @@ libs:
- src/core/lib/event_engine/event_engine_factory.h
- src/core/lib/event_engine/handle_containers.h
- src/core/lib/event_engine/iomgr_engine.h
+ - src/core/lib/event_engine/promise.h
- src/core/lib/event_engine/trace.h
- src/core/lib/gprpp/atomic_utils.h
- src/core/lib/gprpp/bitset.h
@@ -1955,6 +1956,7 @@ libs:
- src/core/lib/event_engine/event_engine_factory.h
- src/core/lib/event_engine/handle_containers.h
- src/core/lib/event_engine/iomgr_engine.h
+ - src/core/lib/event_engine/promise.h
- src/core/lib/event_engine/trace.h
- src/core/lib/gprpp/atomic_utils.h
- src/core/lib/gprpp/bitset.h
@@ -5990,10 +5992,12 @@ targets:
headers:
- test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h
- test/core/event_engine/test_suite/event_engine_test.h
+ - test/core/event_engine/test_suite/event_engine_test_utils.h
src:
- test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.proto
- test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc
- test/core/event_engine/test_suite/event_engine_test.cc
+ - test/core/event_engine/test_suite/event_engine_test_utils.cc
- test/core/event_engine/test_suite/fuzzing_event_engine_test.cc
- test/core/event_engine/test_suite/timer_test.cc
deps:
@@ -6550,8 +6554,10 @@ targets:
language: c++
headers:
- test/core/event_engine/test_suite/event_engine_test.h
+ - test/core/event_engine/test_suite/event_engine_test_utils.h
src:
- test/core/event_engine/test_suite/event_engine_test.cc
+ - test/core/event_engine/test_suite/event_engine_test_utils.cc
- test/core/event_engine/test_suite/iomgr_event_engine_test.cc
- test/core/event_engine/test_suite/timer_test.cc
deps:
@@ -7069,6 +7075,27 @@ targets:
- absl/types:variant
- absl/utility:utility
uses_polling: false
+- name: oracle_event_engine_posix_test
+ gtest: true
+ build: test
+ language: c++
+ headers:
+ - test/core/event_engine/test_suite/event_engine_test.h
+ - test/core/event_engine/test_suite/event_engine_test_utils.h
+ - test/core/event_engine/test_suite/oracle_event_engine_posix.h
+ src:
+ - test/core/event_engine/test_suite/client_test.cc
+ - test/core/event_engine/test_suite/event_engine_test.cc
+ - test/core/event_engine/test_suite/event_engine_test_utils.cc
+ - test/core/event_engine/test_suite/oracle_event_engine_posix.cc
+ - test/core/event_engine/test_suite/oracle_event_engine_posix_test.cc
+ deps:
+ - grpc_test_util
+ platforms:
+ - linux
+ - posix
+ - mac
+ uses_polling: false
- name: orca_service_end2end_test
gtest: true
build: test
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 73ca24739b2..4464db64ff1 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -676,6 +676,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/event_engine_factory.h',
'src/core/lib/event_engine/handle_containers.h',
'src/core/lib/event_engine/iomgr_engine.h',
+ 'src/core/lib/event_engine/promise.h',
'src/core/lib/event_engine/trace.h',
'src/core/lib/gpr/alloc.h',
'src/core/lib/gpr/env.h',
@@ -1497,6 +1498,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/event_engine_factory.h',
'src/core/lib/event_engine/handle_containers.h',
'src/core/lib/event_engine/iomgr_engine.h',
+ 'src/core/lib/event_engine/promise.h',
'src/core/lib/event_engine/trace.h',
'src/core/lib/gpr/alloc.h',
'src/core/lib/gpr/env.h',
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 8c3264b8d8f..c397402e92c 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -1043,6 +1043,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/iomgr_engine.cc',
'src/core/lib/event_engine/iomgr_engine.h',
'src/core/lib/event_engine/memory_allocator.cc',
+ 'src/core/lib/event_engine/promise.h',
'src/core/lib/event_engine/resolved_address.cc',
'src/core/lib/event_engine/slice.cc',
'src/core/lib/event_engine/slice_buffer.cc',
@@ -2099,6 +2100,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/event_engine_factory.h',
'src/core/lib/event_engine/handle_containers.h',
'src/core/lib/event_engine/iomgr_engine.h',
+ 'src/core/lib/event_engine/promise.h',
'src/core/lib/event_engine/trace.h',
'src/core/lib/gpr/alloc.h',
'src/core/lib/gpr/env.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 07028ed1399..2245d7441a1 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -956,6 +956,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/event_engine/iomgr_engine.cc )
s.files += %w( src/core/lib/event_engine/iomgr_engine.h )
s.files += %w( src/core/lib/event_engine/memory_allocator.cc )
+ s.files += %w( src/core/lib/event_engine/promise.h )
s.files += %w( src/core/lib/event_engine/resolved_address.cc )
s.files += %w( src/core/lib/event_engine/slice.cc )
s.files += %w( src/core/lib/event_engine/slice_buffer.cc )
diff --git a/package.xml b/package.xml
index 1f1c46b56fa..9621b439e30 100644
--- a/package.xml
+++ b/package.xml
@@ -938,6 +938,7 @@
+
diff --git a/src/core/lib/event_engine/promise.h b/src/core/lib/event_engine/promise.h
new file mode 100644
index 00000000000..9891d3bba4d
--- /dev/null
+++ b/src/core/lib/event_engine/promise.h
@@ -0,0 +1,69 @@
+// Copyright 2021 The 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_CORE_LIB_EVENT_ENGINE_PROMISE_H
+#define GRPC_CORE_LIB_EVENT_ENGINE_PROMISE_H
+#include
+
+#include
+
+#include "src/core/lib/gprpp/sync.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+/// A minimal promise implementation.
+///
+/// This is light-duty, syntactical sugar around cv wait & signal, which is
+/// useful in some cases. A more robust implementation is being worked on
+/// separately.
+template
+class Promise {
+ public:
+ // The getter will wait until the setter has been called, and will return the
+ // value passed during Set.
+ T& Get() {
+ grpc_core::MutexLock lock(&mu_);
+ if (!set_) {
+ cv_.Wait(&mu_);
+ }
+ return val_;
+ }
+ // This setter can only be called exactly once without a Reset.
+ // Will automatically unblock getters.
+ void Set(T&& val) {
+ grpc_core::MutexLock lock(&mu_);
+ GPR_ASSERT(!set_);
+ val_ = std::move(val);
+ set_ = true;
+ cv_.SignalAll();
+ }
+
+ // Can only be called after a set operation.
+ void Reset() {
+ grpc_core::MutexLock lock(&mu_);
+ GPR_ASSERT(set_);
+ set_ = false;
+ }
+
+ private:
+ grpc_core::Mutex mu_;
+ grpc_core::CondVar cv_;
+ T val_;
+ bool set_ = false;
+};
+
+} // namespace experimental
+} // namespace grpc_event_engine
+
+#endif // GRPC_CORE_LIB_EVENT_ENGINE_PROMISE_H
diff --git a/test/core/event_engine/test_suite/BUILD b/test/core/event_engine/test_suite/BUILD
index 6b5f47ac0de..60db8b7a321 100644
--- a/test/core/event_engine/test_suite/BUILD
+++ b/test/core/event_engine/test_suite/BUILD
@@ -18,7 +18,10 @@ licenses(["notice"])
grpc_package(name = "test/core/event_engine/test_suite")
-COMMON_HEADERS = ["event_engine_test.h"]
+COMMON_HEADERS = [
+ "event_engine_test.h",
+ "event_engine_test_utils.h",
+]
grpc_cc_library(
name = "timer",
@@ -90,10 +93,46 @@ grpc_cc_test(
# -- Internal targets --
+grpc_cc_library(
+ name = "oracle_event_engine_posix",
+ testonly = True,
+ srcs = ["oracle_event_engine_posix.cc"],
+ hdrs = ["oracle_event_engine_posix.h"],
+ tags = [
+ "no_windows",
+ ],
+ deps = [
+ ":conformance_test_base_lib",
+ "//:grpc",
+ "//test/core/util:grpc_test_util",
+ ],
+)
+
+grpc_cc_test(
+ name = "oracle_event_engine_posix_test",
+ srcs = ["oracle_event_engine_posix_test.cc"],
+ external_deps = ["gtest"],
+ language = "C++",
+ tags = [
+ "no_test_ios",
+ "no_windows",
+ ],
+ uses_polling = False,
+ deps = [
+ ":client",
+ ":oracle_event_engine_posix",
+ "//:grpc",
+ "//test/core/util:grpc_test_util",
+ ],
+)
+
grpc_cc_library(
name = "conformance_test_base_lib",
testonly = True,
- srcs = ["event_engine_test.cc"],
+ srcs = [
+ "event_engine_test.cc",
+ "event_engine_test_utils.cc",
+ ],
hdrs = COMMON_HEADERS,
external_deps = ["gtest"],
deps = [
diff --git a/test/core/event_engine/test_suite/client_test.cc b/test/core/event_engine/test_suite/client_test.cc
index f184c317e44..126a5d72c92 100644
--- a/test/core/event_engine/test_suite/client_test.cc
+++ b/test/core/event_engine/test_suite/client_test.cc
@@ -12,10 +12,272 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include
+#include
+#include
+#include
+
+#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
+
+#include
+#include
+#include
+
+#include "src/core/lib/address_utils/parse_address.h"
+#include "src/core/lib/event_engine/channel_args_endpoint_config.h"
+#include "src/core/lib/event_engine/promise.h"
+#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/uri/uri_parser.h"
#include "test/core/event_engine/test_suite/event_engine_test.h"
+#include "test/core/event_engine/test_suite/event_engine_test_utils.h"
class EventEngineClientTest : public EventEngineTest {};
-// TODO(hork): establish meaningful tests
-TEST_F(EventEngineClientTest, TODO) { grpc_core::ExecCtx exec_ctx; }
+namespace {
+
+using ::grpc_event_engine::experimental::ChannelArgsEndpointConfig;
+using ::grpc_event_engine::experimental::EventEngine;
+using ::grpc_event_engine::experimental::Promise;
+using ::grpc_event_engine::experimental::URIToResolvedAddress;
+using Endpoint = ::grpc_event_engine::experimental::EventEngine::Endpoint;
+using Listener = ::grpc_event_engine::experimental::EventEngine::Listener;
+using namespace std::chrono_literals;
+
+constexpr int kMinMessageSize = 1024;
+constexpr int kMaxMessageSize = 4096;
+constexpr int kNumExchangedMessages = 100;
+
+// Returns a random message with bounded length.
+std::string GetNextSendMessage() {
+ static const char alphanum[] =
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ static std::random_device rd;
+ static std::seed_seq seed{rd()};
+ static std::mt19937 gen(seed);
+ static std::uniform_real_distribution<> dis(kMinMessageSize, kMaxMessageSize);
+ static grpc_core::Mutex g_mu;
+ std::string tmp_s;
+ int len;
+ {
+ grpc_core::MutexLock lock(&g_mu);
+ len = dis(gen);
+ }
+ tmp_s.reserve(len);
+ for (int i = 0; i < len; ++i) {
+ tmp_s += alphanum[rand() % (sizeof(alphanum) - 1)];
+ }
+ return tmp_s;
+}
+
+} // namespace
+
+// Create a connection using the test EventEngine to a non-existent listener
+// and verify that the connection fails.
+TEST_F(EventEngineClientTest, ConnectToNonExistentListenerTest) {
+ grpc_core::ExecCtx ctx;
+ auto test_ee = this->NewEventEngine();
+ Promise> client_endpoint_promise;
+ auto memory_quota = std::make_unique("bar");
+ // Create a test EventEngine client endpoint and connect to a non existent
+ // listener.
+ test_ee->Connect(
+ [&client_endpoint_promise](
+ absl::StatusOr> status) {
+ // Connect should fail.
+ EXPECT_FALSE(status.ok());
+ client_endpoint_promise.Set(nullptr);
+ },
+ URIToResolvedAddress("ipv6:[::1]:7000"),
+ ChannelArgsEndpointConfig(nullptr),
+ memory_quota->CreateMemoryAllocator("conn-1"), 24h);
+
+ auto client_endpoint = std::move(client_endpoint_promise.Get());
+ EXPECT_EQ(client_endpoint, nullptr);
+}
+
+// Create a connection using the test EventEngine to a listener created
+// by the oracle EventEngine and exchange bi-di data over the connection.
+// For each data transfer, verify that data written at one end of the stream
+// equals data read at the other end of the stream.
+TEST_F(EventEngineClientTest, ConnectExchangeBidiDataTransferTest) {
+ grpc_core::ExecCtx ctx;
+ auto oracle_ee = this->NewOracleEventEngine();
+ auto test_ee = this->NewEventEngine();
+ auto memory_quota = std::make_unique("bar");
+ std::string target_addr = "ipv6:[::1]:7000";
+ Promise> client_endpoint_promise;
+ Promise> server_endpoint_promise;
+
+ Listener::AcceptCallback accept_cb =
+ [&server_endpoint_promise](
+ std::unique_ptr ep,
+ grpc_core::MemoryAllocator /*memory_allocator*/) {
+ server_endpoint_promise.Set(std::move(ep));
+ };
+
+ auto status = oracle_ee->CreateListener(
+ std::move(accept_cb),
+ [](absl::Status status) { GPR_ASSERT(status.ok()); },
+ ChannelArgsEndpointConfig(nullptr),
+ std::make_unique("foo"));
+ EXPECT_TRUE(status.ok());
+
+ std::unique_ptr listener = std::move(*status);
+ EXPECT_TRUE(listener->Bind(URIToResolvedAddress(target_addr)).ok());
+ EXPECT_TRUE(listener->Start().ok());
+
+ test_ee->Connect(
+ [&client_endpoint_promise](
+ absl::StatusOr> status) {
+ if (!status.ok()) {
+ gpr_log(GPR_ERROR, "Connect failed: %s",
+ status.status().ToString().c_str());
+ client_endpoint_promise.Set(nullptr);
+ } else {
+ client_endpoint_promise.Set(std::move(*status));
+ }
+ },
+ URIToResolvedAddress(target_addr), ChannelArgsEndpointConfig(nullptr),
+ memory_quota->CreateMemoryAllocator("conn-1"), 24h);
+
+ auto client_endpoint = std::move(client_endpoint_promise.Get());
+ auto server_endpoint = std::move(server_endpoint_promise.Get());
+ EXPECT_TRUE(client_endpoint != nullptr);
+ EXPECT_TRUE(server_endpoint != nullptr);
+
+ // Alternate message exchanges between client -- server and server -- client.
+ for (int i = 0; i < kNumExchangedMessages; i++) {
+ // Send from client to server and verify data read at the server.
+ EXPECT_TRUE(SendValidatePayload(GetNextSendMessage(), client_endpoint.get(),
+ server_endpoint.get())
+ .ok());
+
+ // Send from server to client and verify data read at the client.
+ EXPECT_TRUE(SendValidatePayload(GetNextSendMessage(), server_endpoint.get(),
+ client_endpoint.get())
+ .ok());
+ }
+}
+
+// Create 1 listener bound to N IPv6 addresses and M connections where M > N and
+// exchange and verify random number of messages over each connection.
+TEST_F(EventEngineClientTest, MultipleIPv6ConnectionsToOneOracleListenerTest) {
+ grpc_core::ExecCtx ctx;
+ static constexpr int kStartPortNumber = 7000;
+ static constexpr int kNumListenerAddresses = 10; // N
+ static constexpr int kNumConnections = 100; // M
+ auto oracle_ee = this->NewOracleEventEngine();
+ auto test_ee = this->NewEventEngine();
+ auto memory_quota = std::make_unique("bar");
+ Promise> client_endpoint_promise;
+ Promise> server_endpoint_promise;
+ std::vector target_addrs;
+ std::vector, std::unique_ptr>>
+ connections;
+
+ Listener::AcceptCallback accept_cb =
+ [&server_endpoint_promise](
+ std::unique_ptr ep,
+ grpc_core::MemoryAllocator /*memory_allocator*/) {
+ server_endpoint_promise.Set(std::move(ep));
+ };
+ auto status = oracle_ee->CreateListener(
+ std::move(accept_cb),
+ [](absl::Status status) { GPR_ASSERT(status.ok()); },
+ ChannelArgsEndpointConfig(nullptr),
+ std::make_unique("foo"));
+ EXPECT_TRUE(status.ok());
+ std::unique_ptr listener = std::move(*status);
+
+ target_addrs.reserve(kNumListenerAddresses);
+ for (int i = 0; i < kNumListenerAddresses; i++) {
+ std::string target_addr =
+ absl::StrCat("ipv6:[::1]:", std::to_string(kStartPortNumber + i));
+ EXPECT_TRUE(listener->Bind(URIToResolvedAddress(target_addr)).ok());
+ target_addrs.push_back(target_addr);
+ }
+ EXPECT_TRUE(listener->Start().ok());
+ absl::SleepFor(absl::Milliseconds(500));
+ for (int i = 0; i < kNumConnections; i++) {
+ // Create a test EventEngine client endpoint and connect to a one of the
+ // addresses bound to the oracle listener. Verify that the connection
+ // succeeds.
+ test_ee->Connect(
+ [&client_endpoint_promise](
+ absl::StatusOr> status) {
+ if (!status.ok()) {
+ gpr_log(GPR_ERROR, "Connect failed: %s",
+ status.status().ToString().c_str());
+ client_endpoint_promise.Set(nullptr);
+ } else {
+ client_endpoint_promise.Set(std::move(*status));
+ }
+ },
+ URIToResolvedAddress(target_addrs[i % kNumListenerAddresses]),
+ ChannelArgsEndpointConfig(nullptr),
+ memory_quota->CreateMemoryAllocator(
+ absl::StrCat("conn-", std::to_string(i))),
+ 24h);
+
+ auto client_endpoint = std::move(client_endpoint_promise.Get());
+ auto server_endpoint = std::move(server_endpoint_promise.Get());
+ EXPECT_TRUE(client_endpoint != nullptr);
+ EXPECT_TRUE(server_endpoint != nullptr);
+ connections.push_back(std::make_tuple(std::move(client_endpoint),
+ std::move(server_endpoint)));
+ client_endpoint_promise.Reset();
+ server_endpoint_promise.Reset();
+ }
+
+ std::vector threads;
+ // Create one thread for each connection. For each connection, create
+ // 2 more worker threads: to exchange and verify bi-directional data transfer.
+ threads.reserve(kNumConnections);
+ for (int i = 0; i < kNumConnections; i++) {
+ // For each connection, simulate a parallel bi-directional data transfer.
+ // All bi-directional transfers are run in parallel across all connections.
+ // Each bi-directional data transfer uses a random number of messages.
+ threads.emplace_back([client_endpoint =
+ std::move(std::get<0>(connections[i])),
+ server_endpoint =
+ std::move(std::get<1>(connections[i]))]() {
+ std::vector workers;
+ workers.reserve(2);
+ auto worker = [client_endpoint = client_endpoint.get(),
+ server_endpoint =
+ server_endpoint.get()](bool client_to_server) {
+ grpc_core::ExecCtx ctx;
+ for (int i = 0; i < kNumExchangedMessages; i++) {
+ // If client_to_server is true, send from client to server and
+ // verify data read at the server. Otherwise send data from server
+ // to client and verify data read at client.
+ if (client_to_server) {
+ EXPECT_TRUE(SendValidatePayload(GetNextSendMessage(),
+ client_endpoint, server_endpoint)
+ .ok());
+ } else {
+ EXPECT_TRUE(SendValidatePayload(GetNextSendMessage(),
+ server_endpoint, client_endpoint)
+ .ok());
+ }
+ }
+ };
+ // worker[0] simulates a flow from client to server endpoint
+ workers.emplace_back([&worker]() { worker(true); });
+ // worker[1] simulates a flow from server to client endpoint
+ workers.emplace_back([&worker]() { worker(false); });
+ workers[0].join();
+ workers[1].join();
+ });
+ }
+ for (auto& t : threads) {
+ t.join();
+ }
+}
+
+// TODO(vigneshbabu): Add more tests which create listeners bound to a mix
+// Ipv6 and other type of addresses (UDS) in the same test.
diff --git a/test/core/event_engine/test_suite/event_engine_test.cc b/test/core/event_engine/test_suite/event_engine_test.cc
index 5ba038f2683..a3e115d08ef 100644
--- a/test/core/event_engine/test_suite/event_engine_test.cc
+++ b/test/core/event_engine/test_suite/event_engine_test.cc
@@ -20,9 +20,16 @@
std::function()>*
g_ee_factory = nullptr;
-void SetEventEngineFactory(
+std::function()>*
+ g_oracle_ee_factory = nullptr;
+
+void SetEventEngineFactories(
+ std::function<
+ std::unique_ptr()>
+ factory,
std::function<
std::unique_ptr()>
- factory) {
- testing::AddGlobalTestEnvironment(new EventEngineTestEnvironment(factory));
+ oracle_ee_factory) {
+ testing::AddGlobalTestEnvironment(
+ new EventEngineTestEnvironment(factory, oracle_ee_factory));
}
diff --git a/test/core/event_engine/test_suite/event_engine_test.h b/test/core/event_engine/test_suite/event_engine_test.h
index 743217bb827..27197486704 100644
--- a/test/core/event_engine/test_suite/event_engine_test.h
+++ b/test/core/event_engine/test_suite/event_engine_test.h
@@ -24,22 +24,37 @@ extern std::function<
std::unique_ptr()>*
g_ee_factory;
+extern std::function<
+ std::unique_ptr()>*
+ g_oracle_ee_factory;
+
// Manages the lifetime of the global EventEngine factory.
class EventEngineTestEnvironment : public testing::Environment {
public:
- explicit EventEngineTestEnvironment(
+ EventEngineTestEnvironment(
std::function<
std::unique_ptr()>
- factory)
- : factory_(factory) {}
+ factory,
+ std::function<
+ std::unique_ptr()>
+ oracle_factory)
+ : factory_(factory), oracle_factory_(oracle_factory) {}
- void SetUp() override { g_ee_factory = &factory_; }
+ void SetUp() override {
+ g_ee_factory = &factory_;
+ g_oracle_ee_factory = &oracle_factory_;
+ }
- void TearDown() override { g_ee_factory = nullptr; }
+ void TearDown() override {
+ g_ee_factory = nullptr;
+ g_oracle_ee_factory = nullptr;
+ }
private:
std::function()>
factory_;
+ std::function()>
+ oracle_factory_;
};
class EventEngineTest : public testing::Test {
@@ -49,12 +64,22 @@ class EventEngineTest : public testing::Test {
GPR_ASSERT(g_ee_factory != nullptr);
return (*g_ee_factory)();
}
+
+ std::unique_ptr
+ NewOracleEventEngine() {
+ GPR_ASSERT(g_oracle_ee_factory != nullptr);
+ return (*g_oracle_ee_factory)();
+ }
};
-// Set a custom factory for the EventEngine test suite.
-void SetEventEngineFactory(
+// Set a custom factory for the EventEngine test suite. An optional oracle
+// EventEngine can additionally be specified here.
+void SetEventEngineFactories(
+ std::function<
+ std::unique_ptr()>
+ ee_factory,
std::function<
std::unique_ptr()>
- factory);
+ oracle_ee_factory);
#endif // GRPC_TEST_CORE_EVENT_ENGINE_TEST_SUITE_EVENT_ENGINE_TEST_H
diff --git a/test/core/event_engine/test_suite/event_engine_test_utils.cc b/test/core/event_engine/test_suite/event_engine_test_utils.cc
new file mode 100644
index 00000000000..7831495f97e
--- /dev/null
+++ b/test/core/event_engine/test_suite/event_engine_test_utils.cc
@@ -0,0 +1,206 @@
+// Copyright 2022 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/event_engine/test_suite/event_engine_test_utils.h"
+
+#include
+#include
+#include
+#include
+
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/str_cat.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/time/time.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "src/core/lib/address_utils/parse_address.h"
+#include "src/core/lib/event_engine/channel_args_endpoint_config.h"
+#include "src/core/lib/resource_quota/memory_quota.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+using ::grpc_event_engine::experimental::EventEngine;
+using Endpoint = ::grpc_event_engine::experimental::EventEngine::Endpoint;
+using Listener = ::grpc_event_engine::experimental::EventEngine::Listener;
+
+namespace grpc_event_engine {
+namespace experimental {
+
+EventEngine::ResolvedAddress URIToResolvedAddress(std::string address_str) {
+ grpc_resolved_address addr;
+ absl::StatusOr uri = grpc_core::URI::Parse(address_str);
+ if (!uri.ok()) {
+ gpr_log(GPR_ERROR, "Failed to parse. Error: %s",
+ uri.status().ToString().c_str());
+ GPR_ASSERT(uri.ok());
+ }
+ GPR_ASSERT(grpc_parse_uri(*uri, &addr));
+ return EventEngine::ResolvedAddress(
+ reinterpret_cast(addr.addr), addr.len);
+}
+
+void AppendStringToSliceBuffer(SliceBuffer* buf, std::string data) {
+ buf->Append(Slice::FromCopiedString(data));
+}
+
+std::string ExtractSliceBufferIntoString(SliceBuffer* buf) {
+ if (!buf->Length()) {
+ return std::string();
+ }
+ std::string tmp(buf->Length(), '\0');
+ char* bytes = const_cast(tmp.c_str());
+ grpc_slice_buffer_move_first_into_buffer(buf->RawSliceBuffer(), buf->Length(),
+ bytes);
+ return tmp;
+}
+
+absl::Status SendValidatePayload(std::string data, Endpoint* send_endpoint,
+ Endpoint* receive_endpoint) {
+ GPR_ASSERT(receive_endpoint != nullptr && send_endpoint != nullptr);
+ int num_bytes_written = data.size();
+ Promise read_promise;
+ Promise write_promise;
+ SliceBuffer read_slice_buf;
+ SliceBuffer write_slice_buf;
+
+ AppendStringToSliceBuffer(&write_slice_buf, data);
+ EventEngine::Endpoint::ReadArgs args = {num_bytes_written};
+ std::function read_cb;
+ read_cb = [receive_endpoint, &read_slice_buf, &read_cb, &read_promise,
+ &args](absl::Status status) {
+ GPR_ASSERT(status.ok());
+ if (read_slice_buf.Length() == static_cast(args.read_hint_bytes)) {
+ read_promise.Set(true);
+ return;
+ }
+ args.read_hint_bytes -= read_slice_buf.Length();
+ receive_endpoint->Read(std::move(read_cb), &read_slice_buf, &args);
+ };
+ // Start asynchronous reading at the receive_endpoint.
+ receive_endpoint->Read(std::move(read_cb), &read_slice_buf, &args);
+ // Start asynchronous writing at the send_endpoint.
+ send_endpoint->Write(
+ [&write_promise](absl::Status status) {
+ GPR_ASSERT(status.ok());
+ write_promise.Set(true);
+ },
+ &write_slice_buf, nullptr);
+ // Wait for async write to complete.
+ GPR_ASSERT(write_promise.Get() == true);
+ // Wait for async read to complete.
+ GPR_ASSERT(read_promise.Get() == true);
+ // Check if data written == data read
+ if (data != ExtractSliceBufferIntoString(&read_slice_buf)) {
+ return absl::CancelledError("Data read != Data written");
+ }
+ return absl::OkStatus();
+}
+
+absl::Status ConnectionManager::BindAndStartListener(
+ std::vector addrs, bool listener_type_oracle) {
+ grpc_core::MutexLock lock(&mu_);
+ if (addrs.empty()) {
+ return absl::InvalidArgumentError(
+ "Atleast one bind address must be specified");
+ }
+ for (auto& addr : addrs) {
+ if (listeners_.find(addr) != listeners_.end()) {
+ // There is already a listener at this address. Return error.
+ return absl::AlreadyExistsError(
+ absl::StrCat("Listener already existis for address: ", addr));
+ }
+ }
+ Listener::AcceptCallback accept_cb =
+ [this](std::unique_ptr ep,
+ MemoryAllocator /*memory_allocator*/) {
+ last_in_progress_connection_.SetServerEndpoint(std::move(ep));
+ };
+
+ EventEngine* event_engine = listener_type_oracle ? oracle_event_engine_.get()
+ : test_event_engine_.get();
+
+ auto status = event_engine->CreateListener(
+ std::move(accept_cb),
+ [](absl::Status status) { GPR_ASSERT(status.ok()); },
+ ChannelArgsEndpointConfig(nullptr),
+ std::make_unique("foo"));
+ if (!status.ok()) {
+ return status.status();
+ }
+
+ std::shared_ptr listener((*status).release());
+ for (auto& addr : addrs) {
+ auto bind_status = listener->Bind(URIToResolvedAddress(addr));
+ if (!bind_status.ok()) {
+ gpr_log(GPR_ERROR, "Binding listener failed: %s",
+ bind_status.status().ToString().c_str());
+ return bind_status.status();
+ }
+ }
+ GPR_ASSERT(listener->Start().ok());
+ // Insert same listener pointer for all bind addresses after the listener
+ // has started successfully.
+ for (auto& addr : addrs) {
+ listeners_.insert(std::make_pair(addr, listener));
+ }
+ return absl::OkStatus();
+}
+
+absl::StatusOr, std::unique_ptr>>
+ConnectionManager::CreateConnection(std::string target_addr,
+ EventEngine::Duration timeout,
+ bool client_type_oracle) {
+ // Only allow one CreateConnection call to proceed at a time.
+ grpc_core::MutexLock lock(&mu_);
+ std::string conn_name =
+ absl::StrCat("connection-", std::to_string(num_processed_connections_++));
+ EventEngine* event_engine = client_type_oracle ? oracle_event_engine_.get()
+ : test_event_engine_.get();
+ event_engine->Connect(
+ [this](absl::StatusOr> status) {
+ if (!status.ok()) {
+ gpr_log(GPR_ERROR, "Connect failed: %s",
+ status.status().ToString().c_str());
+ last_in_progress_connection_.SetClientEndpoint(nullptr);
+ } else {
+ last_in_progress_connection_.SetClientEndpoint(std::move(*status));
+ }
+ },
+ URIToResolvedAddress(target_addr), ChannelArgsEndpointConfig(nullptr),
+ memory_quota_->CreateMemoryAllocator(conn_name), timeout);
+
+ auto client_endpoint = last_in_progress_connection_.GetClientEndpoint();
+ if (client_endpoint != nullptr &&
+ listeners_.find(target_addr) != listeners_.end()) {
+ // There is a listener for the specified address. Wait until it
+ // creates a ServerEndpoint after accepting the connection.
+ auto server_endpoint = last_in_progress_connection_.GetServerEndpoint();
+ GPR_ASSERT(server_endpoint != nullptr);
+ // Set last_in_progress_connection_ to nullptr
+ return std::make_tuple(std::move(client_endpoint),
+ std::move(server_endpoint));
+ }
+ return absl::CancelledError("Failed to create connection.");
+}
+
+} // namespace experimental
+} // namespace grpc_event_engine
diff --git a/test/core/event_engine/test_suite/event_engine_test_utils.h b/test/core/event_engine/test_suite/event_engine_test_utils.h
new file mode 100644
index 00000000000..00868e7c1d3
--- /dev/null
+++ b/test/core/event_engine/test_suite/event_engine_test_utils.h
@@ -0,0 +1,126 @@
+// Copyright 2022 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_EVENT_ENGINE_TEST_SUITE_EVENT_ENGINE_TEST_UTILS_H_
+#define GRPC_TEST_CORE_EVENT_ENGINE_TEST_SUITE_EVENT_ENGINE_TEST_UTILS_H_
+
+#include
+#include