Update timeout encoding algorithm (#28346)

* update timeout encoding algorithm

* Automated change: Fix sanity tests

* fix

* review feedback

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/28336/head
Craig Tiller 3 years ago committed by GitHub
parent 2a4b7f25e6
commit 1a2b459daa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 64
      CMakeLists.txt
  2. 19
      build_autogenerated.yaml
  3. 36
      src/core/ext/transport/chttp2/transport/hpack_encoder.cc
  4. 6
      src/core/ext/transport/chttp2/transport/hpack_encoder.h
  5. 12
      src/core/lib/transport/metadata_batch.h
  6. 265
      src/core/lib/transport/timeout_encoding.cc
  7. 50
      src/core/lib/transport/timeout_encoding.h
  8. 3
      test/core/transport/BUILD
  9. 67
      test/core/transport/timeout_encoding_test.cc
  10. 48
      tools/run_tests/generated/tests.json

64
CMakeLists.txt generated

@ -753,7 +753,6 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_c thd_test)
add_dependencies(buildtests_c threadpool_test)
add_dependencies(buildtests_c time_averaged_stats_test)
add_dependencies(buildtests_c timeout_encoding_test)
add_dependencies(buildtests_c timer_heap_test)
add_dependencies(buildtests_c timer_list_test)
add_dependencies(buildtests_c transport_security_common_api_test)
@ -992,6 +991,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx time_jump_test)
endif()
add_dependencies(buildtests_cxx time_util_test)
add_dependencies(buildtests_cxx timeout_encoding_test)
add_dependencies(buildtests_cxx timer_test)
add_dependencies(buildtests_cxx tls_certificate_verifier_test)
add_dependencies(buildtests_cxx tls_security_connector_test)
@ -7382,33 +7382,6 @@ target_link_libraries(time_averaged_stats_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(timeout_encoding_test
test/core/transport/timeout_encoding_test.cc
)
target_include_directories(timeout_encoding_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
)
target_link_libraries(timeout_encoding_test
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
@ -15952,6 +15925,41 @@ target_link_libraries(time_util_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(timeout_encoding_test
test/core/transport/timeout_encoding_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(timeout_encoding_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(timeout_encoding_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)

@ -4288,15 +4288,6 @@ targets:
deps:
- grpc_test_util
uses_polling: false
- name: timeout_encoding_test
build: test
language: c
headers: []
src:
- test/core/transport/timeout_encoding_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: timer_heap_test
build: test
language: c
@ -7957,6 +7948,16 @@ targets:
deps:
- grpc_test_util
uses_polling: false
- name: timeout_encoding_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/transport/timeout_encoding_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: timer_test
gtest: true
build: test

@ -725,13 +725,35 @@ void HPackCompressor::Framer::EncodeIndexedKeyWithBinaryValue(
void HPackCompressor::Framer::Encode(GrpcTimeoutMetadata,
grpc_millis deadline) {
char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
grpc_mdelem mdelem;
grpc_http2_encode_timeout(deadline - ExecCtx::Get()->Now(), timeout_str);
mdelem = grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_TIMEOUT,
UnmanagedMemorySlice(timeout_str));
EncodeDynamic(mdelem);
GRPC_MDELEM_UNREF(mdelem);
Timeout timeout = Timeout::FromDuration(deadline - ExecCtx::Get()->Now());
for (auto it = compressor_->previous_timeouts_.begin();
it != compressor_->previous_timeouts_.end(); ++it) {
double ratio = timeout.RatioVersus(it->timeout);
// If the timeout we're sending is shorter than a previous timeout, but
// within 3% of it, we'll consider sending it.
if (ratio > -3 && ratio <= 0 &&
compressor_->table_.ConvertableToDynamicIndex(it->index)) {
EmitIndexed(compressor_->table_.DynamicIndex(it->index));
// Put this timeout to the front of the queue - forces common timeouts to
// be considered earlier.
std::swap(*it, *compressor_->previous_timeouts_.begin());
return;
}
}
// Clean out some expired timeouts.
while (!compressor_->previous_timeouts_.empty() &&
!compressor_->table_.ConvertableToDynamicIndex(
compressor_->previous_timeouts_.back().index)) {
compressor_->previous_timeouts_.pop_back();
}
Slice encoded = timeout.Encode();
uint32_t index = compressor_->table_.AllocateIndex(
GrpcTimeoutMetadata::key().length() + encoded.length() +
hpack_constants::kEntryOverhead);
compressor_->previous_timeouts_.push_back(PreviousTimeout{timeout, index});
EmitLitHdrWithNonBinaryStringKeyIncIdx(
StaticSlice::FromStaticString(GrpcTimeoutMetadata::key()).c_slice(),
encoded.c_slice());
}
void HPackCompressor::Framer::Encode(UserAgentMetadata, const Slice& slice) {

@ -288,6 +288,11 @@ class HPackCompressor {
std::vector<ValueIndex> values_;
};
struct PreviousTimeout {
Timeout timeout;
uint32_t index;
};
// entry tables for keys & elems: these tables track values that have been
// seen and *may* be in the decompressor table
HPackEncoderIndex<KeyElem, kNumFilterValues> elem_index_;
@ -308,6 +313,7 @@ class HPackCompressor {
Slice user_agent_;
SliceIndex path_index_;
SliceIndex authority_index_;
std::vector<PreviousTimeout> previous_timeouts_;
};
} // namespace grpc_core

@ -88,12 +88,12 @@ struct GrpcTimeoutMetadata {
using MementoType = grpc_millis;
static absl::string_view key() { return "grpc-timeout"; }
static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error) {
grpc_millis timeout;
if (GPR_UNLIKELY(!grpc_http2_decode_timeout(value.c_slice(), &timeout))) {
auto timeout = ParseTimeout(value);
if (!timeout.has_value()) {
on_error("invalid value", value);
timeout = GRPC_MILLIS_INF_FUTURE;
return GRPC_MILLIS_INF_FUTURE;
}
return timeout;
return *timeout;
}
static ValueType MementoToValue(MementoType timeout) {
if (timeout == GRPC_MILLIS_INF_FUTURE) {
@ -102,9 +102,7 @@ struct GrpcTimeoutMetadata {
return ExecCtx::Get()->Now() + timeout;
}
static Slice Encode(ValueType x) {
char timeout[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
grpc_http2_encode_timeout(x, timeout);
return Slice::FromCopiedString(timeout);
return Timeout::FromDuration(x - ExecCtx::Get()->Now()).Encode();
}
static MementoType DisplayValue(MementoType x) { return x; }
};

@ -23,83 +23,214 @@
#include <stdio.h>
#include <string.h>
#include <grpc/support/log.h>
#include "src/core/lib/gpr/string.h"
static int64_t round_up(int64_t x, int64_t divisor) {
return (x / divisor + (x % divisor != 0)) * divisor;
namespace grpc_core {
namespace {
int64_t DivideRoundingUp(int64_t dividend, int64_t divisor) {
return (dividend + divisor - 1) / divisor;
}
/* round an integer up to the next value with three significant figures */
static int64_t round_up_to_three_sig_figs(int64_t x) {
if (x < 1000) return x;
if (x < 10000) return round_up(x, 10);
if (x < 100000) return round_up(x, 100);
if (x < 1000000) return round_up(x, 1000);
if (x < 10000000) return round_up(x, 10000);
if (x < 100000000) return round_up(x, 100000);
if (x < 1000000000) return round_up(x, 1000000);
return round_up(x, 10000000);
constexpr int64_t kSecondsPerMinute = 60;
constexpr int64_t kMinutesPerHour = 60;
constexpr int64_t kSecondsPerHour = kSecondsPerMinute * kMinutesPerHour;
constexpr int64_t kMaxHours = 27000;
bool IsAllSpace(const uint8_t* p, const uint8_t* end) {
while (p != end && *p == ' ') p++;
return p == end;
}
/* encode our minimum viable timeout value */
static void enc_tiny(char* buffer) { memcpy(buffer, "1n", 3); }
} // namespace
/* encode our maximum timeout value, about 1157 days */
static void enc_huge(char* buffer) { memcpy(buffer, "99999999S", 10); }
Timeout Timeout::FromDuration(grpc_millis duration) {
return Timeout::FromMillis(duration);
}
static void enc_ext(char* buffer, int64_t value, char ext) {
int n = int64_ttoa(value, buffer);
buffer[n] = ext;
buffer[n + 1] = 0;
double Timeout::RatioVersus(Timeout other) const {
double a = AsDuration();
double b = other.AsDuration();
if (b == 0) {
if (a > 0) return 100;
if (a < 0) return -100;
return 0;
}
return 100 * (a / b - 1);
}
static void enc_seconds(char* buffer, int64_t sec) {
sec = round_up_to_three_sig_figs(sec);
if (sec % 3600 == 0) {
enc_ext(buffer, sec / 3600, 'H');
} else if (sec % 60 == 0) {
enc_ext(buffer, sec / 60, 'M');
} else {
enc_ext(buffer, sec, 'S');
grpc_millis Timeout::AsDuration() const {
grpc_millis value = value_;
switch (unit_) {
case Unit::kNanoseconds:
return 0;
case Unit::kMilliseconds:
return value;
case Unit::kTenMilliseconds:
return value * 10;
case Unit::kHundredMilliseconds:
return value * 100;
case Unit::kSeconds:
return value * 1000;
case Unit::kTenSeconds:
return value * 10000;
case Unit::kHundredSeconds:
return value * 100000;
case Unit::kMinutes:
return value * 1000 * kSecondsPerMinute;
case Unit::kTenMinutes:
return value * 10000 * kSecondsPerMinute;
case Unit::kHundredMinutes:
return value * 100000 * kSecondsPerMinute;
case Unit::kHours:
return value * 1000 * kSecondsPerHour;
}
}
static void enc_millis(char* buffer, int64_t x) {
x = round_up_to_three_sig_figs(x);
if (x < GPR_MS_PER_SEC) {
enc_ext(buffer, x, 'm');
Slice Timeout::Encode() const {
char buf[10];
char* p = buf;
uint16_t n = value_;
int digits;
if (n >= 10000) {
digits = 5;
} else if (n >= 1000) {
digits = 4;
} else if (n >= 100) {
digits = 3;
} else if (n >= 10) {
digits = 2;
} else {
if (x % GPR_MS_PER_SEC == 0) {
enc_seconds(buffer, x / GPR_MS_PER_SEC);
} else {
enc_ext(buffer, x, 'm');
digits = 1;
}
switch (digits) {
case 5:
*p++ = '0' + n / 10000;
n %= 10000;
ABSL_FALLTHROUGH_INTENDED;
case 4:
*p++ = '0' + n / 1000;
n %= 1000;
ABSL_FALLTHROUGH_INTENDED;
case 3:
*p++ = '0' + n / 100;
n %= 100;
ABSL_FALLTHROUGH_INTENDED;
case 2:
*p++ = '0' + n / 10;
n %= 10;
ABSL_FALLTHROUGH_INTENDED;
case 1:
*p++ = '0' + n;
}
switch (unit_) {
case Unit::kNanoseconds:
*p++ = 'n';
break;
case Unit::kHundredMilliseconds:
*p++ = '0';
ABSL_FALLTHROUGH_INTENDED;
case Unit::kTenMilliseconds:
*p++ = '0';
ABSL_FALLTHROUGH_INTENDED;
case Unit::kMilliseconds:
*p++ = 'm';
break;
case Unit::kHundredSeconds:
*p++ = '0';
ABSL_FALLTHROUGH_INTENDED;
case Unit::kTenSeconds:
*p++ = '0';
ABSL_FALLTHROUGH_INTENDED;
case Unit::kSeconds:
*p++ = 'S';
break;
case Unit::kHundredMinutes:
*p++ = '0';
ABSL_FALLTHROUGH_INTENDED;
case Unit::kTenMinutes:
*p++ = '0';
ABSL_FALLTHROUGH_INTENDED;
case Unit::kMinutes:
*p++ = 'M';
break;
case Unit::kHours:
*p++ = 'H';
break;
}
return Slice::FromCopiedBuffer(buf, p - buf);
}
Timeout Timeout::FromMillis(int64_t millis) {
if (millis <= 0) {
return Timeout(1, Unit::kNanoseconds);
} else if (millis < 1000) {
return Timeout(millis, Unit::kMilliseconds);
} else if (millis < 10000) {
int64_t value = DivideRoundingUp(millis, 10);
if (value % 100 != 0) return Timeout(value, Unit::kTenMilliseconds);
} else if (millis < 100000) {
int64_t value = DivideRoundingUp(millis, 100);
if (value % 10 != 0) return Timeout(value, Unit::kHundredMilliseconds);
}
return Timeout::FromSeconds(DivideRoundingUp(millis, 1000));
}
Timeout Timeout::FromSeconds(int64_t seconds) {
GPR_DEBUG_ASSERT(seconds != 0);
if (seconds < 1000) {
if (seconds % kSecondsPerMinute != 0) {
return Timeout(seconds, Unit::kSeconds);
}
} else if (seconds < 10000) {
int64_t value = DivideRoundingUp(seconds, 10);
if ((value * 10) % kSecondsPerMinute != 0) {
return Timeout(value, Unit::kTenSeconds);
}
} else if (seconds < 100000) {
int64_t value = DivideRoundingUp(seconds, 100);
if ((value * 100) % kSecondsPerMinute != 0) {
return Timeout(value, Unit::kHundredSeconds);
}
}
return Timeout::FromMinutes(DivideRoundingUp(seconds, kSecondsPerMinute));
}
void grpc_http2_encode_timeout(grpc_millis timeout, char* buffer) {
const grpc_millis kMaxTimeout = 99999999000;
if (timeout <= 0) {
enc_tiny(buffer);
} else if (timeout < 1000 * GPR_MS_PER_SEC) {
enc_millis(buffer, timeout);
} else if (timeout >= kMaxTimeout) {
enc_huge(buffer);
} else {
enc_seconds(buffer,
timeout / GPR_MS_PER_SEC + (timeout % GPR_MS_PER_SEC != 0));
Timeout Timeout::FromMinutes(int64_t minutes) {
GPR_DEBUG_ASSERT(minutes != 0);
if (minutes < 1000) {
if (minutes % kMinutesPerHour != 0) {
return Timeout(minutes, Unit::kMinutes);
}
} else if (minutes < 10000) {
int64_t value = DivideRoundingUp(minutes, 10);
if ((value * 10) % kMinutesPerHour != 0) {
return Timeout(value, Unit::kTenMinutes);
}
} else if (minutes < 100000) {
int64_t value = DivideRoundingUp(minutes, 100);
if ((value * 100) % kMinutesPerHour != 0) {
return Timeout(value, Unit::kHundredMinutes);
}
}
return Timeout::FromHours(DivideRoundingUp(minutes, kMinutesPerHour));
}
static int is_all_whitespace(const char* p, const char* end) {
while (p != end && *p == ' ') p++;
return p == end;
Timeout Timeout::FromHours(int64_t hours) {
GPR_DEBUG_ASSERT(hours != 0);
if (hours < kMaxHours) {
return Timeout(hours, Unit::kHours);
}
return Timeout(kMaxHours, Unit::kHours);
}
int grpc_http2_decode_timeout(const grpc_slice& text, grpc_millis* timeout) {
absl::optional<grpc_millis> ParseTimeout(const Slice& text) {
grpc_millis x = 0;
const uint8_t* p = GRPC_SLICE_START_PTR(text);
const uint8_t* end = GRPC_SLICE_END_PTR(text);
const uint8_t* p = text.begin();
const uint8_t* end = text.end();
int have_digit = 0;
/* skip whitespace */
for (; p != end && *p == ' '; p++) {
@ -111,41 +242,43 @@ int grpc_http2_decode_timeout(const grpc_slice& text, grpc_millis* timeout) {
/* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */
if (x >= (100 * 1000 * 1000)) {
if (x != (100 * 1000 * 1000) || digit != 0) {
*timeout = GRPC_MILLIS_INF_FUTURE;
return 1;
return GRPC_MILLIS_INF_FUTURE;
}
}
x = x * 10 + digit;
}
if (!have_digit) return 0;
if (!have_digit) return absl::nullopt;
/* skip whitespace */
for (; p != end && *p == ' '; p++) {
}
if (p == end) return 0;
if (p == end) return absl::nullopt;
/* decode unit specifier */
int64_t timeout;
switch (*p) {
case 'n':
*timeout = x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0);
timeout = x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0);
break;
case 'u':
*timeout = x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0);
timeout = x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0);
break;
case 'm':
*timeout = x;
timeout = x;
break;
case 'S':
*timeout = x * GPR_MS_PER_SEC;
timeout = x * GPR_MS_PER_SEC;
break;
case 'M':
*timeout = x * 60 * GPR_MS_PER_SEC;
timeout = x * 60 * GPR_MS_PER_SEC;
break;
case 'H':
*timeout = x * 60 * 60 * GPR_MS_PER_SEC;
timeout = x * 60 * 60 * GPR_MS_PER_SEC;
break;
default:
return 0;
return absl::nullopt;
}
p++;
return is_all_whitespace(reinterpret_cast<const char*>(p),
reinterpret_cast<const char*>(end));
if (!IsAllSpace(p, end)) return absl::nullopt;
return timeout;
}
} // namespace grpc_core

@ -21,18 +21,48 @@
#include <grpc/support/port_platform.h>
#include <grpc/slice.h>
#include <grpc/support/time.h>
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/slice/slice.h"
namespace grpc_core {
class Timeout {
public:
static Timeout FromDuration(grpc_millis duration);
// Computes: 100 * ((this - other) / other)
double RatioVersus(Timeout other) const;
Slice Encode() const;
grpc_millis AsDuration() const;
private:
enum class Unit : uint8_t {
kNanoseconds,
kMilliseconds,
kTenMilliseconds,
kHundredMilliseconds,
kSeconds,
kTenSeconds,
kHundredSeconds,
kMinutes,
kTenMinutes,
kHundredMinutes,
kHours,
};
Timeout(uint16_t value, Unit unit) : value_(value), unit_(unit) {}
static Timeout FromMillis(int64_t millis);
static Timeout FromSeconds(int64_t seconds);
static Timeout FromMinutes(int64_t minutes);
static Timeout FromHours(int64_t hours);
uint16_t value_;
Unit unit_;
};
#define GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE 10
absl::optional<grpc_millis> ParseTimeout(const Slice& text);
/* Encode/decode timeouts to the GRPC over HTTP/2 format;
encoding may round up arbitrarily. If the timeout is larger than about 1157
days, it will be capped and "99999999S" will be sent on the wire. */
void grpc_http2_encode_timeout(grpc_millis timeout, char* buffer);
int grpc_http2_decode_timeout(const grpc_slice& text, grpc_millis* timeout);
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */

@ -159,6 +159,9 @@ grpc_cc_test(
grpc_cc_test(
name = "timeout_encoding_test",
srcs = ["timeout_encoding_test.cc"],
external_deps = [
"gtest",
],
language = "C++",
uses_polling = False,
deps = [

@ -23,6 +23,8 @@
#include <string>
#include <gtest/gtest.h>
#include "absl/strings/str_format.h"
#include <grpc/support/alloc.h>
@ -33,17 +35,16 @@
#include "src/core/lib/gpr/useful.h"
#include "test/core/util/test_config.h"
#define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
namespace grpc_core {
namespace {
static void assert_encodes_as(grpc_millis ts, const char* s) {
char buffer[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
grpc_http2_encode_timeout(ts, buffer);
gpr_log(GPR_INFO, "check '%s' == '%s'", buffer, s);
GPR_ASSERT(0 == strcmp(buffer, s));
void assert_encodes_as(grpc_millis ts, const char* s) {
EXPECT_EQ(absl::string_view(s),
Timeout::FromDuration(ts).Encode().as_string_view())
<< " ts=" << ts;
}
void test_encoding(void) {
LOG_TEST("test_encoding");
TEST(TimeoutTest, Encoding) {
assert_encodes_as(-1, "1n");
assert_encodes_as(-10, "1n");
assert_encodes_as(1, "1m");
@ -67,20 +68,11 @@ void test_encoding(void) {
assert_encodes_as(10 * 60 * 60 * GPR_MS_PER_SEC, "10H");
assert_encodes_as(60 * 60 * GPR_MS_PER_SEC - 100, "1H");
assert_encodes_as(100 * 60 * 60 * GPR_MS_PER_SEC, "100H");
assert_encodes_as(100000000000, "99999999S");
assert_encodes_as(100000000000, "27000H");
}
static void assert_decodes_as(const char* buffer, grpc_millis expected) {
grpc_millis got;
uint32_t hash = gpr_murmur_hash3(buffer, strlen(buffer), 0);
gpr_log(GPR_INFO, "check decoding '%s' (hash=0x%x)", buffer, hash);
GPR_ASSERT(1 == grpc_http2_decode_timeout(
grpc_slice_from_static_string(buffer), &got));
if (got != expected) {
gpr_log(GPR_ERROR, "got:'%" PRId64 "' != expected:'%" PRId64 "'", got,
expected);
abort();
}
void assert_decodes_as(const char* buffer, grpc_millis expected) {
EXPECT_EQ(expected, ParseTimeout(Slice::FromCopiedString(buffer)));
}
void decode_suite(char ext, grpc_millis (*answer)(int64_t x)) {
@ -102,27 +94,26 @@ void decode_suite(char ext, grpc_millis (*answer)(int64_t x)) {
}
}
static grpc_millis millis_from_nanos(int64_t x) {
grpc_millis millis_from_nanos(int64_t x) {
return static_cast<grpc_millis>(x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0));
}
static grpc_millis millis_from_micros(int64_t x) {
grpc_millis millis_from_micros(int64_t x) {
return static_cast<grpc_millis>(x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0));
}
static grpc_millis millis_from_millis(int64_t x) {
grpc_millis millis_from_millis(int64_t x) {
return static_cast<grpc_millis>(x);
}
static grpc_millis millis_from_seconds(int64_t x) {
grpc_millis millis_from_seconds(int64_t x) {
return static_cast<grpc_millis>(x * GPR_MS_PER_SEC);
}
static grpc_millis millis_from_minutes(int64_t x) {
grpc_millis millis_from_minutes(int64_t x) {
return static_cast<grpc_millis>(x * 60 * GPR_MS_PER_SEC);
}
static grpc_millis millis_from_hours(int64_t x) {
grpc_millis millis_from_hours(int64_t x) {
return static_cast<grpc_millis>(x * 3600 * GPR_MS_PER_SEC);
}
void test_decoding(void) {
LOG_TEST("test_decoding");
TEST(TimeoutTest, DecodingSucceeds) {
decode_suite('n', millis_from_nanos);
decode_suite('u', millis_from_micros);
decode_suite('m', millis_from_millis);
@ -136,14 +127,12 @@ void test_decoding(void) {
assert_decodes_as("9999999999S", GRPC_MILLIS_INF_FUTURE);
}
static void assert_decoding_fails(const char* s) {
grpc_millis x;
GPR_ASSERT(0 ==
grpc_http2_decode_timeout(grpc_slice_from_static_string(s), &x));
void assert_decoding_fails(const char* s) {
EXPECT_EQ(absl::nullopt, ParseTimeout(Slice::FromCopiedString(s)))
<< " s=" << s;
}
void test_decoding_fails(void) {
LOG_TEST("test_decoding_fails");
TEST(TimeoutTest, DecodingFails) {
assert_decoding_fails("");
assert_decoding_fails(" ");
assert_decoding_fails("x");
@ -155,10 +144,10 @@ void test_decoding_fails(void) {
assert_decoding_fails("-1u");
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(argc, argv);
test_encoding();
test_decoding();
test_decoding_fails();
return 0;
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -2743,30 +2743,6 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": false,
"language": "c",
"name": "timeout_encoding_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
@ -7051,6 +7027,30 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "timeout_encoding_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save