diff options
Diffstat (limited to 'test/core/iomgr')
26 files changed, 1990 insertions, 838 deletions
diff --git a/test/core/iomgr/BUILD b/test/core/iomgr/BUILD index 349a06d578..70ee83acd2 100644 --- a/test/core/iomgr/BUILD +++ b/test/core/iomgr/BUILD @@ -18,7 +18,10 @@ licenses(["notice"]) # Apache v2 load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer") -grpc_package(name = "test/core/iomgr", visibility = "public") # Useful for third party devs to test their io manager implementation. +grpc_package( + name = "test/core/iomgr", + visibility = "public", +) # Useful for third party devs to test their io manager implementation. grpc_cc_library( name = "endpoint_tests", @@ -37,6 +40,7 @@ grpc_cc_library( grpc_cc_test( name = "combiner_test", srcs = ["combiner_test.cc"], + data = ["//third_party/toolchains:RBE_USE_MACHINE_TYPE_LARGE"], language = "C++", deps = [ "//:gpr", @@ -73,15 +77,15 @@ grpc_cc_test( ) grpc_cc_test( - name = "ev_epollsig_linux_test", - srcs = ["ev_epollsig_linux_test.cc"], + name = "ev_epollex_linux_test", + srcs = ["ev_epollex_linux_test.cc"], + language = "C++", deps = [ "//:gpr", "//:grpc", "//test/core/util:gpr_test_util", "//test/core/util:grpc_test_util", ], - language = "C++", ) grpc_cc_test( @@ -109,8 +113,8 @@ grpc_cc_test( ) grpc_cc_test( - name = "load_file_test", - srcs = ["load_file_test.cc"], + name = "grpc_ipv6_loopback_available_test", + srcs = ["grpc_ipv6_loopback_available_test.cc"], language = "C++", deps = [ "//:gpr", @@ -121,8 +125,8 @@ grpc_cc_test( ) grpc_cc_test( - name = "pollset_set_test", - srcs = ["pollset_set_test.cc"], + name = "load_file_test", + srcs = ["load_file_test.cc"], language = "C++", deps = [ "//:gpr", @@ -145,8 +149,26 @@ grpc_cc_test( ) grpc_cc_test( - name = "resolve_address_test", + name = "resolve_address_using_ares_resolver_test", srcs = ["resolve_address_test.cc"], + args = [ + "--resolver=ares", + ], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + ], +) + +grpc_cc_test( + name = "resolve_address_using_native_resolver_test", + srcs = ["resolve_address_test.cc"], + args = [ + "--resolver=native", + ], language = "C++", deps = [ "//:gpr", @@ -218,9 +240,22 @@ grpc_cc_test( ) grpc_cc_test( + name = "buffer_list_test", + srcs = ["buffer_list_test.cc"], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + ], +) + +grpc_cc_test( name = "tcp_server_posix_test", srcs = ["tcp_server_posix_test.cc"], language = "C++", + tags = ["manual"], # TODO(adelez): Remove once this works on Foundry. deps = [ "//:gpr", "//:grpc", diff --git a/test/core/iomgr/buffer_list_test.cc b/test/core/iomgr/buffer_list_test.cc new file mode 100644 index 0000000000..c7f30fa092 --- /dev/null +++ b/test/core/iomgr/buffer_list_test.cc @@ -0,0 +1,111 @@ +/* + * + * Copyright 2018 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/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/buffer_list.h" + +#include <grpc/grpc.h> + +#include "test/core/util/test_config.h" + +#ifdef GRPC_LINUX_ERRQUEUE + +static void TestShutdownFlushesListVerifier(void* arg, + grpc_core::Timestamps* ts, + grpc_error* error) { + GPR_ASSERT(error == GRPC_ERROR_NONE); + GPR_ASSERT(arg != nullptr); + gpr_atm* done = reinterpret_cast<gpr_atm*>(arg); + gpr_atm_rel_store(done, static_cast<gpr_atm>(1)); +} + +/** Tests that all TracedBuffer elements in the list are flushed out on + * shutdown. + * Also tests that arg is passed correctly. + */ +static void TestShutdownFlushesList() { + grpc_core::grpc_tcp_set_write_timestamps_callback( + TestShutdownFlushesListVerifier); + grpc_core::TracedBuffer* list = nullptr; +#define NUM_ELEM 5 + gpr_atm verifier_called[NUM_ELEM]; + for (auto i = 0; i < NUM_ELEM; i++) { + gpr_atm_rel_store(&verifier_called[i], static_cast<gpr_atm>(0)); + grpc_core::TracedBuffer::AddNewEntry( + &list, i, static_cast<void*>(&verifier_called[i])); + } + grpc_core::TracedBuffer::Shutdown(&list, GRPC_ERROR_NONE); + GPR_ASSERT(list == nullptr); + for (auto i = 0; i < NUM_ELEM; i++) { + GPR_ASSERT(gpr_atm_acq_load(&verifier_called[i]) == + static_cast<gpr_atm>(1)); + } +} + +static void TestVerifierCalledOnAckVerifier(void* arg, + grpc_core::Timestamps* ts, + grpc_error* error) { + GPR_ASSERT(error == GRPC_ERROR_NONE); + GPR_ASSERT(arg != nullptr); + GPR_ASSERT(ts->acked_time.clock_type == GPR_CLOCK_REALTIME); + GPR_ASSERT(ts->acked_time.tv_sec == 123); + GPR_ASSERT(ts->acked_time.tv_nsec == 456); + gpr_atm* done = reinterpret_cast<gpr_atm*>(arg); + gpr_atm_rel_store(done, static_cast<gpr_atm>(1)); +} + +/** Tests that the timestamp verifier is called on an ACK timestamp. + */ +static void TestVerifierCalledOnAck() { + struct sock_extended_err serr; + serr.ee_data = 213; + serr.ee_info = grpc_core::SCM_TSTAMP_ACK; + struct grpc_core::scm_timestamping tss; + tss.ts[0].tv_sec = 123; + tss.ts[0].tv_nsec = 456; + grpc_core::grpc_tcp_set_write_timestamps_callback( + TestVerifierCalledOnAckVerifier); + grpc_core::TracedBuffer* list = nullptr; + gpr_atm verifier_called; + gpr_atm_rel_store(&verifier_called, static_cast<gpr_atm>(0)); + grpc_core::TracedBuffer::AddNewEntry(&list, 213, &verifier_called); + grpc_core::TracedBuffer::ProcessTimestamp(&list, &serr, &tss); + GPR_ASSERT(gpr_atm_acq_load(&verifier_called) == static_cast<gpr_atm>(1)); + GPR_ASSERT(list == nullptr); + grpc_core::TracedBuffer::Shutdown(&list, GRPC_ERROR_NONE); +} + +static void TestTcpBufferList() { + TestVerifierCalledOnAck(); + TestShutdownFlushesList(); +} + +int main(int argc, char** argv) { + grpc_test_init(argc, argv); + grpc_init(); + TestTcpBufferList(); + grpc_shutdown(); + return 0; +} + +#else /* GRPC_LINUX_ERRQUEUE */ + +int main(int argc, char** argv) { return 0; } + +#endif /* GRPC_LINUX_ERRQUEUE */ diff --git a/test/core/iomgr/endpoint_tests.cc b/test/core/iomgr/endpoint_tests.cc index 8db8ac5ed6..a9e8ba86c5 100644 --- a/test/core/iomgr/endpoint_tests.cc +++ b/test/core/iomgr/endpoint_tests.cc @@ -150,8 +150,8 @@ static void read_and_write_test_write_handler(void* data, grpc_error* error) { &state->current_write_data); grpc_slice_buffer_reset_and_unref(&state->outgoing); grpc_slice_buffer_addn(&state->outgoing, slices, nslices); - grpc_endpoint_write(state->write_ep, &state->outgoing, - &state->done_write); + grpc_endpoint_write(state->write_ep, &state->outgoing, &state->done_write, + nullptr); gpr_free(slices); return; } @@ -294,7 +294,8 @@ static void multiple_shutdown_test(grpc_endpoint_test_config config) { grpc_slice_buffer_add(&slice_buffer, grpc_slice_from_copied_string("a")); grpc_endpoint_write(f.client_ep, &slice_buffer, GRPC_CLOSURE_CREATE(inc_on_failure, &fail_count, - grpc_schedule_on_exec_ctx)); + grpc_schedule_on_exec_ctx), + nullptr); wait_for_fail_count(&fail_count, 3); grpc_endpoint_shutdown(f.client_ep, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Test Shutdown")); diff --git a/test/core/iomgr/error_test.cc b/test/core/iomgr/error_test.cc index a1628a1f71..d78a8c2af3 100644 --- a/test/core/iomgr/error_test.cc +++ b/test/core/iomgr/error_test.cc @@ -187,16 +187,6 @@ static void test_os_error() { GRPC_ERROR_UNREF(error); } -static void test_special() { - grpc_error* error = GRPC_ERROR_NONE; - error = grpc_error_add_child( - error, GRPC_ERROR_CREATE_FROM_STATIC_STRING("test child")); - intptr_t i; - GPR_ASSERT(grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &i)); - GPR_ASSERT(i == GRPC_STATUS_OK); - GRPC_ERROR_UNREF(error); -} - static void test_overflow() { grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Overflow"); @@ -235,7 +225,6 @@ int main(int argc, char** argv) { test_os_error(); test_create_referencing(); test_create_referencing_many(); - test_special(); test_overflow(); grpc_shutdown(); diff --git a/test/core/iomgr/ev_epollex_linux_test.cc b/test/core/iomgr/ev_epollex_linux_test.cc new file mode 100644 index 0000000000..08d1e68b39 --- /dev/null +++ b/test/core/iomgr/ev_epollex_linux_test.cc @@ -0,0 +1,115 @@ +/* + * + * Copyright 2018 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/lib/iomgr/port.h" + +/* This test only relevant on linux systems where epoll() is available */ +#if defined(GRPC_LINUX_EPOLL_CREATE1) && defined(GRPC_LINUX_EVENTFD) +#include "src/core/lib/iomgr/ev_epollex_linux.h" + +#include <grpc/grpc.h> +#include <string.h> +#include <sys/eventfd.h> + +#include "test/core/util/test_config.h" + +static void pollset_destroy(void* ps, grpc_error* error) { + grpc_pollset_destroy(static_cast<grpc_pollset*>(ps)); + gpr_free(ps); +} + +// This test is added to cover the case found in bug: +// https://github.com/grpc/grpc/issues/15760 +static void test_pollable_owner_fd() { + grpc_core::ExecCtx exec_ctx; + int ev_fd1; + int ev_fd2; + grpc_fd* grpc_fd1; + grpc_fd* grpc_fd2; + grpc_pollset* ps; + gpr_mu* mu; + + // == Create two grpc_fds == + // All we need is two file descriptors. Doesn't matter what type. We use + // eventfd type here for the purpose of this test + ev_fd1 = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + ev_fd2 = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (ev_fd1 < 0 || ev_fd2 < 0) { + gpr_log(GPR_ERROR, "Error in creating event fds for the test"); + return; + } + grpc_fd1 = grpc_fd_create(ev_fd1, "epollex-test-fd1", false); + grpc_fd2 = grpc_fd_create(ev_fd2, "epollex-test-fd2", false); + grpc_core::ExecCtx::Get()->Flush(); + + // == Create a pollset == + ps = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size())); + grpc_pollset_init(ps, &mu); + grpc_core::ExecCtx::Get()->Flush(); + + // == Add fd1 to pollset == + grpc_pollset_add_fd(ps, grpc_fd1); + grpc_core::ExecCtx::Get()->Flush(); + + // == Destroy fd1 == + grpc_fd_orphan(grpc_fd1, nullptr, nullptr, "test fd1 orphan"); + grpc_core::ExecCtx::Get()->Flush(); + + // = Add fd2 to pollset == + // + // Before https://github.com/grpc/grpc/issues/15760, the following line caused + // unexpected behavior (The previous grpc_pollset_add_fd(ps, grpc_fd1) created + // an underlying structure in epollex that held a reference to grpc_fd1 which + // was being accessed here even after grpc_fd_orphan(grpc_fd1) was called + grpc_pollset_add_fd(ps, grpc_fd2); + grpc_core::ExecCtx::Get()->Flush(); + + // == Destroy fd2 == + grpc_fd_orphan(grpc_fd2, nullptr, nullptr, "test fd2 orphan"); + grpc_core::ExecCtx::Get()->Flush(); + + // == Destroy pollset + grpc_closure ps_destroy_closure; + GRPC_CLOSURE_INIT(&ps_destroy_closure, pollset_destroy, ps, + grpc_schedule_on_exec_ctx); + grpc_pollset_shutdown(ps, &ps_destroy_closure); + grpc_core::ExecCtx::Get()->Flush(); +} + +int main(int argc, char** argv) { + const char* poll_strategy = nullptr; + grpc_test_init(argc, argv); + grpc_init(); + { + grpc_core::ExecCtx exec_ctx; + poll_strategy = grpc_get_poll_strategy_name(); + if (poll_strategy != nullptr && strcmp(poll_strategy, "epollex") == 0) { + test_pollable_owner_fd(); + } else { + gpr_log(GPR_INFO, + "Skipping the test. The test is only relevant for 'epollex' " + "strategy. and the current strategy is: '%s'", + poll_strategy); + } + } + + grpc_shutdown(); + return 0; +} +#else /* defined(GRPC_LINUX_EPOLL_CREATE1) && defined(GRPC_LINUX_EVENTFD) */ +int main(int argc, char** argv) { return 0; } +#endif diff --git a/test/core/iomgr/ev_epollsig_linux_test.cc b/test/core/iomgr/ev_epollsig_linux_test.cc deleted file mode 100644 index c3ba6d7c14..0000000000 --- a/test/core/iomgr/ev_epollsig_linux_test.cc +++ /dev/null @@ -1,323 +0,0 @@ -/* - * - * Copyright 2015 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/lib/iomgr/port.h" - -/* This test only relevant on linux systems where epoll() is available */ -#ifdef GRPC_LINUX_EPOLL_CREATE1 -#include "src/core/lib/iomgr/ev_epollsig_linux.h" -#include "src/core/lib/iomgr/ev_posix.h" - -#include <errno.h> -#include <string.h> -#include <unistd.h> - -#include <grpc/grpc.h> -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/gprpp/thd.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "test/core/util/test_config.h" - -typedef struct test_pollset { - grpc_pollset* pollset; - gpr_mu* mu; -} test_pollset; - -typedef struct test_fd { - int inner_fd; - grpc_fd* fd; -} test_fd; - -/* num_fds should be an even number */ -static void test_fd_init(test_fd* tfds, int* fds, int num_fds) { - int i; - int r; - - /* Create some dummy file descriptors. Currently using pipe file descriptors - * for this test but we could use any other type of file descriptors. Also, - * since pipe() used in this test creates two fds in each call, num_fds should - * be an even number */ - GPR_ASSERT((num_fds % 2) == 0); - for (i = 0; i < num_fds; i = i + 2) { - r = pipe(fds + i); - if (r != 0) { - gpr_log(GPR_ERROR, "Error in creating pipe. %d (%s)", errno, - strerror(errno)); - return; - } - } - - for (i = 0; i < num_fds; i++) { - tfds[i].inner_fd = fds[i]; - tfds[i].fd = grpc_fd_create(fds[i], "test_fd"); - } -} - -static void test_fd_cleanup(test_fd* tfds, int num_fds) { - int release_fd; - int i; - - for (i = 0; i < num_fds; i++) { - grpc_fd_shutdown(tfds[i].fd, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("test_fd_cleanup")); - grpc_core::ExecCtx::Get()->Flush(); - - grpc_fd_orphan(tfds[i].fd, nullptr, &release_fd, false /* already_closed */, - "test_fd_cleanup"); - grpc_core::ExecCtx::Get()->Flush(); - - GPR_ASSERT(release_fd == tfds[i].inner_fd); - close(tfds[i].inner_fd); - } -} - -static void test_pollset_init(test_pollset* pollsets, int num_pollsets) { - int i; - for (i = 0; i < num_pollsets; i++) { - pollsets[i].pollset = - static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size())); - grpc_pollset_init(pollsets[i].pollset, &pollsets[i].mu); - } -} - -static void destroy_pollset(void* p, grpc_error* error) { - grpc_pollset_destroy(static_cast<grpc_pollset*>(p)); -} - -static void test_pollset_cleanup(test_pollset* pollsets, int num_pollsets) { - grpc_closure destroyed; - int i; - - for (i = 0; i < num_pollsets; i++) { - GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, pollsets[i].pollset, - grpc_schedule_on_exec_ctx); - grpc_pollset_shutdown(pollsets[i].pollset, &destroyed); - - grpc_core::ExecCtx::Get()->Flush(); - gpr_free(pollsets[i].pollset); - } -} - - /* - * Cases to test: - * case 1) Polling islands of both fd and pollset are NULL - * case 2) Polling island of fd is NULL but that of pollset is not-NULL - * case 3) Polling island of fd is not-NULL but that of pollset is NULL - * case 4) Polling islands of both fd and pollset are not-NULL and: - * case 4.1) Polling islands of fd and pollset are equal - * case 4.2) Polling islands of fd and pollset are NOT-equal (This results - * in a merge) - * */ - -#define NUM_FDS 8 -#define NUM_POLLSETS 4 - -static void test_add_fd_to_pollset() { - grpc_core::ExecCtx exec_ctx; - test_fd tfds[NUM_FDS]; - int fds[NUM_FDS]; - test_pollset pollsets[NUM_POLLSETS]; - void* expected_pi = nullptr; - int i; - - test_fd_init(tfds, fds, NUM_FDS); - test_pollset_init(pollsets, NUM_POLLSETS); - - /*Step 1. - * Create three polling islands (This will exercise test case 1 and 2) with - * the following configuration: - * polling island 0 = { fds:0,1,2, pollsets:0} - * polling island 1 = { fds:3,4, pollsets:1} - * polling island 2 = { fds:5,6,7 pollsets:2} - * - *Step 2. - * Add pollset 3 to polling island 0 (by adding fds 0 and 1 to pollset 3) - * (This will exercise test cases 3 and 4.1). The configuration becomes: - * polling island 0 = { fds:0,1,2, pollsets:0,3} <<< pollset 3 added here - * polling island 1 = { fds:3,4, pollsets:1} - * polling island 2 = { fds:5,6,7 pollsets:2} - * - *Step 3. - * Merge polling islands 0 and 1 by adding fd 0 to pollset 1 (This will - * exercise test case 4.2). The configuration becomes: - * polling island (merged) = {fds: 0,1,2,3,4, pollsets: 0,1,3} - * polling island 2 = {fds: 5,6,7 pollsets: 2} - * - *Step 4. - * Finally do one more merge by adding fd 3 to pollset 2. - * polling island (merged) = {fds: 0,1,2,3,4,5,6,7, pollsets: 0,1,2,3} - */ - - /* == Step 1 == */ - for (i = 0; i <= 2; i++) { - grpc_pollset_add_fd(pollsets[0].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - for (i = 3; i <= 4; i++) { - grpc_pollset_add_fd(pollsets[1].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - for (i = 5; i <= 7; i++) { - grpc_pollset_add_fd(pollsets[2].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - /* == Step 2 == */ - for (i = 0; i <= 1; i++) { - grpc_pollset_add_fd(pollsets[3].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - /* == Step 3 == */ - grpc_pollset_add_fd(pollsets[1].pollset, tfds[0].fd); - grpc_core::ExecCtx::Get()->Flush(); - - /* == Step 4 == */ - grpc_pollset_add_fd(pollsets[2].pollset, tfds[3].fd); - grpc_core::ExecCtx::Get()->Flush(); - - /* All polling islands are merged at this point */ - - /* Compare Fd:0's polling island with that of all other Fds */ - expected_pi = grpc_fd_get_polling_island(tfds[0].fd); - for (i = 1; i < NUM_FDS; i++) { - GPR_ASSERT(grpc_are_polling_islands_equal( - expected_pi, grpc_fd_get_polling_island(tfds[i].fd))); - } - - /* Compare Fd:0's polling island with that of all other pollsets */ - for (i = 0; i < NUM_POLLSETS; i++) { - GPR_ASSERT(grpc_are_polling_islands_equal( - expected_pi, grpc_pollset_get_polling_island(pollsets[i].pollset))); - } - - test_fd_cleanup(tfds, NUM_FDS); - test_pollset_cleanup(pollsets, NUM_POLLSETS); -} - -#undef NUM_FDS -#undef NUM_POLLSETS - -typedef struct threading_shared { - gpr_mu* mu; - grpc_pollset* pollset; - grpc_wakeup_fd* wakeup_fd; - grpc_fd* wakeup_desc; - grpc_closure on_wakeup; - int wakeups; -} threading_shared; - -static __thread int thread_wakeups = 0; - -static void test_threading_loop(void* arg) { - threading_shared* shared = static_cast<threading_shared*>(arg); - while (thread_wakeups < 1000000) { - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - gpr_mu_lock(shared->mu); - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "pollset_work", - grpc_pollset_work(shared->pollset, &worker, GRPC_MILLIS_INF_FUTURE))); - gpr_mu_unlock(shared->mu); - } -} - -static void test_threading_wakeup(void* arg, grpc_error* error) { - threading_shared* shared = static_cast<threading_shared*>(arg); - ++shared->wakeups; - ++thread_wakeups; - if (error == GRPC_ERROR_NONE) { - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "consume_wakeup", grpc_wakeup_fd_consume_wakeup(shared->wakeup_fd))); - grpc_fd_notify_on_read(shared->wakeup_desc, &shared->on_wakeup); - GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_next", - grpc_wakeup_fd_wakeup(shared->wakeup_fd))); - } -} - -static void test_threading(void) { - threading_shared shared; - shared.pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size())); - grpc_pollset_init(shared.pollset, &shared.mu); - - grpc_core::Thread thds[10]; - for (auto& th : thds) { - th = grpc_core::Thread("test_thread", test_threading_loop, &shared); - th.Start(); - } - grpc_wakeup_fd fd; - GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_fd_init", grpc_wakeup_fd_init(&fd))); - shared.wakeup_fd = &fd; - shared.wakeup_desc = grpc_fd_create(fd.read_fd, "wakeup"); - shared.wakeups = 0; - { - grpc_core::ExecCtx exec_ctx; - grpc_pollset_add_fd(shared.pollset, shared.wakeup_desc); - grpc_fd_notify_on_read( - shared.wakeup_desc, - GRPC_CLOSURE_INIT(&shared.on_wakeup, test_threading_wakeup, &shared, - grpc_schedule_on_exec_ctx)); - } - GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_first", - grpc_wakeup_fd_wakeup(shared.wakeup_fd))); - for (auto& th : thds) { - th.Join(); - } - fd.read_fd = 0; - grpc_wakeup_fd_destroy(&fd); - { - grpc_core::ExecCtx exec_ctx; - grpc_fd_shutdown(shared.wakeup_desc, GRPC_ERROR_CANCELLED); - grpc_fd_orphan(shared.wakeup_desc, nullptr, nullptr, - false /* already_closed */, "done"); - grpc_pollset_shutdown(shared.pollset, - GRPC_CLOSURE_CREATE(destroy_pollset, shared.pollset, - grpc_schedule_on_exec_ctx)); - } - gpr_free(shared.pollset); -} - -int main(int argc, char** argv) { - const char* poll_strategy = nullptr; - grpc_test_init(argc, argv); - grpc_init(); - { - grpc_core::ExecCtx exec_ctx; - - poll_strategy = grpc_get_poll_strategy_name(); - if (poll_strategy != nullptr && strcmp(poll_strategy, "epollsig") == 0) { - test_add_fd_to_pollset(); - test_threading(); - } else { - gpr_log(GPR_INFO, - "Skipping the test. The test is only relevant for 'epollsig' " - "strategy. and the current strategy is: '%s'", - poll_strategy); - } - } - - grpc_shutdown(); - return 0; -} -#else /* defined(GRPC_LINUX_EPOLL_CREATE1) */ -int main(int argc, char** argv) { return 0; } -#endif /* !defined(GRPC_LINUX_EPOLL_CREATE1) */ diff --git a/test/core/iomgr/fd_posix_test.cc b/test/core/iomgr/fd_posix_test.cc index b81c73b2c0..4ea2389bbd 100644 --- a/test/core/iomgr/fd_posix_test.cc +++ b/test/core/iomgr/fd_posix_test.cc @@ -115,7 +115,7 @@ static void session_shutdown_cb(void* arg, /*session */ bool success) { session* se = static_cast<session*>(arg); server* sv = se->sv; - grpc_fd_orphan(se->em_fd, nullptr, nullptr, false /* already_closed */, "a"); + grpc_fd_orphan(se->em_fd, nullptr, nullptr, "a"); gpr_free(se); /* Start to shutdown listen fd. */ grpc_fd_shutdown(sv->em_fd, @@ -171,7 +171,7 @@ static void session_read_cb(void* arg, /*session */ static void listen_shutdown_cb(void* arg /*server */, int success) { server* sv = static_cast<server*>(arg); - grpc_fd_orphan(sv->em_fd, nullptr, nullptr, false /* already_closed */, "b"); + grpc_fd_orphan(sv->em_fd, nullptr, nullptr, "b"); gpr_mu_lock(g_mu); sv->done = 1; @@ -204,7 +204,7 @@ static void listen_cb(void* arg, /*=sv_arg*/ fcntl(fd, F_SETFL, flags | O_NONBLOCK); se = static_cast<session*>(gpr_malloc(sizeof(*se))); se->sv = sv; - se->em_fd = grpc_fd_create(fd, "listener"); + se->em_fd = grpc_fd_create(fd, "listener", false); grpc_pollset_add_fd(g_pollset, se->em_fd); GRPC_CLOSURE_INIT(&se->session_read_closure, session_read_cb, se, grpc_schedule_on_exec_ctx); @@ -233,7 +233,7 @@ static int server_start(server* sv) { port = ntohs(sin.sin_port); GPR_ASSERT(listen(fd, MAX_NUM_FD) == 0); - sv->em_fd = grpc_fd_create(fd, "server"); + sv->em_fd = grpc_fd_create(fd, "server", false); grpc_pollset_add_fd(g_pollset, sv->em_fd); /* Register to be interested in reading from listen_fd. */ GRPC_CLOSURE_INIT(&sv->listen_closure, listen_cb, sv, @@ -289,7 +289,7 @@ static void client_init(client* cl) { /* Called when a client upload session is ready to shutdown. */ static void client_session_shutdown_cb(void* arg /*client */, int success) { client* cl = static_cast<client*>(arg); - grpc_fd_orphan(cl->em_fd, nullptr, nullptr, false /* already_closed */, "c"); + grpc_fd_orphan(cl->em_fd, nullptr, nullptr, "c"); cl->done = 1; GPR_ASSERT( GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, nullptr))); @@ -353,7 +353,7 @@ static void client_start(client* cl, int port) { } } - cl->em_fd = grpc_fd_create(fd, "client"); + cl->em_fd = grpc_fd_create(fd, "client", false); grpc_pollset_add_fd(g_pollset, cl->em_fd); client_session_write(cl, GRPC_ERROR_NONE); @@ -454,7 +454,7 @@ static void test_grpc_fd_change(void) { flags = fcntl(sv[1], F_GETFL, 0); GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); - em_fd = grpc_fd_create(sv[0], "test_grpc_fd_change"); + em_fd = grpc_fd_create(sv[0], "test_grpc_fd_change", false); grpc_pollset_add_fd(g_pollset, em_fd); /* Register the first callback, then make its FD readable */ @@ -502,7 +502,7 @@ static void test_grpc_fd_change(void) { GPR_ASSERT(b.cb_that_ran == second_read_callback); gpr_mu_unlock(g_mu); - grpc_fd_orphan(em_fd, nullptr, nullptr, false /* already_closed */, "d"); + grpc_fd_orphan(em_fd, nullptr, nullptr, "d"); destroy_change_data(&a); destroy_change_data(&b); diff --git a/test/core/iomgr/grpc_ipv6_loopback_available_test.cc b/test/core/iomgr/grpc_ipv6_loopback_available_test.cc new file mode 100644 index 0000000000..329aa9a851 --- /dev/null +++ b/test/core/iomgr/grpc_ipv6_loopback_available_test.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015 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/lib/iomgr/port.h" + +// grpc_ipv6_loopback_available isn't currently available on UV. +#ifndef GRPC_UV + +#include <grpc/grpc.h> +#include <grpc/support/log.h> +#include "test/core/util/test_config.h" + +#ifdef GPR_WINDOWS +#include "src/core/lib/iomgr/socket_windows.h" +#else +#include "src/core/lib/iomgr/socket_utils_posix.h" +#endif + +int main(int argc, char** argv) { + grpc_test_init(argc, argv); + grpc_init(); + // This test assumes that the ipv6 loopback is available + // in all environments in which grpc tests run in. + GPR_ASSERT(grpc_ipv6_loopback_available()); + grpc_shutdown(); + return 0; +} + +#else + +int main(int argc, char** argv) { return 0; } + +#endif /* GRPC_UV */ diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamClientTests.mm b/test/core/iomgr/ios/CFStreamTests/CFStreamClientTests.mm new file mode 100644 index 0000000000..414b6c78c0 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/CFStreamClientTests.mm @@ -0,0 +1,201 @@ +/* + * + * Copyright 2018 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. + * + */ + +#import <XCTest/XCTest.h> + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_CFSTREAM + +#include <netinet/in.h> + +#include <grpc/impl/codegen/sync.h> +#include <grpc/support/sync.h> + +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "test/core/util/test_config.h" + +// static int g_connections_complete = 0; +static gpr_mu g_mu; +static int g_connections_complete = 0; +static grpc_endpoint* g_connecting = nullptr; + +static void finish_connection() { + gpr_mu_lock(&g_mu); + g_connections_complete++; + gpr_mu_unlock(&g_mu); +} + +static void must_succeed(void* arg, grpc_error* error) { + GPR_ASSERT(g_connecting != nullptr); + GPR_ASSERT(error == GRPC_ERROR_NONE); + grpc_endpoint_shutdown(g_connecting, GRPC_ERROR_CREATE_FROM_STATIC_STRING("must_succeed called")); + grpc_endpoint_destroy(g_connecting); + g_connecting = nullptr; + finish_connection(); +} + +static void must_fail(void* arg, grpc_error* error) { + GPR_ASSERT(g_connecting == nullptr); + GPR_ASSERT(error != GRPC_ERROR_NONE); + const char* error_str = grpc_error_string(error); + NSLog(@"%s", error_str); + finish_connection(); +} + +@interface CFStreamClientTests : XCTestCase + +@end + +@implementation CFStreamClientTests + ++ (void)setUp { + grpc_init(); + gpr_mu_init(&g_mu); +} + ++ (void)tearDown { + grpc_shutdown(); +} + +- (void)testSucceeds { + grpc_resolved_address resolved_addr; + struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(resolved_addr.addr); + int svr_fd; + int r; + int connections_complete_before; + grpc_closure done; + grpc_core::ExecCtx exec_ctx; + + gpr_log(GPR_DEBUG, "test_succeeds"); + + memset(&resolved_addr, 0, sizeof(resolved_addr)); + resolved_addr.len = sizeof(struct sockaddr_in); + addr->sin_family = AF_INET; + + /* create a dummy server */ + svr_fd = socket(AF_INET, SOCK_STREAM, 0); + GPR_ASSERT(svr_fd >= 0); + GPR_ASSERT(0 == bind(svr_fd, (struct sockaddr*)addr, (socklen_t)resolved_addr.len)); + GPR_ASSERT(0 == listen(svr_fd, 1)); + + gpr_mu_lock(&g_mu); + connections_complete_before = g_connections_complete; + gpr_mu_unlock(&g_mu); + + /* connect to it */ + GPR_ASSERT(getsockname(svr_fd, (struct sockaddr*)addr, (socklen_t*)&resolved_addr.len) == 0); + GRPC_CLOSURE_INIT(&done, must_succeed, nullptr, grpc_schedule_on_exec_ctx); + grpc_tcp_client_connect(&done, &g_connecting, nullptr, nullptr, &resolved_addr, + GRPC_MILLIS_INF_FUTURE); + + /* await the connection */ + do { + resolved_addr.len = sizeof(addr); + r = accept(svr_fd, reinterpret_cast<struct sockaddr*>(addr), + reinterpret_cast<socklen_t*>(&resolved_addr.len)); + } while (r == -1 && errno == EINTR); + GPR_ASSERT(r >= 0); + close(r); + + grpc_core::ExecCtx::Get()->Flush(); + + /* wait for the connection callback to finish */ + gpr_mu_lock(&g_mu); + NSDate* deadline = [NSDate dateWithTimeIntervalSinceNow:5]; + while (connections_complete_before == g_connections_complete) { + gpr_mu_unlock(&g_mu); + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:deadline]; + gpr_mu_lock(&g_mu); + } + XCTAssertGreaterThan(g_connections_complete, connections_complete_before); + + gpr_mu_unlock(&g_mu); +} + +- (void)testFails { + grpc_core::ExecCtx exec_ctx; + + grpc_resolved_address resolved_addr; + struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(resolved_addr.addr); + int connections_complete_before; + grpc_closure done; + int svr_fd; + + gpr_log(GPR_DEBUG, "test_fails"); + + memset(&resolved_addr, 0, sizeof(resolved_addr)); + resolved_addr.len = static_cast<socklen_t>(sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; + + svr_fd = socket(AF_INET, SOCK_STREAM, 0); + GPR_ASSERT(svr_fd >= 0); + GPR_ASSERT(0 == bind(svr_fd, (struct sockaddr*)addr, (socklen_t)resolved_addr.len)); + GPR_ASSERT(0 == listen(svr_fd, 1)); + GPR_ASSERT(getsockname(svr_fd, (struct sockaddr*)addr, (socklen_t*)&resolved_addr.len) == 0); + close(svr_fd); + + gpr_mu_lock(&g_mu); + connections_complete_before = g_connections_complete; + gpr_mu_unlock(&g_mu); + + /* connect to a broken address */ + GRPC_CLOSURE_INIT(&done, must_fail, nullptr, grpc_schedule_on_exec_ctx); + grpc_tcp_client_connect(&done, &g_connecting, nullptr, nullptr, &resolved_addr, + GRPC_MILLIS_INF_FUTURE); + + grpc_core::ExecCtx::Get()->Flush(); + + /* wait for the connection callback to finish */ + gpr_mu_lock(&g_mu); + NSDate* deadline = [NSDate dateWithTimeIntervalSinceNow:5]; + while (g_connections_complete == connections_complete_before) { + gpr_mu_unlock(&g_mu); + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:deadline]; + gpr_mu_lock(&g_mu); + } + + XCTAssertGreaterThan(g_connections_complete, connections_complete_before); + + gpr_mu_unlock(&g_mu); +} + +@end + +#else // GRPC_CFSTREAM + +// Dummy test suite +@interface CFStreamClientTests : XCTestCase + +@end + +@implementation CFStreamClientTests + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +@end + +#endif // GRPC_CFSTREAM diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamEndpointTests.mm b/test/core/iomgr/ios/CFStreamTests/CFStreamEndpointTests.mm new file mode 100644 index 0000000000..fbc34c74d6 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/CFStreamEndpointTests.mm @@ -0,0 +1,344 @@ +/* + * + * Copyright 2018 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. + * + */ + +#import <XCTest/XCTest.h> + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_CFSTREAM + +#include <netinet/in.h> + +#include <grpc/impl/codegen/sync.h> +#include <grpc/support/sync.h> + +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "test/core/util/test_config.h" + +static const int kConnectTimeout = 5; +static const int kWriteTimeout = 5; +static const int kReadTimeout = 5; + +static const int kBufferSize = 10000; + +static const int kRunLoopTimeout = 1; + +static void set_atm(void *arg, grpc_error *error) { + gpr_atm *p = static_cast<gpr_atm *>(arg); + gpr_atm_full_cas(p, -1, reinterpret_cast<gpr_atm>(error)); +} + +static void init_event_closure(grpc_closure *closure, gpr_atm *atm) { + *atm = -1; + GRPC_CLOSURE_INIT(closure, set_atm, static_cast<void *>(atm), grpc_schedule_on_exec_ctx); +} + +static bool compare_slice_buffer_with_buffer(grpc_slice_buffer *slices, const char *buffer, + size_t buffer_len) { + if (slices->length != buffer_len) { + return false; + } + + for (int i = 0; i < slices->count; i++) { + grpc_slice slice = slices->slices[i]; + if (0 != memcmp(buffer, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice))) { + return false; + } + buffer += GRPC_SLICE_LENGTH(slice); + } + + return true; +} + +@interface CFStreamEndpointTests : XCTestCase + +@end + +@implementation CFStreamEndpointTests { + grpc_endpoint *ep_; + int svr_fd_; +} + +- (BOOL)waitForEvent:(gpr_atm *)event timeout:(int)timeout { + grpc_core::ExecCtx::Get()->Flush(); + + NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:kConnectTimeout]; + while (gpr_atm_acq_load(event) == -1 && [deadline timeIntervalSinceNow] > 0) { + NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:kRunLoopTimeout]; + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:deadline]; + } + + return (gpr_atm_acq_load(event) != -1); +} + ++ (void)setUp { + grpc_init(); +} + ++ (void)tearDown { + grpc_shutdown(); +} + +- (void)setUp { + self.continueAfterFailure = NO; + + // Set up CFStream connection before testing the endpoint + + grpc_core::ExecCtx exec_ctx; + + grpc_resolved_address resolved_addr; + struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(resolved_addr.addr); + int svr_fd; + int r; + gpr_atm connected = -1; + grpc_closure done; + + gpr_log(GPR_DEBUG, "test_succeeds"); + + memset(&resolved_addr, 0, sizeof(resolved_addr)); + resolved_addr.len = sizeof(struct sockaddr_in); + addr->sin_family = AF_INET; + + /* create a dummy server */ + svr_fd = socket(AF_INET, SOCK_STREAM, 0); + XCTAssertGreaterThanOrEqual(svr_fd, 0); + XCTAssertEqual(bind(svr_fd, (struct sockaddr *)addr, (socklen_t)resolved_addr.len), 0); + XCTAssertEqual(listen(svr_fd, 1), 0); + + /* connect to it */ + XCTAssertEqual(getsockname(svr_fd, (struct sockaddr *)addr, (socklen_t *)&resolved_addr.len), 0); + init_event_closure(&done, &connected); + grpc_tcp_client_connect(&done, &ep_, nullptr, nullptr, &resolved_addr, GRPC_MILLIS_INF_FUTURE); + + /* await the connection */ + do { + resolved_addr.len = sizeof(addr); + r = accept(svr_fd, reinterpret_cast<struct sockaddr *>(addr), + reinterpret_cast<socklen_t *>(&resolved_addr.len)); + } while (r == -1 && errno == EINTR); + XCTAssertGreaterThanOrEqual(r, 0); + svr_fd_ = r; + + /* wait for the connection callback to finish */ + XCTAssertEqual([self waitForEvent:&connected timeout:kConnectTimeout], YES); + XCTAssertEqual(reinterpret_cast<grpc_error *>(connected), GRPC_ERROR_NONE); +} + +- (void)tearDown { + grpc_core::ExecCtx exec_ctx; + close(svr_fd_); + grpc_endpoint_destroy(ep_); +} + +- (void)testReadWrite { + grpc_core::ExecCtx exec_ctx; + + gpr_atm read; + grpc_closure read_done; + grpc_slice_buffer read_slices; + grpc_slice_buffer read_one_slice; + gpr_atm write; + grpc_closure write_done; + grpc_slice_buffer write_slices; + + grpc_slice slice; + char write_buffer[kBufferSize]; + char read_buffer[kBufferSize]; + size_t recv_size = 0; + + grpc_slice_buffer_init(&write_slices); + slice = grpc_slice_from_static_buffer(write_buffer, kBufferSize); + grpc_slice_buffer_add(&write_slices, slice); + init_event_closure(&write_done, &write); + grpc_endpoint_write(ep_, &write_slices, &write_done); + + XCTAssertEqual([self waitForEvent:&write timeout:kWriteTimeout], YES); + XCTAssertEqual(reinterpret_cast<grpc_error *>(write), GRPC_ERROR_NONE); + + while (recv_size < kBufferSize) { + ssize_t size = recv(svr_fd_, read_buffer, kBufferSize, 0); + XCTAssertGreaterThanOrEqual(size, 0); + recv_size += size; + } + + XCTAssertEqual(recv_size, kBufferSize); + XCTAssertEqual(memcmp(read_buffer, write_buffer, kBufferSize), 0); + ssize_t send_size = send(svr_fd_, read_buffer, kBufferSize, 0); + XCTAssertGreaterThanOrEqual(send_size, 0); + + grpc_slice_buffer_init(&read_slices); + grpc_slice_buffer_init(&read_one_slice); + while (read_slices.length < kBufferSize) { + init_event_closure(&read_done, &read); + grpc_endpoint_read(ep_, &read_one_slice, &read_done); + XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES); + XCTAssertEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE); + grpc_slice_buffer_move_into(&read_one_slice, &read_slices); + XCTAssertLessThanOrEqual(read_slices.length, kBufferSize); + } + XCTAssertTrue(compare_slice_buffer_with_buffer(&read_slices, read_buffer, kBufferSize)); + + grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE); + grpc_slice_buffer_reset_and_unref(&read_slices); + grpc_slice_buffer_reset_and_unref(&write_slices); + grpc_slice_buffer_reset_and_unref(&read_one_slice); +} + +- (void)testShutdownBeforeRead { + grpc_core::ExecCtx exec_ctx; + + gpr_atm read; + grpc_closure read_done; + grpc_slice_buffer read_slices; + gpr_atm write; + grpc_closure write_done; + grpc_slice_buffer write_slices; + + grpc_slice slice; + char write_buffer[kBufferSize]; + char read_buffer[kBufferSize]; + size_t recv_size = 0; + + grpc_slice_buffer_init(&read_slices); + init_event_closure(&read_done, &read); + grpc_endpoint_read(ep_, &read_slices, &read_done); + + grpc_slice_buffer_init(&write_slices); + slice = grpc_slice_from_static_buffer(write_buffer, kBufferSize); + grpc_slice_buffer_add(&write_slices, slice); + init_event_closure(&write_done, &write); + grpc_endpoint_write(ep_, &write_slices, &write_done); + + XCTAssertEqual([self waitForEvent:&write timeout:kWriteTimeout], YES); + XCTAssertEqual(reinterpret_cast<grpc_error *>(write), GRPC_ERROR_NONE); + + while (recv_size < kBufferSize) { + ssize_t size = recv(svr_fd_, read_buffer, kBufferSize, 0); + XCTAssertGreaterThanOrEqual(size, 0); + recv_size += size; + } + + XCTAssertEqual(recv_size, kBufferSize); + XCTAssertEqual(memcmp(read_buffer, write_buffer, kBufferSize), 0); + + XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], NO); + + grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE); + + grpc_core::ExecCtx::Get()->Flush(); + XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES); + XCTAssertNotEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE); + + grpc_slice_buffer_reset_and_unref(&read_slices); + grpc_slice_buffer_reset_and_unref(&write_slices); +} + +- (void)testRemoteClosed { + grpc_core::ExecCtx exec_ctx; + + gpr_atm read; + grpc_closure read_done; + grpc_slice_buffer read_slices; + gpr_atm write; + grpc_closure write_done; + grpc_slice_buffer write_slices; + + grpc_slice slice; + char write_buffer[kBufferSize]; + char read_buffer[kBufferSize]; + size_t recv_size = 0; + + init_event_closure(&read_done, &read); + grpc_slice_buffer_init(&read_slices); + grpc_endpoint_read(ep_, &read_slices, &read_done); + + grpc_slice_buffer_init(&write_slices); + slice = grpc_slice_from_static_buffer(write_buffer, kBufferSize); + grpc_slice_buffer_add(&write_slices, slice); + init_event_closure(&write_done, &write); + grpc_endpoint_write(ep_, &write_slices, &write_done); + + XCTAssertEqual([self waitForEvent:&write timeout:kWriteTimeout], YES); + XCTAssertEqual(reinterpret_cast<grpc_error *>(write), GRPC_ERROR_NONE); + + while (recv_size < kBufferSize) { + ssize_t size = recv(svr_fd_, read_buffer, kBufferSize, 0); + XCTAssertGreaterThanOrEqual(size, 0); + recv_size += size; + } + + XCTAssertEqual(recv_size, kBufferSize); + XCTAssertEqual(memcmp(read_buffer, write_buffer, kBufferSize), 0); + + close(svr_fd_); + + XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES); + XCTAssertNotEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE); + + grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE); + grpc_slice_buffer_reset_and_unref(&read_slices); + grpc_slice_buffer_reset_and_unref(&write_slices); +} + +- (void)testRemoteReset { + grpc_core::ExecCtx exec_ctx; + + gpr_atm read; + grpc_closure read_done; + grpc_slice_buffer read_slices; + + init_event_closure(&read_done, &read); + grpc_slice_buffer_init(&read_slices); + grpc_endpoint_read(ep_, &read_slices, &read_done); + + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + setsockopt(svr_fd_, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); + + close(svr_fd_); + + XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES); + XCTAssertNotEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE); + + grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE); + grpc_slice_buffer_reset_and_unref(&read_slices); +} + +@end + +#else // GRPC_CFSTREAM + +// Dummy test suite +@interface CFStreamEndpointTests : XCTestCase +@end + +@implementation CFStreamEndpointTests +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +@end + +#endif // GRPC_CFSTREAM diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/project.pbxproj b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..2218f129ae --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/project.pbxproj @@ -0,0 +1,338 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 5E143B892069D72200715A6E /* CFStreamClientTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E143B882069D72200715A6E /* CFStreamClientTests.mm */; }; + 5E143B8C206B5F9F00715A6E /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5E143B8A2069D72700715A6E /* Info.plist */; }; + 5E143B8E206C5B9A00715A6E /* CFStreamEndpointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E143B8D206C5B9A00715A6E /* CFStreamEndpointTests.mm */; }; + 604EA96D9CD477A8EA411BDF /* libPods-CFStreamTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AFFA154D492751CEAC05D591 /* libPods-CFStreamTests.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5E143B792069D67300715A6E /* CFStreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CFStreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E143B882069D72200715A6E /* CFStreamClientTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CFStreamClientTests.mm; sourceTree = "<group>"; }; + 5E143B8A2069D72700715A6E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 5E143B8D206C5B9A00715A6E /* CFStreamEndpointTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CFStreamEndpointTests.mm; sourceTree = "<group>"; }; + 8CB4409E07E180CCA59987DF /* Pods-CFStreamTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CFStreamTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CFStreamTests/Pods-CFStreamTests.release.xcconfig"; sourceTree = "<group>"; }; + 9E3FAF9DA6B98ED4FE6D6848 /* Pods-CFStreamTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CFStreamTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CFStreamTests/Pods-CFStreamTests.debug.xcconfig"; sourceTree = "<group>"; }; + AFFA154D492751CEAC05D591 /* libPods-CFStreamTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CFStreamTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5E143B762069D67300715A6E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 604EA96D9CD477A8EA411BDF /* libPods-CFStreamTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5E143B582069D67300715A6E = { + isa = PBXGroup; + children = ( + 5E143B8D206C5B9A00715A6E /* CFStreamEndpointTests.mm */, + 5E143B8A2069D72700715A6E /* Info.plist */, + 5E143B882069D72200715A6E /* CFStreamClientTests.mm */, + 5E143B622069D67300715A6E /* Products */, + A331D95F7F230B507FBF6D22 /* Pods */, + 6AC36F6C5DB5CA8F717D1B67 /* Frameworks */, + ); + sourceTree = "<group>"; + }; + 5E143B622069D67300715A6E /* Products */ = { + isa = PBXGroup; + children = ( + 5E143B792069D67300715A6E /* CFStreamTests.xctest */, + ); + name = Products; + sourceTree = "<group>"; + }; + 6AC36F6C5DB5CA8F717D1B67 /* Frameworks */ = { + isa = PBXGroup; + children = ( + AFFA154D492751CEAC05D591 /* libPods-CFStreamTests.a */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + A331D95F7F230B507FBF6D22 /* Pods */ = { + isa = PBXGroup; + children = ( + 9E3FAF9DA6B98ED4FE6D6848 /* Pods-CFStreamTests.debug.xcconfig */, + 8CB4409E07E180CCA59987DF /* Pods-CFStreamTests.release.xcconfig */, + ); + name = Pods; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5E143B782069D67300715A6E /* CFStreamTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E143B852069D67300715A6E /* Build configuration list for PBXNativeTarget "CFStreamTests" */; + buildPhases = ( + 4EBA55D3E23FC6C84596E3D5 /* [CP] Check Pods Manifest.lock */, + 5E143B752069D67300715A6E /* Sources */, + 5E143B762069D67300715A6E /* Frameworks */, + 5E143B772069D67300715A6E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CFStreamTests; + productName = CFStreamTestsTests; + productReference = 5E143B792069D67300715A6E /* CFStreamTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5E143B592069D67300715A6E /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = gRPC; + TargetAttributes = { + 5E143B782069D67300715A6E = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 5E143B5C2069D67300715A6E /* Build configuration list for PBXProject "CFStreamTests" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5E143B582069D67300715A6E; + productRefGroup = 5E143B622069D67300715A6E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5E143B782069D67300715A6E /* CFStreamTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5E143B772069D67300715A6E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E143B8C206B5F9F00715A6E /* Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4EBA55D3E23FC6C84596E3D5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-CFStreamTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5E143B752069D67300715A6E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E143B892069D72200715A6E /* CFStreamClientTests.mm in Sources */, + 5E143B8E206C5B9A00715A6E /* CFStreamEndpointTests.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 5E143B802069D67300715A6E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 5E143B812069D67300715A6E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5E143B862069D67300715A6E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9E3FAF9DA6B98ED4FE6D6848 /* Pods-CFStreamTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "COCOAPODS=1", + "$(inherited)", + "PB_FIELD_32BIT=1", + "PB_NO_PACKED_STRUCTS=1", + "GRPC_CFSTREAM=1", + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CFStreamTestsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = ../../../../..; + }; + name = Debug; + }; + 5E143B872069D67300715A6E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8CB4409E07E180CCA59987DF /* Pods-CFStreamTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CFStreamTestsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = ../../../../..; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5E143B5C2069D67300715A6E /* Build configuration list for PBXProject "CFStreamTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E143B802069D67300715A6E /* Debug */, + 5E143B812069D67300715A6E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5E143B852069D67300715A6E /* Build configuration list for PBXNativeTarget "CFStreamTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E143B862069D67300715A6E /* Debug */, + 5E143B872069D67300715A6E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5E143B592069D67300715A6E /* Project object */; +} diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests.xcscheme new file mode 100644 index 0000000000..25d6f780a1 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests.xcscheme @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0920" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E143B782069D67300715A6E" + BuildableName = "CFStreamTests.xctest" + BlueprintName = "CFStreamTests" + ReferencedContainer = "container:CFStreamTests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Asan.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Asan.xcscheme new file mode 100644 index 0000000000..6c5c43aa72 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Asan.xcscheme @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0920" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableAddressSanitizer = "YES" + enableASanStackUseAfterReturn = "YES" + language = "" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E143B782069D67300715A6E" + BuildableName = "CFStreamTests.xctest" + BlueprintName = "CFStreamTests" + ReferencedContainer = "container:CFStreamTests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableASanStackUseAfterReturn = "YES" + language = "" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Msan.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Msan.xcscheme new file mode 100644 index 0000000000..3e39ff84d0 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Msan.xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0920" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E143B782069D67300715A6E" + BuildableName = "CFStreamTests.xctest" + BlueprintName = "CFStreamTests" + ReferencedContainer = "container:CFStreamTests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <AdditionalOptions> + <AdditionalOption + key = "DYLD_INSERT_LIBRARIES" + value = "/usr/lib/libgmalloc.dylib" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "NSZombieEnabled" + value = "YES" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "MallocGuardEdges" + value = "" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "MallocScribble" + value = "" + isEnabled = "YES"> + </AdditionalOption> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Tsan.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Tsan.xcscheme new file mode 100644 index 0000000000..f0bde837c5 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Tsan.xcscheme @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0920" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableThreadSanitizer = "YES" + language = "" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E143B782069D67300715A6E" + BuildableName = "CFStreamTests.xctest" + BlueprintName = "CFStreamTests" + ReferencedContainer = "container:CFStreamTests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + stopOnEveryThreadSanitizerIssue = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/test/core/iomgr/ios/CFStreamTests/Info.plist b/test/core/iomgr/ios/CFStreamTests/Info.plist new file mode 100644 index 0000000000..6c40a6cd0c --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/Info.plist @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> +</dict> +</plist> diff --git a/test/core/iomgr/ios/CFStreamTests/Podfile b/test/core/iomgr/ios/CFStreamTests/Podfile new file mode 100644 index 0000000000..e6ec66d549 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/Podfile @@ -0,0 +1,51 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +install! 'cocoapods', :deterministic_uuids => false + +# Location of gRPC's repo root relative to this file. +GRPC_LOCAL_SRC = '../../../../..' + +# Install the dependencies in the main target plus all test targets. +target 'CFStreamTests' do + pod 'gRPC-Core/CFStream-Implementation', :path => GRPC_LOCAL_SRC + pod 'BoringSSL-GRPC', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true +end + +pre_install do |installer| + # This is the gRPC-Core podspec object, as initialized by its podspec file. + grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Core'}.root_spec + + # Copied from gRPC-Core.podspec, except for the adjusted src_root: + src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" + grpc_core_spec.pod_target_xcconfig = { + 'GRPC_SRC_ROOT' => src_root, + 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', + 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', + # If we don't set these two settings, `include/grpc/support/time.h` and + # `src/core/lib/gpr/string.h` shadow the system `<time.h>` and `<string.h>`, breaking the + # build. + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES' + end + + # CocoaPods creates duplicated library targets of gRPC-Core when the test targets include + # non-default subspecs of gRPC-Core. All of these library targets start with prefix 'gRPC-Core' + # and require the same error suppresion. + if target.name.start_with?('gRPC-Core') + target.build_configurations.each do |config| + # TODO(zyc): Remove this setting after the issue is resolved + # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void + # function" warning + config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO' + end + end + end +end diff --git a/test/core/iomgr/ios/CFStreamTests/build_tests.sh b/test/core/iomgr/ios/CFStreamTests/build_tests.sh new file mode 100755 index 0000000000..d23f26f5db --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/build_tests.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright 2018 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. + +# Don't run this script standalone. Instead, run from the repository root: +# ./tools/run_tests/run_tests.py -l objc + +set -e + +# CocoaPods requires the terminal to be using UTF-8 encoding. +export LANG=en_US.UTF-8 + +cd "$(dirname "$0")" + +hash pod 2>/dev/null || { echo >&2 "Cocoapods needs to be installed."; exit 1; } +hash xcodebuild 2>/dev/null || { + echo >&2 "XCode command-line tools need to be installed." + exit 1 +} + +# clean the directory +rm -rf Pods +rm -rf CFStreamTests.xcworkspace +rm -f Podfile.lock + +echo "TIME: $(date)" +pod install + diff --git a/test/core/iomgr/ios/CFStreamTests/run_tests.sh b/test/core/iomgr/ios/CFStreamTests/run_tests.sh new file mode 100755 index 0000000000..1045ec10a8 --- /dev/null +++ b/test/core/iomgr/ios/CFStreamTests/run_tests.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# Copyright 2018 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. + +# Don't run this script standalone. Instead, run from the repository root: +# ./tools/run_tests/run_tests.py -l objc + +set -ev + +cd "$(dirname "$0")" + +echo "TIME: $(date)" + +XCODEBUILD_FILTER='(^CompileC |^Ld |^ *[^ ]*clang |^ *cd |^ *export |^Libtool |^ *[^ ]*libtool |^CpHeader |^ *builtin-copy )' + +xcodebuild \ + -workspace CFStreamTests.xcworkspace \ + -scheme CFStreamTests \ + -destination name="iPhone 8" \ + test \ + | egrep -v "$XCODEBUILD_FILTER" \ + | egrep -v '^$' \ + | egrep -v "(GPBDictionary|GPBArray)" - + +echo "TIME: $(date)" + +xcodebuild \ + -workspace CFStreamTests.xcworkspace \ + -scheme CFStreamTests_Asan \ + -destination name="iPhone 8" \ + test \ + | egrep -v "$XCODEBUILD_FILTER" \ + | egrep -v '^$' \ + | egrep -v "(GPBDictionary|GPBArray)" - + +echo "TIME: $(date)" + +xcodebuild \ + -workspace CFStreamTests.xcworkspace \ + -scheme CFStreamTests_Tsan \ + -destination name="iPhone 8" \ + test \ + | egrep -v "$XCODEBUILD_FILTER" \ + | egrep -v '^$' \ + | egrep -v "(GPBDictionary|GPBArray)" - + +echo "TIME: $(date)" + +xcodebuild \ + -workspace CFStreamTests.xcworkspace \ + -scheme CFStreamTests_Msan \ + -destination name="iPhone 8" \ + test \ + | egrep -v "$XCODEBUILD_FILTER" \ + | egrep -v '^$' \ + | egrep -v "(GPBDictionary|GPBArray)" - diff --git a/test/core/iomgr/pollset_set_test.cc b/test/core/iomgr/pollset_set_test.cc deleted file mode 100644 index 0dc75a5f3f..0000000000 --- a/test/core/iomgr/pollset_set_test.cc +++ /dev/null @@ -1,448 +0,0 @@ -/* - * - * Copyright 2016 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/lib/iomgr/port.h" - -/* This test only relevant on linux systems where epoll is available */ -#ifdef GRPC_LINUX_EPOLL_CREATE1 - -#include <errno.h> -#include <string.h> -#include <unistd.h> - -#include <grpc/grpc.h> -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "test/core/util/test_config.h" - -/******************************************************************************* - * test_pollset_set - */ - -typedef struct test_pollset_set { - grpc_pollset_set* pss; -} test_pollset_set; - -void init_test_pollset_sets(test_pollset_set* pollset_sets, const int num_pss) { - for (int i = 0; i < num_pss; i++) { - pollset_sets[i].pss = grpc_pollset_set_create(); - } -} - -void cleanup_test_pollset_sets(test_pollset_set* pollset_sets, - const int num_pss) { - for (int i = 0; i < num_pss; i++) { - grpc_pollset_set_destroy(pollset_sets[i].pss); - pollset_sets[i].pss = nullptr; - } -} - -/******************************************************************************* - * test_pollset - */ - -typedef struct test_pollset { - grpc_pollset* ps; - gpr_mu* mu; -} test_pollset; - -static void init_test_pollsets(test_pollset* pollsets, const int num_pollsets) { - for (int i = 0; i < num_pollsets; i++) { - pollsets[i].ps = - static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size())); - grpc_pollset_init(pollsets[i].ps, &pollsets[i].mu); - } -} - -static void destroy_pollset(void* p, grpc_error* error) { - grpc_pollset_destroy(static_cast<grpc_pollset*>(p)); -} - -static void cleanup_test_pollsets(test_pollset* pollsets, - const int num_pollsets) { - grpc_closure destroyed; - for (int i = 0; i < num_pollsets; i++) { - GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, pollsets[i].ps, - grpc_schedule_on_exec_ctx); - grpc_pollset_shutdown(pollsets[i].ps, &destroyed); - - grpc_core::ExecCtx::Get()->Flush(); - gpr_free(pollsets[i].ps); - pollsets[i].ps = nullptr; - } -} - -/******************************************************************************* - * test_fd - */ - -typedef struct test_fd { - grpc_fd* fd; - grpc_wakeup_fd wakeup_fd; - - bool is_on_readable_called; /* Is on_readable closure is called ? */ - grpc_closure on_readable; /* Closure to call when this fd is readable */ -} test_fd; - -void on_readable(void* tfd, grpc_error* error) { - (static_cast<test_fd*>(tfd))->is_on_readable_called = true; -} - -static void reset_test_fd(test_fd* tfd) { - tfd->is_on_readable_called = false; - - GRPC_CLOSURE_INIT(&tfd->on_readable, on_readable, tfd, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(tfd->fd, &tfd->on_readable); -} - -static void init_test_fds(test_fd* tfds, const int num_fds) { - for (int i = 0; i < num_fds; i++) { - GPR_ASSERT(GRPC_ERROR_NONE == grpc_wakeup_fd_init(&tfds[i].wakeup_fd)); - tfds[i].fd = grpc_fd_create(GRPC_WAKEUP_FD_GET_READ_FD(&tfds[i].wakeup_fd), - "test_fd"); - reset_test_fd(&tfds[i]); - } -} - -static void cleanup_test_fds(test_fd* tfds, const int num_fds) { - int release_fd; - - for (int i = 0; i < num_fds; i++) { - grpc_fd_shutdown(tfds[i].fd, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("fd cleanup")); - grpc_core::ExecCtx::Get()->Flush(); - - /* grpc_fd_orphan frees the memory allocated for grpc_fd. Normally it also - * calls close() on the underlying fd. In our case, we are using - * grpc_wakeup_fd and we would like to destroy it ourselves (by calling - * grpc_wakeup_fd_destroy). To prevent grpc_fd from calling close() on the - * underlying fd, call it with a non-NULL 'release_fd' parameter */ - grpc_fd_orphan(tfds[i].fd, nullptr, &release_fd, false /* already_closed */, - "test_fd_cleanup"); - grpc_core::ExecCtx::Get()->Flush(); - - grpc_wakeup_fd_destroy(&tfds[i].wakeup_fd); - } -} - -static void make_test_fds_readable(test_fd* tfds, const int num_fds) { - for (int i = 0; i < num_fds; i++) { - GPR_ASSERT(GRPC_ERROR_NONE == grpc_wakeup_fd_wakeup(&tfds[i].wakeup_fd)); - } -} - -static void verify_readable_and_reset(test_fd* tfds, const int num_fds) { - for (int i = 0; i < num_fds; i++) { - /* Verify that the on_readable callback was called */ - GPR_ASSERT(tfds[i].is_on_readable_called); - - /* Reset the tfd[i] structure */ - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_wakeup_fd_consume_wakeup(&tfds[i].wakeup_fd)); - reset_test_fd(&tfds[i]); - } -} - -/******************************************************************************* - * Main tests - */ - -/* Test some typical scenarios in pollset_set */ -static void pollset_set_test_basic() { - /* We construct the following structure for this test: - * - * +---> FD0 (Added before PSS1, PS1 and PS2 are added to PSS0) - * | - * +---> FD5 (Added after PSS1, PS1 and PS2 are added to PSS0) - * | - * | - * | +---> FD1 (Added before PSS1 is added to PSS0) - * | | - * | +---> FD6 (Added after PSS1 is added to PSS0) - * | | - * +---> PSS1--+ +--> FD2 (Added before PS0 is added to PSS1) - * | | | - * | +---> PS0---+ - * | | - * PSS0---+ +--> FD7 (Added after PS0 is added to PSS1) - * | - * | - * | +---> FD3 (Added before PS1 is added to PSS0) - * | | - * +---> PS1---+ - * | | - * | +---> FD8 (Added after PS1 added to PSS0) - * | - * | - * | +---> FD4 (Added before PS2 is added to PSS0) - * | | - * +---> PS2---+ - * | - * +---> FD9 (Added after PS2 is added to PSS0) - */ - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - grpc_millis deadline; - - test_fd tfds[10]; - test_pollset pollsets[3]; - test_pollset_set pollset_sets[2]; - const int num_fds = GPR_ARRAY_SIZE(tfds); - const int num_ps = GPR_ARRAY_SIZE(pollsets); - const int num_pss = GPR_ARRAY_SIZE(pollset_sets); - - init_test_fds(tfds, num_fds); - init_test_pollsets(pollsets, num_ps); - init_test_pollset_sets(pollset_sets, num_pss); - - /* Construct the pollset_set/pollset/fd tree (see diagram above) */ - - grpc_pollset_set_add_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[1].fd); - - grpc_pollset_add_fd(pollsets[0].ps, tfds[2].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[3].fd); - grpc_pollset_add_fd(pollsets[2].ps, tfds[4].fd); - - grpc_pollset_set_add_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - - grpc_pollset_set_add_pollset(pollset_sets[1].pss, pollsets[0].ps); - grpc_pollset_set_add_pollset(pollset_sets[0].pss, pollsets[1].ps); - grpc_pollset_set_add_pollset(pollset_sets[0].pss, pollsets[2].ps); - - grpc_pollset_set_add_fd(pollset_sets[0].pss, tfds[5].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[6].fd); - - grpc_pollset_add_fd(pollsets[0].ps, tfds[7].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[8].fd); - grpc_pollset_add_fd(pollsets[2].ps, tfds[9].fd); - - grpc_core::ExecCtx::Get()->Flush(); - - /* Test that if any FD in the above structure is readable, it is observable by - * doing grpc_pollset_work on any pollset - * - * For every pollset, do the following: - * - (Ensure that all FDs are in reset state) - * - Make all FDs readable - * - Call grpc_pollset_work() on the pollset - * - Flush the exec_ctx - * - Verify that on_readable call back was called for all FDs (and - * reset the FDs) - * */ - for (int i = 0; i < num_ps; i++) { - make_test_fds_readable(tfds, num_fds); - - gpr_mu_lock(pollsets[i].mu); - deadline = grpc_timespec_to_millis_round_up( - grpc_timeout_milliseconds_to_deadline(2)); - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_pollset_work(pollsets[i].ps, &worker, deadline)); - gpr_mu_unlock(pollsets[i].mu); - - grpc_core::ExecCtx::Get()->Flush(); - - verify_readable_and_reset(tfds, num_fds); - grpc_core::ExecCtx::Get()->Flush(); - } - - /* Test tear down */ - grpc_pollset_set_del_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_del_fd(pollset_sets[0].pss, tfds[5].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[1].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[6].fd); - grpc_core::ExecCtx::Get()->Flush(); - - grpc_pollset_set_del_pollset(pollset_sets[1].pss, pollsets[0].ps); - grpc_pollset_set_del_pollset(pollset_sets[0].pss, pollsets[1].ps); - grpc_pollset_set_del_pollset(pollset_sets[0].pss, pollsets[2].ps); - - grpc_pollset_set_del_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - grpc_core::ExecCtx::Get()->Flush(); - - cleanup_test_fds(tfds, num_fds); - cleanup_test_pollsets(pollsets, num_ps); - cleanup_test_pollset_sets(pollset_sets, num_pss); -} - -/* Same FD added multiple times to the pollset_set tree */ -void pollset_set_test_dup_fds() { - /* We construct the following structure for this test: - * - * +---> FD0 - * | - * | - * PSS0---+ - * | +---> FD0 (also under PSS0) - * | | - * +---> PSS1--+ +--> FD1 (also under PSS1) - * | | - * +---> PS ---+ - * | | - * | +--> FD2 - * +---> FD1 - */ - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - grpc_millis deadline; - - test_fd tfds[3]; - test_pollset pollset; - test_pollset_set pollset_sets[2]; - const int num_fds = GPR_ARRAY_SIZE(tfds); - const int num_ps = 1; - const int num_pss = GPR_ARRAY_SIZE(pollset_sets); - - init_test_fds(tfds, num_fds); - init_test_pollsets(&pollset, num_ps); - init_test_pollset_sets(pollset_sets, num_pss); - - /* Construct the structure */ - grpc_pollset_set_add_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[0].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[1].fd); - - grpc_pollset_add_fd(pollset.ps, tfds[1].fd); - grpc_pollset_add_fd(pollset.ps, tfds[2].fd); - - grpc_pollset_set_add_pollset(pollset_sets[1].pss, pollset.ps); - grpc_pollset_set_add_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - - /* Test. Make all FDs readable and make sure that can be observed by doing a - * grpc_pollset_work on the pollset 'PS' */ - make_test_fds_readable(tfds, num_fds); - - gpr_mu_lock(pollset.mu); - deadline = grpc_timespec_to_millis_round_up( - grpc_timeout_milliseconds_to_deadline(2)); - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_pollset_work(pollset.ps, &worker, deadline)); - gpr_mu_unlock(pollset.mu); - grpc_core::ExecCtx::Get()->Flush(); - - verify_readable_and_reset(tfds, num_fds); - grpc_core::ExecCtx::Get()->Flush(); - - /* Tear down */ - grpc_pollset_set_del_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[0].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[1].fd); - - grpc_pollset_set_del_pollset(pollset_sets[1].pss, pollset.ps); - grpc_pollset_set_del_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - grpc_core::ExecCtx::Get()->Flush(); - - cleanup_test_fds(tfds, num_fds); - cleanup_test_pollsets(&pollset, num_ps); - cleanup_test_pollset_sets(pollset_sets, num_pss); -} - -/* Pollset_set with an empty pollset */ -void pollset_set_test_empty_pollset() { - /* We construct the following structure for this test: - * - * +---> PS0 (EMPTY) - * | - * +---> FD0 - * | - * PSS0---+ - * | +---> FD1 - * | | - * +---> PS1--+ - * | - * +---> FD2 - */ - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - grpc_millis deadline; - - test_fd tfds[3]; - test_pollset pollsets[2]; - test_pollset_set pollset_set; - const int num_fds = GPR_ARRAY_SIZE(tfds); - const int num_ps = GPR_ARRAY_SIZE(pollsets); - const int num_pss = 1; - - init_test_fds(tfds, num_fds); - init_test_pollsets(pollsets, num_ps); - init_test_pollset_sets(&pollset_set, num_pss); - - /* Construct the structure */ - grpc_pollset_set_add_fd(pollset_set.pss, tfds[0].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[1].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[2].fd); - - grpc_pollset_set_add_pollset(pollset_set.pss, pollsets[0].ps); - grpc_pollset_set_add_pollset(pollset_set.pss, pollsets[1].ps); - - /* Test. Make all FDs readable and make sure that can be observed by doing - * grpc_pollset_work on the empty pollset 'PS0' */ - make_test_fds_readable(tfds, num_fds); - - gpr_mu_lock(pollsets[0].mu); - deadline = grpc_timespec_to_millis_round_up( - grpc_timeout_milliseconds_to_deadline(2)); - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_pollset_work(pollsets[0].ps, &worker, deadline)); - gpr_mu_unlock(pollsets[0].mu); - grpc_core::ExecCtx::Get()->Flush(); - - verify_readable_and_reset(tfds, num_fds); - grpc_core::ExecCtx::Get()->Flush(); - - /* Tear down */ - grpc_pollset_set_del_fd(pollset_set.pss, tfds[0].fd); - grpc_pollset_set_del_pollset(pollset_set.pss, pollsets[0].ps); - grpc_pollset_set_del_pollset(pollset_set.pss, pollsets[1].ps); - grpc_core::ExecCtx::Get()->Flush(); - - cleanup_test_fds(tfds, num_fds); - cleanup_test_pollsets(pollsets, num_ps); - cleanup_test_pollset_sets(&pollset_set, num_pss); -} - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - grpc_init(); - { - grpc_core::ExecCtx exec_ctx; - const char* poll_strategy = grpc_get_poll_strategy_name(); - - if (poll_strategy != nullptr && - (strcmp(poll_strategy, "epollsig") == 0 || - strcmp(poll_strategy, "epoll-threadpool") == 0)) { - pollset_set_test_basic(); - pollset_set_test_dup_fds(); - pollset_set_test_empty_pollset(); - } else { - gpr_log(GPR_INFO, - "Skipping the test. The test is only relevant for 'epoll' " - "strategy. and the current strategy is: '%s'", - poll_strategy); - } - } - grpc_shutdown(); - return 0; -} -#else /* defined(GRPC_LINUX_EPOLL_CREATE1) */ -int main(int argc, char** argv) { return 0; } -#endif /* !defined(GRPC_LINUX_EPOLL_CREATE1) */ diff --git a/test/core/iomgr/resolve_address_posix_test.cc b/test/core/iomgr/resolve_address_posix_test.cc index 79b2b50e70..e495e4c877 100644 --- a/test/core/iomgr/resolve_address_posix_test.cc +++ b/test/core/iomgr/resolve_address_posix_test.cc @@ -91,7 +91,7 @@ static void actually_poll(void* argsp) { break; } grpc_millis time_left = deadline - grpc_core::ExecCtx::Get()->Now(); - gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRIdPTR, done, time_left); + gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64, done, time_left); GPR_ASSERT(time_left >= 0); grpc_pollset_worker* worker = nullptr; gpr_mu_lock(args->mu); diff --git a/test/core/iomgr/resolve_address_test.cc b/test/core/iomgr/resolve_address_test.cc index a0dc484f3e..52e4840c7c 100644 --- a/test/core/iomgr/resolve_address_test.cc +++ b/test/core/iomgr/resolve_address_test.cc @@ -22,8 +22,14 @@ #include <grpc/support/log.h> #include <grpc/support/sync.h> #include <grpc/support/time.h> + +#include <string.h> + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr.h" +#include "test/core/util/cmdline.h" #include "test/core/util/test_config.h" static gpr_timespec test_deadline(void) { @@ -82,7 +88,7 @@ static void poll_pollset_until_request_done(args_struct* args) { break; } grpc_millis time_left = deadline - grpc_core::ExecCtx::Get()->Now(); - gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRIdPTR, done, time_left); + gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64, done, time_left); GPR_ASSERT(time_left >= 0); grpc_pollset_worker* worker = nullptr; gpr_mu_lock(args->mu); @@ -240,6 +246,28 @@ static void test_unparseable_hostports(void) { } int main(int argc, char** argv) { + // First set the resolver type based off of --resolver + const char* resolver_type = nullptr; + gpr_cmdline* cl = gpr_cmdline_create("resolve address test"); + gpr_cmdline_add_string(cl, "resolver", "Resolver type (ares or native)", + &resolver_type); + gpr_cmdline_parse(cl, argc, argv); + const char* cur_resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + if (cur_resolver != nullptr && strlen(cur_resolver) != 0) { + gpr_log(GPR_INFO, "Warning: overriding resolver setting of %s", + cur_resolver); + } + if (gpr_stricmp(resolver_type, "native") == 0) { + gpr_setenv("GRPC_DNS_RESOLVER", "native"); + } else if (gpr_stricmp(resolver_type, "ares") == 0) { +#ifndef GRPC_UV + gpr_setenv("GRPC_DNS_RESOLVER", "ares"); +#endif + } else { + gpr_log(GPR_ERROR, "--resolver_type was not set to ares or native"); + abort(); + } + // Run the test. grpc_test_init(argc, argv); grpc_init(); { @@ -250,10 +278,18 @@ int main(int argc, char** argv) { test_missing_default_port(); test_ipv6_with_port(); test_ipv6_without_port(); - test_invalid_ip_addresses(); - test_unparseable_hostports(); + if (gpr_stricmp(resolver_type, "ares") != 0) { + // These tests can trigger DNS queries to the nearby nameserver + // that need to come back in order for the test to succeed. + // c-ares is prone to not using the local system caches that the + // native getaddrinfo implementations take advantage of, so running + // these unit tests under c-ares risks flakiness. + test_invalid_ip_addresses(); + test_unparseable_hostports(); + } grpc_executor_shutdown(); } + gpr_cmdline_destroy(cl); grpc_shutdown(); return 0; diff --git a/test/core/iomgr/resource_quota_test.cc b/test/core/iomgr/resource_quota_test.cc index 059ff7b5f8..f3b35fed32 100644 --- a/test/core/iomgr/resource_quota_test.cc +++ b/test/core/iomgr/resource_quota_test.cc @@ -798,6 +798,98 @@ static void test_negative_rq_free_pool(void) { } } +// Simple test to check resource quota thread limits +static void test_thread_limit() { + grpc_core::ExecCtx exec_ctx; + + grpc_resource_quota* rq = grpc_resource_quota_create("test_thread_limit"); + grpc_resource_user* ru1 = grpc_resource_user_create(rq, "ru1"); + grpc_resource_user* ru2 = grpc_resource_user_create(rq, "ru2"); + + // Max threads = 100 + grpc_resource_quota_set_max_threads(rq, 100); + + // Request quota for 100 threads (50 for ru1, 50 for ru2) + GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 10)); + GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 10)); + GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 40)); + GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 40)); + + // Threads exhausted. Next request must fail + GPR_ASSERT(!grpc_resource_user_allocate_threads(ru2, 20)); + + // Free 20 threads from two different users + grpc_resource_user_free_threads(ru1, 10); + grpc_resource_user_free_threads(ru2, 10); + + // Next request to 20 threads must succeed + GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 20)); + + // No more thread quota again + GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 20)); + + // Free 10 more + grpc_resource_user_free_threads(ru1, 10); + + GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 5)); + GPR_ASSERT( + !grpc_resource_user_allocate_threads(ru2, 10)); // Only 5 available + GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 5)); + + // Teardown (ru1 and ru2 release all the quota back to rq) + grpc_resource_user_unref(ru1); + grpc_resource_user_unref(ru2); + grpc_resource_quota_unref(rq); +} + +// Change max quota in either direction dynamically +static void test_thread_maxquota_change() { + grpc_core::ExecCtx exec_ctx; + + grpc_resource_quota* rq = + grpc_resource_quota_create("test_thread_maxquota_change"); + grpc_resource_user* ru1 = grpc_resource_user_create(rq, "ru1"); + grpc_resource_user* ru2 = grpc_resource_user_create(rq, "ru2"); + + // Max threads = 100 + grpc_resource_quota_set_max_threads(rq, 100); + + // Request quota for 100 threads (50 for ru1, 50 for ru2) + GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 50)); + GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 50)); + + // Threads exhausted. Next request must fail + GPR_ASSERT(!grpc_resource_user_allocate_threads(ru2, 20)); + + // Increase maxquota and retry + // Max threads = 150; + grpc_resource_quota_set_max_threads(rq, 150); + GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 20)); // ru2=70, ru1=50 + + // Decrease maxquota (Note: Quota already given to ru1 and ru2 is unaffected) + // Max threads = 10; + grpc_resource_quota_set_max_threads(rq, 10); + + // New requests will fail until quota is available + GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 10)); + + // Make quota available + grpc_resource_user_free_threads(ru1, 50); // ru1 now has 0 + GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 10)); // not enough + + grpc_resource_user_free_threads(ru2, 70); // ru2 now has 0 + + // Now we can get quota up-to 10, the current max + GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 10)); + // No more thread quota again + GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 10)); + + // Teardown (ru1 and ru2 release all the quota back to rq) + grpc_resource_user_unref(ru1); + grpc_resource_user_unref(ru2); + grpc_resource_quota_unref(rq); +} + int main(int argc, char** argv) { grpc_test_init(argc, argv); grpc_init(); @@ -827,6 +919,11 @@ int main(int argc, char** argv) { test_negative_rq_free_pool(); gpr_mu_destroy(&g_mu); gpr_cv_destroy(&g_cv); + + // Resource quota thread related + test_thread_limit(); + test_thread_maxquota_change(); + grpc_shutdown(); return 0; } diff --git a/test/core/iomgr/tcp_posix_test.cc b/test/core/iomgr/tcp_posix_test.cc index f4df6fca23..6447cc234d 100644 --- a/test/core/iomgr/tcp_posix_test.cc +++ b/test/core/iomgr/tcp_posix_test.cc @@ -36,6 +36,9 @@ #include <grpc/support/time.h> #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/buffer_list.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/sockaddr_posix.h" #include "src/core/lib/slice/slice_internal.h" #include "test/core/iomgr/endpoint_tests.h" #include "test/core/util/test_config.h" @@ -68,6 +71,43 @@ static void create_sockets(int sv[2]) { GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); } +static void create_inet_sockets(int sv[2]) { + /* Prepare listening socket */ + struct sockaddr_in addr; + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + int sock = socket(AF_INET, SOCK_STREAM, 0); + GPR_ASSERT(sock); + GPR_ASSERT(bind(sock, (sockaddr*)&addr, sizeof(sockaddr_in)) == 0); + listen(sock, 1); + + /* Prepare client socket and connect to server */ + socklen_t len = sizeof(sockaddr_in); + GPR_ASSERT(getsockname(sock, (sockaddr*)&addr, &len) == 0); + + int client = socket(AF_INET, SOCK_STREAM, 0); + GPR_ASSERT(client); + int ret; + do { + ret = connect(client, (sockaddr*)&addr, sizeof(sockaddr_in)); + } while (ret == -1 && errno == EINTR); + + /* Accept client connection */ + len = sizeof(socklen_t); + int server; + do { + server = accept(sock, (sockaddr*)&addr, (socklen_t*)&len); + } while (server == -1 && errno == EINTR); + GPR_ASSERT(server != -1); + + sv[0] = server; + sv[1] = client; + int flags = fcntl(sv[0], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0); + flags = fcntl(sv[1], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); +} + static ssize_t fill_socket(int fd) { ssize_t write_bytes; ssize_t total_bytes = 0; @@ -176,7 +216,8 @@ static void read_test(size_t num_bytes, size_t slice_size) { a[0].type = GRPC_ARG_INTEGER, a[0].value.integer = static_cast<int>(slice_size); grpc_channel_args args = {GPR_ARRAY_SIZE(a), a}; - ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), &args, "test"); + ep = + grpc_tcp_create(grpc_fd_create(sv[1], "read_test", false), &args, "test"); grpc_endpoint_add_to_pollset(ep, g_pollset); written_bytes = fill_socket_partial(sv[0], num_bytes); @@ -226,7 +267,8 @@ static void large_read_test(size_t slice_size) { a[0].type = GRPC_ARG_INTEGER; a[0].value.integer = static_cast<int>(slice_size); grpc_channel_args args = {GPR_ARRAY_SIZE(a), a}; - ep = grpc_tcp_create(grpc_fd_create(sv[1], "large_read_test"), &args, "test"); + ep = grpc_tcp_create(grpc_fd_create(sv[1], "large_read_test", false), &args, + "test"); grpc_endpoint_add_to_pollset(ep, g_pollset); written_bytes = fill_socket(sv[0]); @@ -287,11 +329,10 @@ static grpc_slice* allocate_blocks(size_t num_bytes, size_t slice_size, static void write_done(void* user_data /* write_socket_state */, grpc_error* error) { + GPR_ASSERT(error == GRPC_ERROR_NONE); struct write_socket_state* state = static_cast<struct write_socket_state*>(user_data); - gpr_log(GPR_INFO, "Write done callback called"); gpr_mu_lock(g_mu); - gpr_log(GPR_INFO, "Signalling write done"); state->write_done = 1; GPR_ASSERT( GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, nullptr))); @@ -338,10 +379,24 @@ void drain_socket_blocking(int fd, size_t num_bytes, size_t read_size) { gpr_free(buf); } +/* Verifier for timestamps callback for write_test */ +void timestamps_verifier(void* arg, grpc_core::Timestamps* ts, + grpc_error* error) { + GPR_ASSERT(error == GRPC_ERROR_NONE); + GPR_ASSERT(arg != nullptr); + GPR_ASSERT(ts->sendmsg_time.clock_type == GPR_CLOCK_REALTIME); + GPR_ASSERT(ts->scheduled_time.clock_type == GPR_CLOCK_REALTIME); + GPR_ASSERT(ts->acked_time.clock_type == GPR_CLOCK_REALTIME); + gpr_atm* done_timestamps = (gpr_atm*)arg; + gpr_atm_rel_store(done_timestamps, static_cast<gpr_atm>(1)); +} + /* Write to a socket using the grpc_tcp API, then drain it directly. Note that if the write does not complete immediately we need to drain the - socket in parallel with the read. */ -static void write_test(size_t num_bytes, size_t slice_size) { + socket in parallel with the read. If collect_timestamps is true, it will + try to get timestamps for the write. */ +static void write_test(size_t num_bytes, size_t slice_size, + bool collect_timestamps) { int sv[2]; grpc_endpoint* ep; struct write_socket_state state; @@ -354,18 +409,27 @@ static void write_test(size_t num_bytes, size_t slice_size) { grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(20)); grpc_core::ExecCtx exec_ctx; + if (collect_timestamps && !grpc_event_engine_can_track_errors()) { + return; + } + gpr_log(GPR_INFO, "Start write test with %" PRIuPTR " bytes, slice size %" PRIuPTR, num_bytes, slice_size); - create_sockets(sv); + if (collect_timestamps) { + create_inet_sockets(sv); + } else { + create_sockets(sv); + } grpc_arg a[1]; a[0].key = const_cast<char*>(GRPC_ARG_TCP_READ_CHUNK_SIZE); a[0].type = GRPC_ARG_INTEGER, a[0].value.integer = static_cast<int>(slice_size); grpc_channel_args args = {GPR_ARRAY_SIZE(a), a}; - ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_test"), &args, "test"); + ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_test", collect_timestamps), + &args, "test"); grpc_endpoint_add_to_pollset(ep, g_pollset); state.ep = ep; @@ -378,18 +442,26 @@ static void write_test(size_t num_bytes, size_t slice_size) { GRPC_CLOSURE_INIT(&write_done_closure, write_done, &state, grpc_schedule_on_exec_ctx); - grpc_endpoint_write(ep, &outgoing, &write_done_closure); + gpr_atm done_timestamps; + gpr_atm_rel_store(&done_timestamps, static_cast<gpr_atm>(0)); + grpc_endpoint_write(ep, &outgoing, &write_done_closure, + grpc_event_engine_can_track_errors() && collect_timestamps + ? (void*)&done_timestamps + : nullptr); drain_socket_blocking(sv[0], num_bytes, num_bytes); + exec_ctx.Flush(); gpr_mu_lock(g_mu); for (;;) { grpc_pollset_worker* worker = nullptr; - if (state.write_done) { + if (state.write_done && + (!(grpc_event_engine_can_track_errors() && collect_timestamps) || + gpr_atm_acq_load(&done_timestamps) == static_cast<gpr_atm>(1))) { break; } GPR_ASSERT(GRPC_LOG_IF_ERROR( "pollset_work", grpc_pollset_work(g_pollset, &worker, deadline))); gpr_mu_unlock(g_mu); - + exec_ctx.Flush(); gpr_mu_lock(g_mu); } gpr_mu_unlock(g_mu); @@ -433,7 +505,8 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) { a[0].type = GRPC_ARG_INTEGER; a[0].value.integer = static_cast<int>(slice_size); grpc_channel_args args = {GPR_ARRAY_SIZE(a), a}; - ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), &args, "test"); + ep = + grpc_tcp_create(grpc_fd_create(sv[1], "read_test", false), &args, "test"); GPR_ASSERT(grpc_tcp_fd(ep) == sv[1] && sv[1] >= 0); grpc_endpoint_add_to_pollset(ep, g_pollset); @@ -493,14 +566,21 @@ void run_tests(void) { large_read_test(8192); large_read_test(1); - write_test(100, 8192); - write_test(100, 1); - write_test(100000, 8192); - write_test(100000, 1); - write_test(100000, 137); + write_test(100, 8192, false); + write_test(100, 1, false); + write_test(100000, 8192, false); + write_test(100000, 1, false); + write_test(100000, 137, false); + + write_test(100, 8192, true); + write_test(100, 1, true); + write_test(100000, 8192, true); + write_test(100000, 1, true); + write_test(100, 137, true); for (i = 1; i < 1000; i = GPR_MAX(i + 1, i * 5 / 4)) { - write_test(40320, i); + write_test(40320, i, false); + write_test(40320, i, true); } release_fd_test(100, 8192); @@ -522,10 +602,10 @@ static grpc_endpoint_test_fixture create_fixture_tcp_socketpair( a[0].type = GRPC_ARG_INTEGER; a[0].value.integer = static_cast<int>(slice_size); grpc_channel_args args = {GPR_ARRAY_SIZE(a), a}; - f.client_ep = - grpc_tcp_create(grpc_fd_create(sv[0], "fixture:client"), &args, "test"); - f.server_ep = - grpc_tcp_create(grpc_fd_create(sv[1], "fixture:server"), &args, "test"); + f.client_ep = grpc_tcp_create(grpc_fd_create(sv[0], "fixture:client", false), + &args, "test"); + f.server_ep = grpc_tcp_create(grpc_fd_create(sv[1], "fixture:server", false), + &args, "test"); grpc_resource_quota_unref_internal(resource_quota); grpc_endpoint_add_to_pollset(f.client_ep, g_pollset); grpc_endpoint_add_to_pollset(f.server_ep, g_pollset); @@ -545,6 +625,7 @@ int main(int argc, char** argv) { grpc_closure destroyed; grpc_test_init(argc, argv); grpc_init(); + grpc_core::grpc_tcp_set_write_timestamps_callback(timestamps_verifier); { grpc_core::ExecCtx exec_ctx; g_pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size())); diff --git a/test/core/iomgr/timer_heap_test.cc b/test/core/iomgr/timer_heap_test.cc index ebe5e32f3a..ebe5e6610d 100644 --- a/test/core/iomgr/timer_heap_test.cc +++ b/test/core/iomgr/timer_heap_test.cc @@ -204,7 +204,7 @@ static void test2(void) { } if (num_inserted) { - gpr_atm* min_deadline = nullptr; + grpc_millis* min_deadline = nullptr; for (size_t i = 0; i < elems_size; i++) { if (elems[i].inserted) { if (min_deadline == nullptr) { diff --git a/test/core/iomgr/timer_list_test.cc b/test/core/iomgr/timer_list_test.cc index b1d919b292..fd65d1abf1 100644 --- a/test/core/iomgr/timer_list_test.cc +++ b/test/core/iomgr/timer_list_test.cc @@ -38,6 +38,8 @@ extern grpc_core::TraceFlag grpc_timer_trace; extern grpc_core::TraceFlag grpc_timer_check_trace; static int cb_called[MAX_CB][2]; +static const int64_t kMillisIn25Days = 2160000000; +static const int64_t kHoursIn25Days = 600; static void cb(void* arg, grpc_error* error) { cb_called[(intptr_t)arg][error == GRPC_ERROR_NONE]++; @@ -151,17 +153,108 @@ void destruction_test(void) { GPR_ASSERT(1 == cb_called[2][0]); } -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - grpc_core::ExecCtx::GlobalInit(); +/* Cleans up a list with pending timers that simulate long-running-services. + This test does the following: + 1) Simulates grpc server start time to 25 days in the past (completed in + `main` using TestOnlyGlobalInit()) + 2) Creates 4 timers - one with a deadline 25 days in the future, one just + 3 milliseconds in future, one way out in the future, and one using the + grpc_timespec_to_millis_round_up function to compute a deadline of 25 + days in the future + 3) Simulates 4 milliseconds of elapsed time by changing `now` (cached at + step 1) to `now+4` + 4) Shuts down the timer list + https://github.com/grpc/grpc/issues/15904 */ +void long_running_service_cleanup_test(void) { + grpc_timer timers[4]; grpc_core::ExecCtx exec_ctx; - grpc_determine_iomgr_platform(); - grpc_iomgr_platform_init(); - gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG); - add_test(); - destruction_test(); - grpc_iomgr_platform_shutdown(); + + gpr_log(GPR_INFO, "long_running_service_cleanup_test"); + + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + GPR_ASSERT(now >= kMillisIn25Days); + grpc_timer_list_init(); + grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_trace); + grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_check_trace); + memset(cb_called, 0, sizeof(cb_called)); + + grpc_timer_init( + &timers[0], now + kMillisIn25Days, + GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)0, grpc_schedule_on_exec_ctx)); + grpc_timer_init( + &timers[1], now + 3, + GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)1, grpc_schedule_on_exec_ctx)); + grpc_timer_init( + &timers[2], GRPC_MILLIS_INF_FUTURE - 1, + GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)2, grpc_schedule_on_exec_ctx)); + + gpr_timespec deadline_spec = grpc_millis_to_timespec( + now + kMillisIn25Days, gpr_clock_type::GPR_CLOCK_MONOTONIC); + + /* grpc_timespec_to_millis_round_up is how users usually compute a millisecond + input value into grpc_timer_init, so we mimic that behavior here */ + grpc_timer_init( + &timers[3], grpc_timespec_to_millis_round_up(deadline_spec), + GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)3, grpc_schedule_on_exec_ctx)); + + grpc_core::ExecCtx::Get()->TestOnlySetNow(now + 4); + GPR_ASSERT(grpc_timer_check(nullptr) == GRPC_TIMERS_FIRED); + grpc_core::ExecCtx::Get()->Flush(); + GPR_ASSERT(0 == cb_called[0][0]); // Timer 0 not called + GPR_ASSERT(0 == cb_called[0][1]); + GPR_ASSERT(0 == cb_called[1][0]); + GPR_ASSERT(1 == cb_called[1][1]); // Timer 1 fired + GPR_ASSERT(0 == cb_called[2][0]); // Timer 2 not called + GPR_ASSERT(0 == cb_called[2][1]); + GPR_ASSERT(0 == cb_called[3][0]); // Timer 3 not called + GPR_ASSERT(0 == cb_called[3][1]); + + grpc_timer_list_shutdown(); + grpc_core::ExecCtx::Get()->Flush(); + /* Timers 0, 2, and 3 were fired with an error during cleanup */ + GPR_ASSERT(1 == cb_called[0][0]); + GPR_ASSERT(0 == cb_called[1][0]); + GPR_ASSERT(1 == cb_called[2][0]); + GPR_ASSERT(1 == cb_called[3][0]); +} + +int main(int argc, char** argv) { + /* Tests with default g_start_time */ + { + grpc_test_init(argc, argv); + grpc_core::ExecCtx::GlobalInit(); + grpc_core::ExecCtx exec_ctx; + grpc_determine_iomgr_platform(); + grpc_iomgr_platform_init(); + gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG); + add_test(); + destruction_test(); + grpc_iomgr_platform_shutdown(); + } grpc_core::ExecCtx::GlobalShutdown(); + + /* Begin long running service tests */ + { + grpc_test_init(argc, argv); + /* Set g_start_time back 25 days. */ + /* We set g_start_time here in case there are any initialization + dependencies that use g_start_time. */ + gpr_timespec new_start = + gpr_time_sub(gpr_now(gpr_clock_type::GPR_CLOCK_MONOTONIC), + gpr_time_from_hours(kHoursIn25Days, + gpr_clock_type::GPR_CLOCK_MONOTONIC)); + grpc_core::ExecCtx::TestOnlyGlobalInit(new_start); + grpc_core::ExecCtx exec_ctx; + grpc_determine_iomgr_platform(); + grpc_iomgr_platform_init(); + gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG); + long_running_service_cleanup_test(); + add_test(); + destruction_test(); + grpc_iomgr_platform_shutdown(); + } + grpc_core::ExecCtx::GlobalShutdown(); + return 0; } |