Skip to content

Commit 056bfda

Browse files
authored
[test] add EXPECT_ENVOY_BUG (envoyproxy#14800)
* add EXPECT_ENVOY_BUG macro for testing Signed-off-by: Asra Ali <[email protected]>
1 parent 127aa55 commit 056bfda

File tree

12 files changed

+109
-51
lines changed

12 files changed

+109
-51
lines changed

source/common/common/assert.cc

+10-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class EnvoyBugRegistrationImpl : public ActionRegistration {
5353
// The implementation may also be a inline static counter per-file and line. There is no benchmark
5454
// to show that the performance of this mutex is any worse than atomic counters. Acquiring and
5555
// releasing a mutex is cheaper than a cache miss, but the mutex here is contended for every
56-
// ENVOY_BUG failure rather than per individual bug. Logging ENVOY_BUGs is not a performance
56+
// ENVOY_BUG failure rather than per individual bug. Hitting ENVOY_BUGs is not a performance
5757
// critical path, and mutex contention would indicate that there is a serious failure.
5858
// Currently, this choice reduces code size and has the advantage that behavior is easier to
5959
// understand and debug, and test behavior is predictable.
@@ -75,6 +75,13 @@ class EnvoyBugRegistrationImpl : public ActionRegistration {
7575
}
7676
}
7777

78+
static void resetEnvoyBugCounters() {
79+
{
80+
absl::MutexLock lock(&mutex_);
81+
counters_.clear();
82+
}
83+
}
84+
7885
private:
7986
// This implementation currently only handles one action being set at a time. This is currently
8087
// sufficient. If multiple actions are ever needed, the actions should be chained when
@@ -111,5 +118,7 @@ bool shouldLogAndInvokeEnvoyBugForEnvoyBugMacroUseOnly(absl::string_view bug_nam
111118
return EnvoyBugRegistrationImpl::shouldLogAndInvoke(bug_name);
112119
}
113120

121+
void resetEnvoyBugCountersForTest() { EnvoyBugRegistrationImpl::resetEnvoyBugCounters(); }
122+
114123
} // namespace Assert
115124
} // namespace Envoy

source/common/common/assert.h

+6
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ void invokeEnvoyBugFailureRecordActionForEnvoyBugMacroUseOnly();
7171
*/
7272
bool shouldLogAndInvokeEnvoyBugForEnvoyBugMacroUseOnly(absl::string_view bug_name);
7373

74+
/**
75+
* Resets all counters for EnvoyBugRegistrationImpl between tests.
76+
*
77+
*/
78+
void resetEnvoyBugCountersForTest();
79+
7480
// CONDITION_STR is needed to prevent macros in condition from being expected, which obfuscates
7581
// the logged failure, e.g., "EAGAIN" vs "11".
7682
#define _ASSERT_IMPL(CONDITION, CONDITION_STR, ACTION, DETAILS) \

test/common/buffer/owned_impl_test.cc

+13-20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "test/mocks/api/mocks.h"
88
#include "test/test_common/logging.h"
99
#include "test/test_common/threadsafe_singleton_injector.h"
10+
#include "test/test_common/utility.h"
1011

1112
#include "absl/strings/str_cat.h"
1213
#include "gmock/gmock.h"
@@ -913,16 +914,12 @@ TEST_F(OwnedImplTest, ReserveOverCommit) {
913914
auto reservation = buffer.reserveForRead();
914915
const auto reservation_length = reservation.length();
915916
const auto excess_length = reservation_length + 1;
916-
#ifdef NDEBUG
917-
reservation.commit(excess_length);
918-
919-
// The length should be the Reservation length, not the value passed to commit.
920-
EXPECT_EQ(reservation_length, buffer.length());
921-
#else
922-
EXPECT_DEATH(
923-
reservation.commit(excess_length),
924-
"length <= length_. Details: commit\\(\\) length must be <= size of the Reservation");
925-
#endif
917+
EXPECT_ENVOY_BUG(
918+
{
919+
reservation.commit(excess_length);
920+
EXPECT_EQ(reservation_length, buffer.length());
921+
},
922+
"length <= length_. Details: commit() length must be <= size of the Reservation");
926923
}
927924

928925
// Test behavior when the size to commit() is larger than the reservation.
@@ -931,16 +928,12 @@ TEST_F(OwnedImplTest, ReserveSingleOverCommit) {
931928
auto reservation = buffer.reserveSingleSlice(10);
932929
const auto reservation_length = reservation.length();
933930
const auto excess_length = reservation_length + 1;
934-
#ifdef NDEBUG
935-
reservation.commit(excess_length);
936-
937-
// The length should be the Reservation length, not the value passed to commit.
938-
EXPECT_EQ(reservation_length, buffer.length());
939-
#else
940-
EXPECT_DEATH(
941-
reservation.commit(excess_length),
942-
"length <= slice_.len_. Details: commit\\(\\) length must be <= size of the Reservation");
943-
#endif
931+
EXPECT_ENVOY_BUG(
932+
{
933+
reservation.commit(excess_length);
934+
EXPECT_EQ(reservation_length, buffer.length());
935+
},
936+
"length <= slice_.len_. Details: commit() length must be <= size of the Reservation");
944937
}
945938

