The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#) https://grpc.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
11 KiB

//
//
// 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.
//
//
10 years ago
#include "test/core/end2end/cq_verifier.h"
#include <inttypes.h>
10 years ago
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <initializer_list>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "gtest/gtest.h"
10 years ago
#include <grpc/byte_buffer.h>
#include <grpc/compression.h>
#include <grpc/grpc.h>
#include <grpc/slice.h>
#include <grpc/slice_buffer.h>
10 years ago
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/lib/compression/message_compress.h"
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/match.h"
9 years ago
#include "src/core/lib/surface/event_string.h"
#include "test/core/util/test_config.h"
10 years ago
// a set of metadata we expect to find on an event
10 years ago
typedef struct metadata {
size_t count;
size_t cap;
char** keys;
char** values;
10 years ago
} metadata;
static int has_metadata(const grpc_metadata* md, size_t count, const char* key,
const char* value) {
10 years ago
size_t i;
for (i = 0; i < count; i++) {
if (0 == grpc_slice_str_cmp(md[i].key, key) &&
0 == grpc_slice_str_cmp(md[i].value, value)) {
10 years ago
return 1;
}
}
return 0;
}
int contains_metadata(grpc_metadata_array* array, const char* key,
const char* value) {
return has_metadata(array->metadata, array->count, key, value);
}
static int has_metadata_slices(const grpc_metadata* md, size_t count,
grpc_slice key, grpc_slice value) {
size_t i;
for (i = 0; i < count; i++) {
if (grpc_slice_eq(md[i].key, key) && grpc_slice_eq(md[i].value, value)) {
return 1;
}
}
return 0;
}
int contains_metadata_slices(grpc_metadata_array* array, grpc_slice key,
grpc_slice value) {
return has_metadata_slices(array->metadata, array->count, key, value);
}
static grpc_slice merge_slices(grpc_slice* slices, size_t nslices) {
10 years ago
size_t i;
size_t len = 0;
uint8_t* cursor;
grpc_slice out;
10 years ago
for (i = 0; i < nslices; i++) {
len += GRPC_SLICE_LENGTH(slices[i]);
10 years ago
}
out = grpc_slice_malloc(len);
cursor = GRPC_SLICE_START_PTR(out);
10 years ago
for (i = 0; i < nslices; i++) {
8 years ago
memcpy(cursor, GRPC_SLICE_START_PTR(slices[i]),
GRPC_SLICE_LENGTH(slices[i]));
cursor += GRPC_SLICE_LENGTH(slices[i]);
10 years ago
}
return out;
}
int raw_byte_buffer_eq_slice(grpc_byte_buffer* rbb, grpc_slice b) {
grpc_slice a;
int ok;
if (!rbb) return 0;
a = merge_slices(rbb->data.raw.slice_buffer.slices,
rbb->data.raw.slice_buffer.count);
ok = GRPC_SLICE_LENGTH(a) == GRPC_SLICE_LENGTH(b) &&
0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b),
GRPC_SLICE_LENGTH(a));
if (!ok) {
gpr_log(GPR_ERROR,
"SLICE MISMATCH: left_length=%" PRIuPTR " right_length=%" PRIuPTR,
GRPC_SLICE_LENGTH(a), GRPC_SLICE_LENGTH(b));
std::string out;
const char* a_str = reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(a));
const char* b_str = reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(b));
for (size_t i = 0; i < std::max(GRPC_SLICE_LENGTH(a), GRPC_SLICE_LENGTH(b));
i++) {
if (i >= GRPC_SLICE_LENGTH(a)) {
absl::StrAppend(&out, "\u001b[36m", // cyan
absl::CEscape(absl::string_view(&b_str[i], 1)),
"\u001b[0m");
} else if (i >= GRPC_SLICE_LENGTH(b)) {
absl::StrAppend(&out, "\u001b[35m", // magenta
absl::CEscape(absl::string_view(&a_str[i], 1)),
"\u001b[0m");
} else if (a_str[i] == b_str[i]) {
absl::StrAppend(&out, absl::CEscape(absl::string_view(&a_str[i], 1)));
} else {
absl::StrAppend(&out, "\u001b[31m", // red
absl::CEscape(absl::string_view(&a_str[i], 1)),
"\u001b[33m", // yellow
absl::CEscape(absl::string_view(&b_str[i], 1)),
"\u001b[0m");
}
gpr_log(GPR_ERROR, "%s", out.c_str());
}
}
grpc_slice_unref(a);
grpc_slice_unref(b);
10 years ago
return ok;
}
int byte_buffer_eq_slice(grpc_byte_buffer* bb, grpc_slice b) {
if (bb == nullptr) return 0;
if (bb->data.raw.compression > GRPC_COMPRESS_NONE) {
grpc_slice_buffer decompressed_buffer;
grpc_slice_buffer_init(&decompressed_buffer);
Move compression related metadata to the new system (#28223) * Eliminate most of grpc_message metadata handling * Eliminate most of host metadata handling * Remove more callouts without fixing code * fiiixes * typo * Automated change: Fix sanity tests * try-shrink * Automated change: Fix sanity tests * size tweaks * less tricks * deunique * commonize * commonize * Automated change: Fix sanity tests * size tuning, fixes * Automated change: Fix sanity tests * fix * size tuning, fixes * remove constexpr * fix * reuse code * fix * tweak code * more tweaks * tell no lies * fixes * fixes * Automated change: Fix sanity tests * fix * fix * fix * fix * fix? * fix binder * fix * fix * fixes * Automated change: Fix sanity tests * fix * initial refactoring * optimize status encoding * Automated change: Fix sanity tests * Automated change: Fix sanity tests * content-type * Automated change: Fix sanity tests * clang-format * fix * Move colon prefixed metadata * Automated change: Fix sanity tests * Automated change: Fix sanity tests * try to fix windows failure * try and scale sizes better * ambiguity fix? * wip metadatavalueasslice * Fix status code for resource exhaustion * Revert "Revert "Move a bunch of slice typed metadata to new system (#28107)" (#28208)" This reverts commit 771758706396560d6eb91ef6058cdf0e1e65341e. * fix test * Automated change: Fix sanity tests * Automated change: Fix sanity tests * slice helper * x * noinline * try and scale sizes better * Automated change: Fix sanity tests * fixes * fix * fix * fixes * fix build * fix overflow * progress * Automated change: Fix sanity tests * fix * initial work * progress * fix * fix * Automated change: Fix sanity tests * progress * fix * fix * fix * Automated change: Fix sanity tests * fix * compressor for path/authority * Automated change: Fix sanity tests * Automated change: Fix sanity tests * legalize * legalize * status-enc * fmt * fix * fix * fix * fix * fix/opt * fix * fix * fix * fix * Automated change: Fix sanity tests * fix * comment * fmt * remove arg * Automated change: Fix sanity tests * remove name * Automated change: Fix sanity tests * add specialized encoders for compression metadata * review feedback * fix * missoing files * Small improvement in memory usage and performance * Automated change: Fix sanity tests * Automated change: Fix sanity tests * Fix caching * Automated change: Fix sanity tests * fix crash in alts * default everything * Automated change: Fix sanity tests * review feedback * fixes * fixes * fixes * Automated change: Fix sanity tests * speedup * fix * fix * fixes * fixes * fixes * Automated change: Fix sanity tests * fix * remove debug code Co-authored-by: ctiller <ctiller@users.noreply.github.com>
3 years ago
GPR_ASSERT(grpc_msg_decompress(bb->data.raw.compression,
&bb->data.raw.slice_buffer,
&decompressed_buffer));
grpc_byte_buffer* rbb = grpc_raw_byte_buffer_create(
decompressed_buffer.slices, decompressed_buffer.count);
int ret_val = raw_byte_buffer_eq_slice(rbb, b);
grpc_byte_buffer_destroy(rbb);
grpc_slice_buffer_destroy(&decompressed_buffer);
return ret_val;
}
return raw_byte_buffer_eq_slice(bb, b);
}
int byte_buffer_eq_string(grpc_byte_buffer* bb, const char* str) {
return byte_buffer_eq_slice(bb, grpc_slice_from_copied_string(str));
}
namespace {
bool IsProbablyInteger(void* p) {
4 years ago
return reinterpret_cast<uintptr_t>(p) < 1000000;
}
std::string TagStr(void* tag) {
if (IsProbablyInteger(tag)) {
return absl::StrFormat("tag(%" PRIdPTR ")",
reinterpret_cast<intptr_t>(tag));
} else {
return absl::StrFormat("%p", tag);
10 years ago
}
}
} // namespace
namespace grpc_core {
CqVerifier::CqVerifier(grpc_completion_queue* cq,
absl::AnyInvocable<void(Failure)> fail)
: cq_(cq), fail_(std::move(fail)) {}
10 years ago
CqVerifier::~CqVerifier() { Verify(); }
std::string CqVerifier::Expectation::ToString() const {
return absl::StrCat(
location.file(), ":", location.line(), ": ", TagStr(tag), " ",
Match(
result,
[](bool success) {
return absl::StrCat("success=", success ? "true" : "false");
},
[](Maybe) { return std::string("maybe"); },
[](AnyStatus) { return std::string("any success value"); },
[promises] Run C++ end to end tests with server promises (#32537) Expand server promises to run with C++ end2end tests. Across connected_channel/call/batch_builder/pipe/transport: - fix a bug where read errors weren't propagated from transport to call so that we can populate failed_before_recv_message for the c++ bindings - ensure those errors are not, however, used to populate the returned call status Add a new latch call arg to lazily propagate the bound CQ for a server call (and client call, but here it's used degenerately - it's always populated). This allows server calls to be properly bound to pollsets.(1)/(2) In call.cc: - move some profiling code from FilterStackCall to Call, and then use it in PromiseBasedCall (this should be cleaned up with tracing work) - implement GetServerAuthority In server.cc: - use an RAII pattern on `MatchResult` to avoid a bug whereby a tag could be dropped if we cancel a request after it's been matched but before it's published - fix deadline export to ServerContext In resource_quota_server.cc: - fix some long standing flakes (that were finally obvious with the new test code) - it's legal here to have client calls not arrive at the server due to resource starvation, work through that (includes adding expectations during a `Step` call, which required some small tweaks to cq_verifier) In the C++ end2end_test.cc: - strengthen a flaky test so it passes consistently (it's likely we'll revisit this with the fuzzing efforts to strengthen it into an actually robust test) (1) It's time to remove this concept (2) Surprisingly the only test that *reliably* demonstrates this not being done is time_change_test --------- Co-authored-by: ctiller <ctiller@users.noreply.github.com>
2 years ago
[](const PerformAction&) {
return std::string("perform some action");
},
[](const MaybePerformAction&) {
return std::string("maybe perform action");
}));
}
std::vector<std::string> CqVerifier::ToStrings() const {
std::vector<std::string> expectations;
expectations.reserve(expectations_.size());
for (const auto& e : expectations_) {
expectations.push_back(e.ToString());
10 years ago
}
return expectations;
}
std::string CqVerifier::ToString() const {
return absl::StrJoin(ToStrings(), "\n");
10 years ago
}
void CqVerifier::FailNoEventReceived(const SourceLocation& location) const {
fail_(Failure{location, "No event received", ToStrings()});
10 years ago
}
void CqVerifier::FailUnexpectedEvent(grpc_event* ev,
const SourceLocation& location) const {
fail_(Failure{location,
absl::StrCat("Unexpected event: ", grpc_event_string(ev)),
ToStrings()});
}
void CqVerifier::FailUsingGprCrash(const Failure& failure) {
Crash(absl::StrCat("[", failure.location.file(), ":", failure.location.line(),
"] ", failure.message, "\nexpected:\n",
absl::StrJoin(failure.expected, "\n")));
}
void CqVerifier::FailUsingGtestFail(const Failure& failure) {
std::string message = absl::StrCat(" ", failure.message);
if (!failure.expected.empty()) {
absl::StrAppend(&message, "\n expected:\n");
for (const auto& line : failure.expected) {
absl::StrAppend(&message, " ", line, "\n");
}
} else {
absl::StrAppend(&message, "\n expected nothing");
}
ADD_FAILURE_AT(failure.location.file(), failure.location.line()) << message;
}
namespace {
bool IsMaybe(const CqVerifier::ExpectedResult& r) {
return Match(
r, [](bool) { return false; }, [](CqVerifier::Maybe) { return true; },
[](CqVerifier::AnyStatus) { return false; },
[](const CqVerifier::PerformAction&) { return false; },
[](const CqVerifier::MaybePerformAction&) { return true; });
}
} // namespace
void CqVerifier::Verify(Duration timeout, SourceLocation location) {
const gpr_timespec deadline =
grpc_timeout_milliseconds_to_deadline(timeout.millis());
while (!expectations_.empty()) {
grpc_event ev = grpc_completion_queue_next(cq_, deadline, nullptr);
if (ev.type == GRPC_QUEUE_TIMEOUT) break;
if (ev.type != GRPC_OP_COMPLETE) {
FailUnexpectedEvent(&ev, location);
}
bool found = false;
for (auto it = expectations_.begin(); it != expectations_.end(); ++it) {
if (it->tag != ev.tag) continue;
[promises] Run C++ end to end tests with server promises (#32537) Expand server promises to run with C++ end2end tests. Across connected_channel/call/batch_builder/pipe/transport: - fix a bug where read errors weren't propagated from transport to call so that we can populate failed_before_recv_message for the c++ bindings - ensure those errors are not, however, used to populate the returned call status Add a new latch call arg to lazily propagate the bound CQ for a server call (and client call, but here it's used degenerately - it's always populated). This allows server calls to be properly bound to pollsets.(1)/(2) In call.cc: - move some profiling code from FilterStackCall to Call, and then use it in PromiseBasedCall (this should be cleaned up with tracing work) - implement GetServerAuthority In server.cc: - use an RAII pattern on `MatchResult` to avoid a bug whereby a tag could be dropped if we cancel a request after it's been matched but before it's published - fix deadline export to ServerContext In resource_quota_server.cc: - fix some long standing flakes (that were finally obvious with the new test code) - it's legal here to have client calls not arrive at the server due to resource starvation, work through that (includes adding expectations during a `Step` call, which required some small tweaks to cq_verifier) In the C++ end2end_test.cc: - strengthen a flaky test so it passes consistently (it's likely we'll revisit this with the fuzzing efforts to strengthen it into an actually robust test) (1) It's time to remove this concept (2) Surprisingly the only test that *reliably* demonstrates this not being done is time_change_test --------- Co-authored-by: ctiller <ctiller@users.noreply.github.com>
2 years ago
auto expectation = std::move(*it);
expectations_.erase(it);
const bool expected = Match(
[promises] Run C++ end to end tests with server promises (#32537) Expand server promises to run with C++ end2end tests. Across connected_channel/call/batch_builder/pipe/transport: - fix a bug where read errors weren't propagated from transport to call so that we can populate failed_before_recv_message for the c++ bindings - ensure those errors are not, however, used to populate the returned call status Add a new latch call arg to lazily propagate the bound CQ for a server call (and client call, but here it's used degenerately - it's always populated). This allows server calls to be properly bound to pollsets.(1)/(2) In call.cc: - move some profiling code from FilterStackCall to Call, and then use it in PromiseBasedCall (this should be cleaned up with tracing work) - implement GetServerAuthority In server.cc: - use an RAII pattern on `MatchResult` to avoid a bug whereby a tag could be dropped if we cancel a request after it's been matched but before it's published - fix deadline export to ServerContext In resource_quota_server.cc: - fix some long standing flakes (that were finally obvious with the new test code) - it's legal here to have client calls not arrive at the server due to resource starvation, work through that (includes adding expectations during a `Step` call, which required some small tweaks to cq_verifier) In the C++ end2end_test.cc: - strengthen a flaky test so it passes consistently (it's likely we'll revisit this with the fuzzing efforts to strengthen it into an actually robust test) (1) It's time to remove this concept (2) Surprisingly the only test that *reliably* demonstrates this not being done is time_change_test --------- Co-authored-by: ctiller <ctiller@users.noreply.github.com>
2 years ago
expectation.result,
[ev](bool success) { return ev.success == success; },
[ev](Maybe m) {
if (m.seen != nullptr) *m.seen = true;
return ev.success != 0;
},
[ev](AnyStatus a) {
if (a.result != nullptr) *a.result = ev.success;
return true;
},
[ev](const PerformAction& action) {
action.action(ev.success);
return true;
},
[ev](const MaybePerformAction& action) {
action.action(ev.success);
return true;
});
if (!expected) {
FailUnexpectedEvent(&ev, location);
}
found = true;
break;
}
if (!found) FailUnexpectedEvent(&ev, location);
if (AllMaybes()) break;
}
expectations_.erase(
std::remove_if(expectations_.begin(), expectations_.end(),
[](const Expectation& e) { return IsMaybe(e.result); }),
expectations_.end());
if (!expectations_.empty()) FailNoEventReceived(location);
}
bool CqVerifier::AllMaybes() const {
for (const auto& e : expectations_) {
if (!IsMaybe(e.result)) return false;
10 years ago
}
return true;
10 years ago
}
void CqVerifier::VerifyEmpty(Duration timeout, SourceLocation location) {
const gpr_timespec deadline =
gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), timeout.as_timespec());
GPR_ASSERT(expectations_.empty());
grpc_event ev = grpc_completion_queue_next(cq_, deadline, nullptr);
if (ev.type != GRPC_QUEUE_TIMEOUT) {
FailUnexpectedEvent(&ev, location);
}
10 years ago
}
void CqVerifier::Expect(void* tag, ExpectedResult result,
SourceLocation location) {
expectations_.push_back(Expectation{location, tag, std::move(result)});
}
} // namespace grpc_core