Merge pull request #17842 from rmstar/timechange

Added test for wall clock time change on the client
pull/17949/head
rmstar 6 years ago committed by GitHub
commit 0d9e6d1e36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      CMakeLists.txt
  2. 48
      Makefile
  3. 16
      build.yaml
  4. 8
      src/core/lib/gpr/time.cc
  5. 10
      src/core/lib/gpr/time_posix.cc
  6. 24
      test/cpp/end2end/BUILD
  7. 422
      test/cpp/end2end/time_change_test.cc
  8. 18
      tools/run_tests/generated/sources_and_headers.json
  9. 22
      tools/run_tests/generated/tests.json

@ -702,6 +702,9 @@ endif()
add_dependencies(buildtests_cxx stress_test) add_dependencies(buildtests_cxx stress_test)
add_dependencies(buildtests_cxx thread_manager_test) add_dependencies(buildtests_cxx thread_manager_test)
add_dependencies(buildtests_cxx thread_stress_test) add_dependencies(buildtests_cxx thread_stress_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx time_change_test)
endif()
add_dependencies(buildtests_cxx transport_pid_controller_test) add_dependencies(buildtests_cxx transport_pid_controller_test)
add_dependencies(buildtests_cxx transport_security_common_api_test) add_dependencies(buildtests_cxx transport_security_common_api_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -16012,6 +16015,48 @@ target_link_libraries(thread_stress_test
) )
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_executable(time_change_test
test/cpp/end2end/time_change_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(time_change_test
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
PRIVATE third_party/googletest/googletest/include
PRIVATE third_party/googletest/googletest
PRIVATE third_party/googletest/googlemock/include
PRIVATE third_party/googletest/googlemock
PRIVATE ${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(time_change_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc++_test_util
grpc_test_util
grpc++
grpc
gpr
${_gRPC_GFLAGS_LIBRARIES}
)
endif()
endif (gRPC_BUILD_TESTS) endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS)

@ -1249,6 +1249,7 @@ streaming_throughput_test: $(BINDIR)/$(CONFIG)/streaming_throughput_test
stress_test: $(BINDIR)/$(CONFIG)/stress_test stress_test: $(BINDIR)/$(CONFIG)/stress_test
thread_manager_test: $(BINDIR)/$(CONFIG)/thread_manager_test thread_manager_test: $(BINDIR)/$(CONFIG)/thread_manager_test
thread_stress_test: $(BINDIR)/$(CONFIG)/thread_stress_test thread_stress_test: $(BINDIR)/$(CONFIG)/thread_stress_test
time_change_test: $(BINDIR)/$(CONFIG)/time_change_test
transport_pid_controller_test: $(BINDIR)/$(CONFIG)/transport_pid_controller_test transport_pid_controller_test: $(BINDIR)/$(CONFIG)/transport_pid_controller_test
transport_security_common_api_test: $(BINDIR)/$(CONFIG)/transport_security_common_api_test transport_security_common_api_test: $(BINDIR)/$(CONFIG)/transport_security_common_api_test
writes_per_rpc_test: $(BINDIR)/$(CONFIG)/writes_per_rpc_test writes_per_rpc_test: $(BINDIR)/$(CONFIG)/writes_per_rpc_test
@ -1758,6 +1759,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/stress_test \ $(BINDIR)/$(CONFIG)/stress_test \
$(BINDIR)/$(CONFIG)/thread_manager_test \ $(BINDIR)/$(CONFIG)/thread_manager_test \
$(BINDIR)/$(CONFIG)/thread_stress_test \ $(BINDIR)/$(CONFIG)/thread_stress_test \
$(BINDIR)/$(CONFIG)/time_change_test \
$(BINDIR)/$(CONFIG)/transport_pid_controller_test \ $(BINDIR)/$(CONFIG)/transport_pid_controller_test \
$(BINDIR)/$(CONFIG)/transport_security_common_api_test \ $(BINDIR)/$(CONFIG)/transport_security_common_api_test \
$(BINDIR)/$(CONFIG)/writes_per_rpc_test \ $(BINDIR)/$(CONFIG)/writes_per_rpc_test \
@ -1945,6 +1947,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/stress_test \ $(BINDIR)/$(CONFIG)/stress_test \
$(BINDIR)/$(CONFIG)/thread_manager_test \ $(BINDIR)/$(CONFIG)/thread_manager_test \
$(BINDIR)/$(CONFIG)/thread_stress_test \ $(BINDIR)/$(CONFIG)/thread_stress_test \
$(BINDIR)/$(CONFIG)/time_change_test \
$(BINDIR)/$(CONFIG)/transport_pid_controller_test \ $(BINDIR)/$(CONFIG)/transport_pid_controller_test \
$(BINDIR)/$(CONFIG)/transport_security_common_api_test \ $(BINDIR)/$(CONFIG)/transport_security_common_api_test \
$(BINDIR)/$(CONFIG)/writes_per_rpc_test \ $(BINDIR)/$(CONFIG)/writes_per_rpc_test \
@ -2462,6 +2465,8 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/thread_manager_test || ( echo test thread_manager_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/thread_manager_test || ( echo test thread_manager_test failed ; exit 1 )
$(E) "[RUN] Testing thread_stress_test" $(E) "[RUN] Testing thread_stress_test"
$(Q) $(BINDIR)/$(CONFIG)/thread_stress_test || ( echo test thread_stress_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/thread_stress_test || ( echo test thread_stress_test failed ; exit 1 )
$(E) "[RUN] Testing time_change_test"
$(Q) $(BINDIR)/$(CONFIG)/time_change_test || ( echo test time_change_test failed ; exit 1 )
$(E) "[RUN] Testing transport_pid_controller_test" $(E) "[RUN] Testing transport_pid_controller_test"
$(Q) $(BINDIR)/$(CONFIG)/transport_pid_controller_test || ( echo test transport_pid_controller_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/transport_pid_controller_test || ( echo test transport_pid_controller_test failed ; exit 1 )
$(E) "[RUN] Testing transport_security_common_api_test" $(E) "[RUN] Testing transport_security_common_api_test"
@ -21061,6 +21066,49 @@ endif
endif endif
TIME_CHANGE_TEST_SRC = \
test/cpp/end2end/time_change_test.cc \
TIME_CHANGE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TIME_CHANGE_TEST_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/time_change_test: openssl_dep_error
else
ifeq ($(NO_PROTOBUF),true)
# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
$(BINDIR)/$(CONFIG)/time_change_test: protobuf_dep_error
else
$(BINDIR)/$(CONFIG)/time_change_test: $(PROTOBUF_DEP) $(TIME_CHANGE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LDXX) $(LDFLAGS) $(TIME_CHANGE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/time_change_test
endif
endif
$(OBJDIR)/$(CONFIG)/test/cpp/end2end/time_change_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
deps_time_change_test: $(TIME_CHANGE_TEST_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(TIME_CHANGE_TEST_OBJS:.o=.dep)
endif
endif
TRANSPORT_PID_CONTROLLER_TEST_SRC = \ TRANSPORT_PID_CONTROLLER_TEST_SRC = \
test/core/transport/pid_controller_test.cc \ test/core/transport/pid_controller_test.cc \

@ -5573,6 +5573,22 @@ targets:
- grpc++_unsecure - grpc++_unsecure
- grpc_unsecure - grpc_unsecure
- gpr - gpr
- name: time_change_test
gtest: true
build: test
language: c++
src:
- test/cpp/end2end/time_change_test.cc
deps:
- grpc++_test_util
- grpc_test_util
- grpc++
- grpc
- gpr
platforms:
- mac
- linux
- posix
- name: transport_pid_controller_test - name: transport_pid_controller_test
build: test build: test
language: c++ language: c++

@ -135,6 +135,10 @@ gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) {
gpr_timespec sum; gpr_timespec sum;
int64_t inc = 0; int64_t inc = 0;
GPR_ASSERT(b.clock_type == GPR_TIMESPAN); GPR_ASSERT(b.clock_type == GPR_TIMESPAN);
// tv_nsec in a timespan is always +ve. -ve timespan is represented as (-ve
// tv_sec, +ve tv_nsec). For example, timespan = -2.5 seconds is represented
// as {-3, 5e8, GPR_TIMESPAN}
GPR_ASSERT(b.tv_nsec >= 0);
sum.clock_type = a.clock_type; sum.clock_type = a.clock_type;
sum.tv_nsec = a.tv_nsec + b.tv_nsec; sum.tv_nsec = a.tv_nsec + b.tv_nsec;
if (sum.tv_nsec >= GPR_NS_PER_SEC) { if (sum.tv_nsec >= GPR_NS_PER_SEC) {
@ -165,6 +169,10 @@ gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) {
int64_t dec = 0; int64_t dec = 0;
if (b.clock_type == GPR_TIMESPAN) { if (b.clock_type == GPR_TIMESPAN) {
diff.clock_type = a.clock_type; diff.clock_type = a.clock_type;
// tv_nsec in a timespan is always +ve. -ve timespan is represented as (-ve
// tv_sec, +ve tv_nsec). For example, timespan = -2.5 seconds is represented
// as {-3, 5e8, GPR_TIMESPAN}
GPR_ASSERT(b.tv_nsec >= 0);
} else { } else {
GPR_ASSERT(a.clock_type == b.clock_type); GPR_ASSERT(a.clock_type == b.clock_type);
diff.clock_type = GPR_TIMESPAN; diff.clock_type = GPR_TIMESPAN;

@ -133,12 +133,18 @@ gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl;
#ifdef GPR_LOW_LEVEL_COUNTERS #ifdef GPR_LOW_LEVEL_COUNTERS
gpr_atm gpr_now_call_count; gpr_atm gpr_now_call_count;
#endif #endif
gpr_timespec gpr_now(gpr_clock_type clock_type) { gpr_timespec gpr_now(gpr_clock_type clock_type) {
#ifdef GPR_LOW_LEVEL_COUNTERS #ifdef GPR_LOW_LEVEL_COUNTERS
__atomic_fetch_add(&gpr_now_call_count, 1, __ATOMIC_RELAXED); __atomic_fetch_add(&gpr_now_call_count, 1, __ATOMIC_RELAXED);
#endif #endif
return gpr_now_impl(clock_type); // validate clock type
GPR_ASSERT(clock_type == GPR_CLOCK_MONOTONIC ||
clock_type == GPR_CLOCK_REALTIME ||
clock_type == GPR_CLOCK_PRECISE);
gpr_timespec ts = gpr_now_impl(clock_type);
// tv_nsecs must be in the range [0, 1e9).
GPR_ASSERT(ts.tv_nsec >= 0 && ts.tv_nsec < 1e9);
return ts;
} }
void gpr_sleep_until(gpr_timespec until) { void gpr_sleep_until(gpr_timespec until) {

@ -80,6 +80,27 @@ grpc_cc_test(
], ],
) )
grpc_cc_test(
name = "time_change_test",
srcs = ["time_change_test.cc"],
data = [
":client_crash_test_server",
],
external_deps = [
"gtest",
],
deps = [
":test_service_impl",
"//:gpr",
"//:grpc",
"//:grpc++",
"//src/proto/grpc/testing:echo_messages_proto",
"//src/proto/grpc/testing:echo_proto",
"//test/core/util:grpc_test_util",
"//test/cpp/util:test_util",
],
)
grpc_cc_test( grpc_cc_test(
name = "client_crash_test", name = "client_crash_test",
srcs = ["client_crash_test.cc"], srcs = ["client_crash_test.cc"],
@ -110,6 +131,7 @@ grpc_cc_binary(
"gtest", "gtest",
], ],
deps = [ deps = [
":test_service_impl",
"//:gpr", "//:gpr",
"//:grpc", "//:grpc",
"//:grpc++", "//:grpc++",
@ -219,10 +241,10 @@ grpc_cc_test(
grpc_cc_test( grpc_cc_test(
name = "end2end_test", name = "end2end_test",
size = "large", # with poll-cv this takes long, see #17493
deps = [ deps = [
":end2end_test_lib", ":end2end_test_lib",
], ],
size = "large", # with poll-cv this takes long, see #17493
) )
grpc_cc_test( grpc_cc_test(

@ -0,0 +1,422 @@
/*
*
* Copyright 2019 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 <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>
#include "src/core/lib/iomgr/timer.h"
#include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
#include "test/cpp/end2end/test_service_impl.h"
#include "test/cpp/util/subprocess.h"
#include <gtest/gtest.h>
#include <pthread.h>
#include <sys/time.h>
#include <thread>
using grpc::testing::EchoRequest;
using grpc::testing::EchoResponse;
static std::string g_root;
static gpr_mu g_mu;
extern gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type);
gpr_timespec (*gpr_now_impl_orig)(gpr_clock_type clock_type) = gpr_now_impl;
static int g_time_shift_sec = 0;
static int g_time_shift_nsec = 0;
static gpr_timespec now_impl(gpr_clock_type clock) {
auto ts = gpr_now_impl_orig(clock);
// We only manipulate the realtime clock to simulate changes in wall-clock
// time
if (clock != GPR_CLOCK_REALTIME) {
return ts;
}
GPR_ASSERT(ts.tv_nsec >= 0);
GPR_ASSERT(ts.tv_nsec < GPR_NS_PER_SEC);
gpr_mu_lock(&g_mu);
ts.tv_sec += g_time_shift_sec;
ts.tv_nsec += g_time_shift_nsec;
gpr_mu_unlock(&g_mu);
if (ts.tv_nsec >= GPR_NS_PER_SEC) {
ts.tv_nsec -= GPR_NS_PER_SEC;
++ts.tv_sec;
} else if (ts.tv_nsec < 0) {
--ts.tv_sec;
ts.tv_nsec = GPR_NS_PER_SEC + ts.tv_nsec;
}
return ts;
}
// offset the value returned by gpr_now(GPR_CLOCK_REALTIME) by msecs
// milliseconds
static void set_now_offset(int msecs) {
g_time_shift_sec = msecs / 1000;
g_time_shift_nsec = (msecs % 1000) * 1e6;
}
// restore the original implementation of gpr_now()
static void reset_now_offset() {
g_time_shift_sec = 0;
g_time_shift_nsec = 0;
}
namespace grpc {
namespace testing {
namespace {
// gpr_now() is called with invalid clock_type
TEST(TimespecTest, GprNowInvalidClockType) {
// initialize to some junk value
gpr_clock_type invalid_clock_type = (gpr_clock_type)32641;
EXPECT_DEATH(gpr_now(invalid_clock_type), ".*");
}
// Add timespan with negative nanoseconds
TEST(TimespecTest, GprTimeAddNegativeNs) {
gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
gpr_timespec bad_ts = {1, -1000, GPR_TIMESPAN};
EXPECT_DEATH(gpr_time_add(now, bad_ts), ".*");
}
// Subtract timespan with negative nanoseconds
TEST(TimespecTest, GprTimeSubNegativeNs) {
// Nanoseconds must always be positive. Negative timestamps are represented by
// (negative seconds, positive nanoseconds)
gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
gpr_timespec bad_ts = {1, -1000, GPR_TIMESPAN};
EXPECT_DEATH(gpr_time_sub(now, bad_ts), ".*");
}
// Add negative milliseconds to gpr_timespec
TEST(TimespecTest, GrpcNegativeMillisToTimespec) {
// -1500 milliseconds converts to timespec (-2 secs, 5 * 10^8 nsec)
gpr_timespec ts = grpc_millis_to_timespec(-1500, GPR_CLOCK_MONOTONIC);
GPR_ASSERT(ts.tv_sec = -2);
GPR_ASSERT(ts.tv_nsec = 5e8);
GPR_ASSERT(ts.clock_type == GPR_CLOCK_MONOTONIC);
}
class TimeChangeTest : public ::testing::Test {
protected:
TimeChangeTest() {}
void SetUp() {
auto port = grpc_pick_unused_port_or_die();
std::ostringstream addr_stream;
addr_stream << "localhost:" << port;
auto addr = addr_stream.str();
server_.reset(new SubProcess({
g_root + "/client_crash_test_server",
"--address=" + addr,
}));
GPR_ASSERT(server_);
channel_ = CreateChannel(addr, InsecureChannelCredentials());
GPR_ASSERT(channel_);
stub_ = grpc::testing::EchoTestService::NewStub(channel_);
}
void TearDown() {
server_.reset();
reset_now_offset();
}
std::unique_ptr<grpc::testing::EchoTestService::Stub> CreateStub() {
return grpc::testing::EchoTestService::NewStub(channel_);
}
std::shared_ptr<Channel> GetChannel() { return channel_; }
// time jump offsets in milliseconds
const int TIME_OFFSET1 = 20123;
const int TIME_OFFSET2 = 5678;
private:
std::unique_ptr<SubProcess> server_;
std::shared_ptr<Channel> channel_;
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
};
// Wall-clock time jumps forward on client before bidi stream is created
TEST_F(TimeChangeTest, TimeJumpForwardBeforeStreamCreated) {
EchoRequest request;
EchoResponse response;
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(5000));
context.AddMetadata(kServerResponseStreamsToSend, "1");
auto channel = GetChannel();
GPR_ASSERT(channel);
EXPECT_TRUE(
channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(5000)));
auto stub = CreateStub();
// time jumps forward by TIME_OFFSET1 milliseconds
set_now_offset(TIME_OFFSET1);
auto stream = stub->BidiStream(&context);
request.set_message("Hello");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->WritesDone());
EXPECT_TRUE(stream->Read(&response));
auto status = stream->Finish();
EXPECT_TRUE(status.ok());
}
// Wall-clock time jumps back on client before bidi stream is created
TEST_F(TimeChangeTest, TimeJumpBackBeforeStreamCreated) {
EchoRequest request;
EchoResponse response;
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(5000));
context.AddMetadata(kServerResponseStreamsToSend, "1");
auto channel = GetChannel();
GPR_ASSERT(channel);
EXPECT_TRUE(
channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(5000)));
auto stub = CreateStub();
// time jumps back by TIME_OFFSET1 milliseconds
set_now_offset(-TIME_OFFSET1);
auto stream = stub->BidiStream(&context);
request.set_message("Hello");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->WritesDone());
EXPECT_TRUE(stream->Read(&response));
EXPECT_EQ(request.message(), response.message());
auto status = stream->Finish();
EXPECT_TRUE(status.ok());
}
// Wall-clock time jumps forward on client while call is in progress
TEST_F(TimeChangeTest, TimeJumpForwardAfterStreamCreated) {
EchoRequest request;
EchoResponse response;
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(5000));
context.AddMetadata(kServerResponseStreamsToSend, "2");
auto channel = GetChannel();
GPR_ASSERT(channel);
EXPECT_TRUE(
channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(5000)));
auto stub = CreateStub();
auto stream = stub->BidiStream(&context);
request.set_message("Hello");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->Read(&response));
// time jumps forward by TIME_OFFSET1 milliseconds.
set_now_offset(TIME_OFFSET1);
request.set_message("World");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->WritesDone());
EXPECT_TRUE(stream->Read(&response));
auto status = stream->Finish();
EXPECT_TRUE(status.ok());
}
// Wall-clock time jumps back on client while call is in progress
TEST_F(TimeChangeTest, TimeJumpBackAfterStreamCreated) {
EchoRequest request;
EchoResponse response;
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(5000));
context.AddMetadata(kServerResponseStreamsToSend, "2");
auto channel = GetChannel();
GPR_ASSERT(channel);
EXPECT_TRUE(
channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(5000)));
auto stub = CreateStub();
auto stream = stub->BidiStream(&context);
request.set_message("Hello");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->Read(&response));
// time jumps back TIME_OFFSET1 milliseconds.
set_now_offset(-TIME_OFFSET1);
request.set_message("World");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->WritesDone());
EXPECT_TRUE(stream->Read(&response));
auto status = stream->Finish();
EXPECT_TRUE(status.ok());
}
// Wall-clock time jumps forward on client before connection to server is up
TEST_F(TimeChangeTest, TimeJumpForwardBeforeServerConnect) {
EchoRequest request;
EchoResponse response;
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(5000));
context.AddMetadata(kServerResponseStreamsToSend, "2");
auto channel = GetChannel();
GPR_ASSERT(channel);
// time jumps forward by TIME_OFFSET2 milliseconds
set_now_offset(TIME_OFFSET2);
auto ret =
channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(5000));
// We use monotonic clock for pthread_cond_timedwait() deadline on linux, and
// realtime clock on other platforms - see gpr_cv_wait() in sync_posix.cc.
// So changes in system clock affect deadlines on non-linux platforms
#ifdef GPR_LINUX
EXPECT_TRUE(ret);
auto stub = CreateStub();
auto stream = stub->BidiStream(&context);
request.set_message("Hello");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->Read(&response));
request.set_message("World");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->WritesDone());
EXPECT_TRUE(stream->Read(&response));
auto status = stream->Finish();
EXPECT_TRUE(status.ok());
#else
EXPECT_FALSE(ret);
#endif
}
// Wall-clock time jumps back on client before connection to server is up
TEST_F(TimeChangeTest, TimeJumpBackBeforeServerConnect) {
EchoRequest request;
EchoResponse response;
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(5000));
context.AddMetadata(kServerResponseStreamsToSend, "2");
auto channel = GetChannel();
GPR_ASSERT(channel);
// time jumps back by TIME_OFFSET2 milliseconds
set_now_offset(-TIME_OFFSET2);
EXPECT_TRUE(
channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(5000)));
auto stub = CreateStub();
auto stream = stub->BidiStream(&context);
request.set_message("Hello");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->Read(&response));
request.set_message("World");
EXPECT_TRUE(stream->Write(request));
EXPECT_TRUE(stream->WritesDone());
EXPECT_TRUE(stream->Read(&response));
auto status = stream->Finish();
EXPECT_TRUE(status.ok());
}
// Wall-clock time jumps forward and backwards during call
TEST_F(TimeChangeTest, TimeJumpForwardAndBackDuringCall) {
EchoRequest request;
EchoResponse response;
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(5000));
context.AddMetadata(kServerResponseStreamsToSend, "2");
auto channel = GetChannel();
GPR_ASSERT(channel);
EXPECT_TRUE(
channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(5000)));
auto stub = CreateStub();
auto stream = stub->BidiStream(&context);
request.set_message("Hello");
EXPECT_TRUE(stream->Write(request));
// time jumps back by TIME_OFFSET2 milliseconds
set_now_offset(-TIME_OFFSET2);
EXPECT_TRUE(stream->Read(&response));
request.set_message("World");
// time jumps forward by TIME_OFFSET milliseconds
set_now_offset(TIME_OFFSET1);
EXPECT_TRUE(stream->Write(request));
// time jumps back by TIME_OFFSET2 milliseconds
set_now_offset(-TIME_OFFSET2);
EXPECT_TRUE(stream->WritesDone());
// time jumps back by TIME_OFFSET2 milliseconds
set_now_offset(-TIME_OFFSET2);
EXPECT_TRUE(stream->Read(&response));
// time jumps back by TIME_OFFSET2 milliseconds
set_now_offset(-TIME_OFFSET2);
auto status = stream->Finish();
EXPECT_TRUE(status.ok());
}
} // namespace
} // namespace testing
} // namespace grpc
int main(int argc, char** argv) {
std::string me = argv[0];
// get index of last slash in path to test binary
auto lslash = me.rfind('/');
// set g_root = path to directory containing test binary
if (lslash != std::string::npos) {
g_root = me.substr(0, lslash);
} else {
g_root = ".";
}
gpr_mu_init(&g_mu);
gpr_now_impl = now_impl;
grpc::testing::TestEnvironment env(argc, argv);
::testing::InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();
return ret;
}

@ -4910,6 +4910,24 @@
"third_party": false, "third_party": false,
"type": "target" "type": "target"
}, },
{
"deps": [
"gpr",
"grpc",
"grpc++",
"grpc++_test_util",
"grpc_test_util"
],
"headers": [],
"is_filegroup": false,
"language": "c++",
"name": "time_change_test",
"src": [
"test/cpp/end2end/time_change_test.cc"
],
"third_party": false,
"type": "target"
},
{ {
"deps": [ "deps": [
"gpr", "gpr",

@ -5572,6 +5572,28 @@
], ],
"uses_polling": true "uses_polling": true
}, },
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "time_change_test",
"platforms": [
"linux",
"mac",
"posix"
],
"uses_polling": true
},
{ {
"args": [], "args": [],
"benchmark": false, "benchmark": false,

Loading…
Cancel
Save