946939
// Test functionality of the `freelist` (a performance optimization)

test/common/common/BUILD

+11
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ envoy_cc_test(
2626
deps = [
2727
"//source/common/common:assert_lib",
2828
"//test/test_common:logging_lib",
29+
"//test/test_common:utility_lib",
30+
],
31+
)
32+
33+
envoy_cc_test(
34+
name = "assert2_test",
35+
srcs = ["assert2_test.cc"],
36+
deps = [
37+
"//source/common/common:assert_lib",
38+
"//test/test_common:logging_lib",
39+
"//test/test_common:utility_lib",
2940
],
3041
)
3142

test/common/common/assert2_test.cc

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include "common/common/assert.h"
2+
3+
#include "test/test_common/logging.h"
4+
#include "test/test_common/utility.h"
5+
6+
#include "gtest/gtest.h"
7+
8+
namespace Envoy {
9+
10+
TEST(EnvoyBugDeathTest, TestResetCounters) {
11+
// The callEnvoyBug counter has already been called in assert2_test.cc.
12+
// ENVOY_BUG only log and increment stats on power of two cases. Ensure that counters are reset
13+
// between tests by checking that two consecutive calls trigger the expectation.
14+
for (int i = 0; i < 2; i++) {
15+
EXPECT_ENVOY_BUG(TestEnvoyBug::callEnvoyBug(), "envoy bug failure: false.");
16+
}
17+
}
18+
19+
} // namespace Envoy

test/common/common/assert_test.cc

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "common/common/assert.h"
22

33
#include "test/test_common/logging.h"
4+
#include "test/test_common/utility.h"
45

56
#include "gtest/gtest.h"
67

@@ -47,16 +48,18 @@ TEST(EnvoyBugDeathTest, VariousLogs) {
4748
auto envoy_bug_action_registration =
4849
Assert::setEnvoyBugFailureRecordAction([&]() { envoy_bug_fail_count++; });
4950

50-
#ifndef NDEBUG
51-
EXPECT_DEATH({ ENVOY_BUG(false, ""); }, ".*envoy bug failure: false.*");
52-
EXPECT_DEATH({ ENVOY_BUG(false, ""); }, ".*envoy bug failure: false.*");
53-
EXPECT_DEATH({ ENVOY_BUG(false, "With some logs"); },
54-
".*envoy bug failure: false. Details: With some logs.*");
55-
EXPECT_EQ(0, envoy_bug_fail_count);
56-
#else
57-
// Same log lines trigger exponential back-off.
51+
EXPECT_ENVOY_BUG({ ENVOY_BUG(false, ""); }, "envoy bug failure: false.");
52+
EXPECT_ENVOY_BUG({ ENVOY_BUG(false, ""); }, "envoy bug failure: false.");
53+
EXPECT_ENVOY_BUG({ ENVOY_BUG(false, "With some logs"); },
54+
"envoy bug failure: false. Details: With some logs");
55+
56+
#ifdef NDEBUG
57+
EXPECT_EQ(3, envoy_bug_fail_count);
58+
// Reset envoy bug count to simplify testing exponential back-off below.
59+
envoy_bug_fail_count = 0;
60+
// In release mode, same log lines trigger exponential back-off.
5861
for (int i = 0; i < 4; i++) {
59-
ENVOY_BUG(false, "");
62+
ENVOY_BUG(false, "placeholder ENVOY_BUG");
6063
}
6164
// 3 counts because 1st, 2nd, and 4th instances are powers of 2.
6265
EXPECT_EQ(3, envoy_bug_fail_count);
@@ -69,4 +72,13 @@ TEST(EnvoyBugDeathTest, VariousLogs) {
6972
#endif
7073
}
7174

75+
TEST(EnvoyBugDeathTest, TestResetCounters) {
76+
// The callEnvoyBug counter has already been called in assert2_test.cc.
77+
// ENVOY_BUG only log and increment stats on power of two cases. Ensure that counters are reset
78+
// between tests by checking that two consecutive calls trigger the expectation.
79+
for (int i = 0; i < 2; i++) {
80+
EXPECT_ENVOY_BUG(TestEnvoyBug::callEnvoyBug(), "envoy bug failure: false.");
81+
}
82+
}
83+
7284
} // namespace Envoy

test/common/router/router_test.cc

+1-2
Original file line numberDiff line numberDiff line change
@@ -5744,8 +5744,7 @@ TEST_F(RouterTest, InvalidUpstream) {
57445744
Http::TestRequestHeaderMapImpl headers;
57455745
HttpTestUtility::addDefaultHeaders(headers);
57465746
headers.setMethod("CONNECT");
5747-
EXPECT_DEBUG_DEATH(router_.decodeHeaders(headers, false),
5748-
"envoy bug failure: factory != nullptr.");
5747+
EXPECT_ENVOY_BUG(router_.decodeHeaders(headers, false), "envoy bug failure: factory != nullptr.");
57495748

57505749
router_.onDestroy();
57515750
}

