blob: 2ca9ea54bdead4bdceadeb096114339a80bbdda3 [file]
/*
*
* 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/transport/connectivity_state.h"
#include <string.h>
#include <gtest/gtest.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include "src/core/lib/iomgr/exec_ctx.h"
#include "test/core/util/test_config.h"
#include "test/core/util/tracer_util.h"
namespace grpc_core {
namespace {
TEST(ConnectivityStateName, Basic) {
EXPECT_STREQ("IDLE", ConnectivityStateName(GRPC_CHANNEL_IDLE));
EXPECT_STREQ("CONNECTING", ConnectivityStateName(GRPC_CHANNEL_CONNECTING));
EXPECT_STREQ("READY", ConnectivityStateName(GRPC_CHANNEL_READY));
EXPECT_STREQ("TRANSIENT_FAILURE",
ConnectivityStateName(GRPC_CHANNEL_TRANSIENT_FAILURE));
EXPECT_STREQ("SHUTDOWN", ConnectivityStateName(GRPC_CHANNEL_SHUTDOWN));
}
class Watcher : public ConnectivityStateWatcherInterface {
public:
Watcher(int* count, grpc_connectivity_state* output, absl::Status* status,
bool* destroyed = nullptr)
: count_(count),
output_(output),
status_(status),
destroyed_(destroyed) {}
~Watcher() override {
if (destroyed_ != nullptr) *destroyed_ = true;
}
void Notify(grpc_connectivity_state new_state,
const absl::Status& status) override {
++*count_;
*output_ = new_state;
*status_ = status;
}
private:
int* count_;
grpc_connectivity_state* output_;
absl::Status* status_;
bool* destroyed_;
};
TEST(StateTracker, SetAndGetState) {
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_CONNECTING,
absl::Status());
EXPECT_EQ(tracker.state(), GRPC_CHANNEL_CONNECTING);
EXPECT_TRUE(tracker.status().ok());
tracker.SetState(GRPC_CHANNEL_READY, absl::Status(), "whee");
EXPECT_EQ(tracker.state(), GRPC_CHANNEL_READY);
EXPECT_TRUE(tracker.status().ok());
absl::Status transient_failure_status(absl::StatusCode::kUnavailable,
"status for testing");
tracker.SetState(GRPC_CHANNEL_TRANSIENT_FAILURE, transient_failure_status,
"reason");
EXPECT_EQ(tracker.state(), GRPC_CHANNEL_TRANSIENT_FAILURE);
EXPECT_EQ(tracker.status(), transient_failure_status);
}
TEST(StateTracker, NotificationUponAddingWatcher) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
absl::Status status;
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_CONNECTING);
tracker.AddWatcher(GRPC_CHANNEL_IDLE,
MakeOrphanable<Watcher>(&count, &state, &status));
EXPECT_EQ(count, 1);
EXPECT_EQ(state, GRPC_CHANNEL_CONNECTING);
EXPECT_TRUE(status.ok());
}
TEST(StateTracker, NotificationUponAddingWatcherWithTransientFailure) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
absl::Status status;
absl::Status transient_failure_status(absl::StatusCode::kUnavailable,
"status for testing");
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_TRANSIENT_FAILURE,
transient_failure_status);
tracker.AddWatcher(GRPC_CHANNEL_IDLE,
MakeOrphanable<Watcher>(&count, &state, &status));
EXPECT_EQ(count, 1);
EXPECT_EQ(state, GRPC_CHANNEL_TRANSIENT_FAILURE);
EXPECT_EQ(status, transient_failure_status);
}
TEST(StateTracker, NotificationUponStateChange) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
absl::Status status;
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_IDLE);
tracker.AddWatcher(GRPC_CHANNEL_IDLE,
MakeOrphanable<Watcher>(&count, &state, &status));
EXPECT_EQ(count, 0);
EXPECT_EQ(state, GRPC_CHANNEL_IDLE);
EXPECT_TRUE(status.ok());
absl::Status transient_failure_status(absl::StatusCode::kUnavailable,
"status for testing");
tracker.SetState(GRPC_CHANNEL_TRANSIENT_FAILURE, transient_failure_status,
"whee");
EXPECT_EQ(count, 1);
EXPECT_EQ(state, GRPC_CHANNEL_TRANSIENT_FAILURE);
EXPECT_EQ(status, transient_failure_status);
}
TEST(StateTracker, SubscribeThenUnsubscribe) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
absl::Status status;
bool destroyed = false;
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_IDLE);
ConnectivityStateWatcherInterface* watcher =
new Watcher(&count, &state, &status, &destroyed);
tracker.AddWatcher(GRPC_CHANNEL_IDLE,
OrphanablePtr<ConnectivityStateWatcherInterface>(watcher));
// No initial notification, since we started the watch from the
// current state.
EXPECT_EQ(count, 0);
EXPECT_EQ(state, GRPC_CHANNEL_IDLE);
EXPECT_TRUE(status.ok());
// Cancel watch. This should not generate another notification.
tracker.RemoveWatcher(watcher);
EXPECT_TRUE(destroyed);
EXPECT_EQ(count, 0);
EXPECT_EQ(state, GRPC_CHANNEL_IDLE);
EXPECT_TRUE(status.ok());
}
TEST(StateTracker, OrphanUponShutdown) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
absl::Status status;
bool destroyed = false;
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_IDLE);
ConnectivityStateWatcherInterface* watcher =
new Watcher(&count, &state, &status, &destroyed);
tracker.AddWatcher(GRPC_CHANNEL_IDLE,
OrphanablePtr<ConnectivityStateWatcherInterface>(watcher));
// No initial notification, since we started the watch from the
// current state.
EXPECT_EQ(count, 0);
EXPECT_EQ(state, GRPC_CHANNEL_IDLE);
EXPECT_TRUE(status.ok());
// Set state to SHUTDOWN.
tracker.SetState(GRPC_CHANNEL_SHUTDOWN, absl::Status(), "shutting down");
EXPECT_TRUE(destroyed);
EXPECT_EQ(count, 1);
EXPECT_EQ(state, GRPC_CHANNEL_SHUTDOWN);
EXPECT_TRUE(status.ok());
}
TEST(StateTracker, AddWhenAlreadyShutdown) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
absl::Status status;
bool destroyed = false;
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_SHUTDOWN,
absl::Status());
ConnectivityStateWatcherInterface* watcher =
new Watcher(&count, &state, &status, &destroyed);
tracker.AddWatcher(GRPC_CHANNEL_IDLE,
OrphanablePtr<ConnectivityStateWatcherInterface>(watcher));
EXPECT_TRUE(destroyed);
EXPECT_EQ(count, 1);
EXPECT_EQ(state, GRPC_CHANNEL_SHUTDOWN);
EXPECT_TRUE(status.ok());
}
TEST(StateTracker, NotifyShutdownAtDestruction) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
absl::Status status;
{
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_IDLE);
tracker.AddWatcher(GRPC_CHANNEL_IDLE,
MakeOrphanable<Watcher>(&count, &state, &status));
// No initial notification, since we started the watch from the
// current state.
EXPECT_EQ(count, 0);
EXPECT_EQ(state, GRPC_CHANNEL_IDLE);
}
// Upon tracker destruction, we get a notification for SHUTDOWN.
EXPECT_EQ(count, 1);
EXPECT_EQ(state, GRPC_CHANNEL_SHUTDOWN);
}
TEST(StateTracker, DoNotNotifyShutdownAtDestructionIfAlreadyInShutdown) {
int count = 0;
grpc_connectivity_state state = GRPC_CHANNEL_SHUTDOWN;
absl::Status status;
{
ConnectivityStateTracker tracker("xxx", GRPC_CHANNEL_SHUTDOWN);
tracker.AddWatcher(GRPC_CHANNEL_SHUTDOWN,
MakeOrphanable<Watcher>(&count, &state, &status));
// No initial notification, since we started the watch from the
// current state.
EXPECT_EQ(count, 0);
EXPECT_EQ(state, GRPC_CHANNEL_SHUTDOWN);
}
// No additional notification upon tracker destruction, since we were
// already in state SHUTDOWN.
EXPECT_EQ(count, 0);
EXPECT_EQ(state, GRPC_CHANNEL_SHUTDOWN);
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(&argc, argv);
::testing::InitGoogleTest(&argc, argv);
grpc_init();
grpc_core::testing::grpc_tracer_enable_flag(
&grpc_core::grpc_connectivity_state_trace);
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}