1 //
2 // Copyright 2023 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "test/cpp/interop/xds_stats_watcher.h"
18
19 #include <map>
20 #include <memory>
21
22 #include <gmock/gmock.h>
23 #include <gtest/gtest.h>
24
25 #include <grpc/grpc.h>
26
27 #include "test/core/util/test_config.h"
28
29 namespace grpc {
30 namespace testing {
31 namespace {
32
BuildCallResult(int saved_request_id)33 AsyncClientCallResult BuildCallResult(int saved_request_id) {
34 AsyncClientCallResult result;
35 result.saved_request_id = saved_request_id;
36 result.rpc_type = ClientConfigureRequest::UNARY_CALL;
37 return result;
38 }
39
40 struct MetadataEntryInit {
41 absl::string_view key;
42 absl::string_view value;
43 bool is_trailing;
44 };
45
BuildMetadatas(const std::initializer_list<std::initializer_list<MetadataEntryInit>> & values)46 LoadBalancerStatsResponse::MetadataByPeer BuildMetadatas(
47 const std::initializer_list<std::initializer_list<MetadataEntryInit>>&
48 values) {
49 LoadBalancerStatsResponse::MetadataByPeer metadata_by_peer;
50 for (const auto& per_rpc : values) {
51 auto rpc_metadata = metadata_by_peer.add_rpc_metadata();
52 for (const auto& key_value : per_rpc) {
53 auto entry = rpc_metadata->add_metadata();
54 entry->set_key(key_value.key);
55 entry->set_value(key_value.value);
56 entry->set_type(key_value.is_trailing
57 ? LoadBalancerStatsResponse::TRAILING
58 : LoadBalancerStatsResponse::INITIAL);
59 }
60 }
61 return metadata_by_peer;
62 }
63
TEST(XdsStatsWatcherTest,WaitForRpcStatsResponse)64 TEST(XdsStatsWatcherTest, WaitForRpcStatsResponse) {
65 // "k3" will be ignored
66 XdsStatsWatcher watcher(0, 4, {"k1", "k2"});
67 watcher.RpcCompleted(BuildCallResult(0), "peer1",
68 {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}},
69 {{"k1", "t1"}, {"k3", "t3"}});
70 watcher.RpcCompleted(BuildCallResult(1), "peer1", {{"k1", "v4"}}, {});
71 watcher.RpcCompleted(BuildCallResult(2), "peer1", {}, {});
72 watcher.RpcCompleted(BuildCallResult(3), "peer2",
73 {{"k1", "v5"}, {"k2", "v6"}, {"k3", "v7"}},
74 {{"k1", "t5"}, {"k3", "t7"}});
75 LoadBalancerStatsResponse expected;
76 expected.mutable_rpcs_by_peer()->insert({{"peer1", 3}, {"peer2", 1}});
77 expected.mutable_metadatas_by_peer()->insert({
78 {"peer1",
79 BuildMetadatas({
80 {{"k1", "v1", false}, {"k2", "v2", false}, {"k1", "t1", true}},
81 {{"k1", "v4", false}},
82 {},
83 })},
84 {"peer2",
85 BuildMetadatas({
86 {{"k1", "v5", false}, {"k2", "v6", false}, {"k1", "t5", true}},
87 })},
88 });
89 (*expected.mutable_rpcs_by_method())["UnaryCall"]
90 .mutable_rpcs_by_peer()
91 ->insert({{"peer1", 3}, {"peer2", 1}});
92 EXPECT_EQ(expected.DebugString(),
93 watcher.WaitForRpcStatsResponse(0).DebugString());
94 }
95
TEST(XdsStatsWatcherTest,WaitForRpcStatsResponseIgnoresCase)96 TEST(XdsStatsWatcherTest, WaitForRpcStatsResponseIgnoresCase) {
97 // "k3" will be ignored
98 XdsStatsWatcher watcher(0, 3, {"k1", "K2"});
99 watcher.RpcCompleted(BuildCallResult(0), "peer1",
100 {{"K1", "v1"}, {"k2", "v2"}, {"k3", "v3"}},
101 {{"K1", "t1"}, {"k2", "t2"}});
102 watcher.RpcCompleted(BuildCallResult(1), "peer1", {}, {});
103 watcher.RpcCompleted(BuildCallResult(2), "peer2", {},
104 {{"k1", "v5"}, {"K2", "v6"}, {"k3", "v7"}});
105 LoadBalancerStatsResponse expected;
106 expected.mutable_rpcs_by_peer()->insert({{"peer1", 2}, {"peer2", 1}});
107 expected.mutable_metadatas_by_peer()->insert({
108 {"peer1", BuildMetadatas({
109 {{"K1", "v1", false},
110 {"k2", "v2", false},
111 {"K1", "t1", true},
112 {"k2", "t2", true}},
113 {},
114 })},
115 {"peer2", BuildMetadatas({{{"K2", "v6", true}, {"k1", "v5", true}}})},
116 });
117 (*expected.mutable_rpcs_by_method())["UnaryCall"]
118 .mutable_rpcs_by_peer()
119 ->insert({{"peer1", 2}, {"peer2", 1}});
120 EXPECT_EQ(expected.DebugString(),
121 watcher.WaitForRpcStatsResponse(0).DebugString());
122 }
123
TEST(XdsStatsWatcherTest,WaitForRpcStatsResponseReturnsAll)124 TEST(XdsStatsWatcherTest, WaitForRpcStatsResponseReturnsAll) {
125 // "k3" will be ignored
126 XdsStatsWatcher watcher(0, 3, {"*"});
127 watcher.RpcCompleted(BuildCallResult(0), "peer1",
128 {{"K1", "v1"}, {"k2", "v2"}, {"k3", "v3"}},
129 {{"K1", "t1"}, {"k2", "t2"}});
130 watcher.RpcCompleted(BuildCallResult(1), "peer1", {}, {});
131 watcher.RpcCompleted(BuildCallResult(2), "peer2", {},
132 {{"k1", "v5"}, {"K2", "v6"}, {"k3", "v7"}});
133 LoadBalancerStatsResponse expected;
134 expected.mutable_rpcs_by_peer()->insert({{"peer1", 2}, {"peer2", 1}});
135 expected.mutable_metadatas_by_peer()->insert({
136 {"peer1", BuildMetadatas({
137 {{"K1", "v1", false},
138 {"k2", "v2", false},
139 {"k3", "v3", false},
140 {"K1", "t1", true},
141 {"k2", "t2", true}},
142 {},
143 })},
144 {"peer2",
145 BuildMetadatas(
146 {{{"K2", "v6", true}, {"k1", "v5", true}, {"k3", "v7", true}}})},
147 });
148 (*expected.mutable_rpcs_by_method())["UnaryCall"]
149 .mutable_rpcs_by_peer()
150 ->insert({{"peer1", 2}, {"peer2", 1}});
151 EXPECT_EQ(expected.DebugString(),
152 watcher.WaitForRpcStatsResponse(0).DebugString());
153 }
154
TEST(XdsStatsWatcherTest,WaitForRpcStatsResponseExcludesMetadata)155 TEST(XdsStatsWatcherTest, WaitForRpcStatsResponseExcludesMetadata) {
156 XdsStatsWatcher watcher(0, 3, {});
157 // RPC had metadata - but watcher should ignore it
158 watcher.RpcCompleted(BuildCallResult(0), "peer1",
159 {{"K1", "v1"}, {"k2", "v2"}, {"k3", "v3"}},
160 {{"K1", "t1"}, {"k2", "t2"}});
161 watcher.RpcCompleted(BuildCallResult(1), "peer1", {{"k1", "v4"}}, {});
162 watcher.RpcCompleted(BuildCallResult(2), "peer2", {},
163 {{"k1", "v5"}, {"k2", "v6"}, {"k3", "v7"}});
164 LoadBalancerStatsResponse expected;
165 expected.mutable_rpcs_by_peer()->insert({{"peer1", 2}, {"peer2", 1}});
166 (*expected.mutable_rpcs_by_method())["UnaryCall"]
167 .mutable_rpcs_by_peer()
168 ->insert({{"peer1", 2}, {"peer2", 1}});
169 EXPECT_EQ(watcher.WaitForRpcStatsResponse(0).DebugString(),
170 expected.DebugString());
171 }
172
173 } // namespace
174 } // namespace testing
175 } // namespace grpc
176
main(int argc,char ** argv)177 int main(int argc, char** argv) {
178 ::testing::InitGoogleTest(&argc, argv);
179 grpc::testing::TestEnvironment env(&argc, argv);
180 grpc_init();
181 auto result = RUN_ALL_TESTS();
182 grpc_shutdown();
183 return result;
184 }
185