test/extensions/transport_sockets/tls/utility_test.cc

+2-5
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,8 @@ TEST(UtilityTest, SslErrorDescriptionTest) {
164164
EXPECT_EQ(test_data.second, Utility::getErrorDescription(test_data.first));
165165
}
166166

167-
#if defined(NDEBUG)
168-
EXPECT_EQ(Utility::getErrorDescription(19), "UNKNOWN_ERROR");
169-
#else
170-
EXPECT_DEATH(Utility::getErrorDescription(19), "Unknown BoringSSL error had occurred");
171-
#endif
167+
EXPECT_ENVOY_BUG(EXPECT_EQ(Utility::getErrorDescription(19), "UNKNOWN_ERROR"),
168+
"Unknown BoringSSL error had occurred");
172169
}
173170

174171
} // namespace

test/integration/protocol_integration_test.cc

+6-14
Original file line numberDiff line numberDiff line change
@@ -302,24 +302,16 @@ TEST_P(ProtocolIntegrationTest, ContinueAfterLocalReply) {
302302

303303
// Send a headers only request.
304304
IntegrationStreamDecoderPtr response;
305-
const std::string error = "envoy bug failure: !state_.local_complete_ || status == "
306-
"FilterHeadersStatus::StopIteration. Details: Filters should return "
307-
"FilterHeadersStatus::StopIteration after sending a local reply.";
308-
#ifdef NDEBUG
309-
EXPECT_LOG_CONTAINS("error", error, {
310-
response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
311-
response->waitForEndStream();
312-
});
313-
EXPECT_TRUE(response->complete());
314-
EXPECT_EQ("200", response->headers().getStatusValue());
315-
#else
316-
EXPECT_DEATH(
305+
EXPECT_ENVOY_BUG(
317306
{
318307
response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
319308
response->waitForEndStream();
309+
EXPECT_TRUE(response->complete());
310+
EXPECT_EQ("200", response->headers().getStatusValue());
320311
},
321-
HasSubstr(error));
322-
#endif
312+
"envoy bug failure: !state_.local_complete_ || status == "
313+
"FilterHeadersStatus::StopIteration. Details: Filters should return "
314+
"FilterHeadersStatus::StopIteration after sending a local reply.");
323315
}
324316

325317
TEST_P(ProtocolIntegrationTest, AddEncodedTrailers) {

test/test_common/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ envoy_cc_test_library(
110110
],
111111
deps = [
112112
":file_system_for_test_lib",
113+
":logging_lib",
113114
":printers_lib",
114115
":resources_lib",
115116
":test_time_lib",

test/test_common/utility.h

+16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "common/stats/symbol_table_impl.h"
2929

3030
#include "test/test_common/file_system_for_test.h"
31+
#include "test/test_common/logging.h"
3132
#include "test/test_common/printers.h"
3233
#include "test/test_common/test_time_system.h"
3334
#include "test/test_common/thread_factory_for_test.h"
@@ -80,6 +81,16 @@ namespace Envoy {
8081
EXPECT_THAT_THROWS_MESSAGE(statement, expected_exception, \
8182
::testing::Not(::testing::ContainsRegex(regex_str)))
8283

84+
// Expect that the statement hits an ENVOY_BUG containing the specified message.
85+
#ifdef NDEBUG
86+
// ENVOY_BUGs in release mode log error.
87+
#define EXPECT_ENVOY_BUG(statement, message) EXPECT_LOG_CONTAINS("error", message, statement)
88+
#else
89+
// ENVOY_BUGs in debug mode is fatal.
90+
#define EXPECT_ENVOY_BUG(statement, message) \
91+
EXPECT_DEBUG_DEATH(statement, ::testing::HasSubstr(message))
92+
#endif
93+
8394
#define VERBOSE_EXPECT_NO_THROW(statement) \
8495
try { \
8596
statement; \
@@ -108,6 +119,11 @@ namespace Envoy {
108119
#define DEPRECATED_FEATURE_TEST(X) DISABLED_##X
109120
#endif
110121

122+
class TestEnvoyBug {
123+
public:
124+
static void callEnvoyBug() { ENVOY_BUG(false, ""); }
125+
};
126+
111127
// Random number generator which logs its seed to stderr. To repeat a test run with a non-zero seed
112128
// one can run the test with --test_arg=--gtest_random_seed=[seed]
113129
class TestRandomGenerator {

test/test_runner.cc

+3
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ int TestRunner::RunTests(int argc, char** argv) {
153153
TestEnvironment::getOptions().logPath(), access_log_manager, Logger::Registry::getSink());
154154
}
155155

156+
// Reset all ENVOY_BUG counters.
157+
Envoy::Assert::resetEnvoyBugCountersForTest();
158+
156159
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
157160
// Fuzz tests may run Envoy tests in fuzzing mode to generate corpora. In this case, we do not
158161
// want to fail building the fuzz test because of a failed test run, which can happen when testing

0 commit comments

Comments
 (0)