diff options
author | Sree Kuchibhotla <sreek@google.com> | 2015-10-16 10:56:31 -0700 |
---|---|---|
committer | Sree Kuchibhotla <sreek@google.com> | 2015-10-19 16:15:51 -0700 |
commit | fbc376f935b1d779fb6595d1ef97c169ffd7a47c (patch) | |
tree | cad4fb841ceea72e0d6c95e88b8c835128ca7e84 /test/cpp | |
parent | b839682c0b9e938ccd9d66e6b3f92b6e7cd3ab65 (diff) |
Interop stress tests
Diffstat (limited to 'test/cpp')
-rw-r--r-- | test/cpp/interop/interop_client.cc | 47 | ||||
-rw-r--r-- | test/cpp/interop/interop_client.h | 2 | ||||
-rw-r--r-- | test/cpp/interop/stress_interop_client.cc | 161 | ||||
-rw-r--r-- | test/cpp/interop/stress_interop_client.h | 107 | ||||
-rw-r--r-- | test/cpp/interop/stress_test.cc | 222 |
5 files changed, 508 insertions, 31 deletions
diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc index 5169945762..fd01ad0666 100644 --- a/test/cpp/interop/interop_client.cc +++ b/test/cpp/interop/interop_client.cc @@ -83,7 +83,7 @@ CompressionType GetInteropCompressionTypeFromCompressionAlgorithm( } // namespace InteropClient::InteropClient(std::shared_ptr<Channel> channel) - : channel_(channel) {} + : channel_(channel), stub_(TestService::NewStub(channel)) {} void InteropClient::AssertOkOrPrintErrorStatus(const Status& s) { if (s.ok()) { @@ -96,13 +96,12 @@ void InteropClient::AssertOkOrPrintErrorStatus(const Status& s) { void InteropClient::DoEmpty() { gpr_log(GPR_INFO, "Sending an empty rpc..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); Empty request = Empty::default_instance(); Empty response = Empty::default_instance(); ClientContext context; - Status s = stub->EmptyCall(&context, request, &response); + Status s = stub_->EmptyCall(&context, request, &response); AssertOkOrPrintErrorStatus(s); gpr_log(GPR_INFO, "Empty rpc done."); @@ -111,7 +110,6 @@ void InteropClient::DoEmpty() { // Shared code to set large payload, make rpc and check response payload. void InteropClient::PerformLargeUnary(SimpleRequest* request, SimpleResponse* response) { - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; InteropClientContextInspector inspector(context); @@ -121,7 +119,7 @@ void InteropClient::PerformLargeUnary(SimpleRequest* request, grpc::string payload(kLargeRequestSize, '\0'); request->mutable_payload()->set_body(payload.c_str(), kLargeRequestSize); - Status s = stub->UnaryCall(&context, *request, response); + Status s = stub_->UnaryCall(&context, *request, response); // Compression related checks. GPR_ASSERT(request->response_compression() == @@ -187,11 +185,10 @@ void InteropClient::DoOauth2AuthToken(const grpc::string& username, SimpleResponse response; request.set_fill_username(true); request.set_fill_oauth_scope(true); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; - Status s = stub->UnaryCall(&context, request, &response); + Status s = stub_->UnaryCall(&context, request, &response); AssertOkOrPrintErrorStatus(s); GPR_ASSERT(!response.username().empty()); @@ -207,7 +204,6 @@ void InteropClient::DoPerRpcCreds(const grpc::string& json_key) { SimpleRequest request; SimpleResponse response; request.set_fill_username(true); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; std::chrono::seconds token_lifetime = std::chrono::hours(1); @@ -216,7 +212,7 @@ void InteropClient::DoPerRpcCreds(const grpc::string& json_key) { context.set_credentials(creds); - Status s = stub->UnaryCall(&context, request, &response); + Status s = stub_->UnaryCall(&context, request, &response); AssertOkOrPrintErrorStatus(s); GPR_ASSERT(!response.username().empty()); @@ -269,14 +265,13 @@ void InteropClient::DoLargeCompressedUnary() { void InteropClient::DoRequestStreaming() { gpr_log(GPR_INFO, "Sending request steaming rpc ..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; StreamingInputCallRequest request; StreamingInputCallResponse response; std::unique_ptr<ClientWriter<StreamingInputCallRequest>> stream( - stub->StreamingInputCall(&context, &response)); + stub_->StreamingInputCall(&context, &response)); int aggregated_payload_size = 0; for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) { @@ -295,7 +290,6 @@ void InteropClient::DoRequestStreaming() { void InteropClient::DoResponseStreaming() { gpr_log(GPR_INFO, "Receiving response steaming rpc ..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; StreamingOutputCallRequest request; @@ -305,7 +299,7 @@ void InteropClient::DoResponseStreaming() { } StreamingOutputCallResponse response; std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream( - stub->StreamingOutputCall(&context, request)); + stub_->StreamingOutputCall(&context, request)); unsigned int i = 0; while (stream->Read(&response)) { @@ -320,7 +314,6 @@ void InteropClient::DoResponseStreaming() { } void InteropClient::DoResponseCompressedStreaming() { - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); const CompressionType compression_types[] = {NONE, GZIP, DEFLATE}; const PayloadType payload_types[] = {COMPRESSABLE, UNCOMPRESSABLE, RANDOM}; @@ -348,7 +341,7 @@ void InteropClient::DoResponseCompressedStreaming() { StreamingOutputCallResponse response; std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream( - stub->StreamingOutputCall(&context, request)); + stub_->StreamingOutputCall(&context, request)); size_t k = 0; while (stream->Read(&response)) { @@ -401,7 +394,6 @@ void InteropClient::DoResponseCompressedStreaming() { void InteropClient::DoResponseStreamingWithSlowConsumer() { gpr_log(GPR_INFO, "Receiving response steaming rpc with slow consumer ..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; StreamingOutputCallRequest request; @@ -412,7 +404,7 @@ void InteropClient::DoResponseStreamingWithSlowConsumer() { } StreamingOutputCallResponse response; std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream( - stub->StreamingOutputCall(&context, request)); + stub_->StreamingOutputCall(&context, request)); int i = 0; while (stream->Read(&response)) { @@ -431,12 +423,11 @@ void InteropClient::DoResponseStreamingWithSlowConsumer() { void InteropClient::DoHalfDuplex() { gpr_log(GPR_INFO, "Sending half-duplex streaming rpc ..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest, StreamingOutputCallResponse>> - stream(stub->HalfDuplexCall(&context)); + stream(stub_->HalfDuplexCall(&context)); StreamingOutputCallRequest request; ResponseParameters* response_parameter = request.add_response_parameters(); @@ -461,12 +452,11 @@ void InteropClient::DoHalfDuplex() { void InteropClient::DoPingPong() { gpr_log(GPR_INFO, "Sending Ping Pong streaming rpc ..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest, StreamingOutputCallResponse>> - stream(stub->FullDuplexCall(&context)); + stream(stub_->FullDuplexCall(&context)); StreamingOutputCallRequest request; request.set_response_type(PayloadType::COMPRESSABLE); @@ -491,14 +481,13 @@ void InteropClient::DoPingPong() { void InteropClient::DoCancelAfterBegin() { gpr_log(GPR_INFO, "Sending request steaming rpc ..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; StreamingInputCallRequest request; StreamingInputCallResponse response; std::unique_ptr<ClientWriter<StreamingInputCallRequest>> stream( - stub->StreamingInputCall(&context, &response)); + stub_->StreamingInputCall(&context, &response)); gpr_log(GPR_INFO, "Trying to cancel..."); context.TryCancel(); @@ -509,12 +498,11 @@ void InteropClient::DoCancelAfterBegin() { void InteropClient::DoCancelAfterFirstResponse() { gpr_log(GPR_INFO, "Sending Ping Pong streaming rpc ..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest, StreamingOutputCallResponse>> - stream(stub->FullDuplexCall(&context)); + stream(stub_->FullDuplexCall(&context)); StreamingOutputCallRequest request; request.set_response_type(PayloadType::COMPRESSABLE); @@ -534,7 +522,6 @@ void InteropClient::DoCancelAfterFirstResponse() { void InteropClient::DoTimeoutOnSleepingServer() { gpr_log(GPR_INFO, "Sending Ping Pong streaming rpc with a short deadline..."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; std::chrono::system_clock::time_point deadline = @@ -542,7 +529,7 @@ void InteropClient::DoTimeoutOnSleepingServer() { context.set_deadline(deadline); std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest, StreamingOutputCallResponse>> - stream(stub->FullDuplexCall(&context)); + stream(stub_->FullDuplexCall(&context)); StreamingOutputCallRequest request; request.mutable_payload()->set_body(grpc::string(27182, '\0')); @@ -555,12 +542,11 @@ void InteropClient::DoTimeoutOnSleepingServer() { void InteropClient::DoEmptyStream() { gpr_log(GPR_INFO, "Starting empty_stream."); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest, StreamingOutputCallResponse>> - stream(stub->FullDuplexCall(&context)); + stream(stub_->FullDuplexCall(&context)); stream->WritesDone(); StreamingOutputCallResponse response; GPR_ASSERT(stream->Read(&response) == false); @@ -571,7 +557,6 @@ void InteropClient::DoEmptyStream() { void InteropClient::DoStatusWithMessage() { gpr_log(GPR_INFO, "Sending RPC with a request for status code 2 and message"); - std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_)); ClientContext context; SimpleRequest request; @@ -581,7 +566,7 @@ void InteropClient::DoStatusWithMessage() { grpc::string test_msg = "This is a test message"; requested_status->set_message(test_msg); - Status s = stub->UnaryCall(&context, request, &response); + Status s = stub_->UnaryCall(&context, request, &response); GPR_ASSERT(s.error_code() == grpc::StatusCode::UNKNOWN); GPR_ASSERT(s.error_message() == test_msg); diff --git a/test/cpp/interop/interop_client.h b/test/cpp/interop/interop_client.h index ebecd68c3f..1f13d5b971 100644 --- a/test/cpp/interop/interop_client.h +++ b/test/cpp/interop/interop_client.h @@ -39,6 +39,7 @@ #include <grpc/grpc.h> #include <grpc++/channel.h> #include "test/proto/messages.grpc.pb.h" +#include "test/proto/test.grpc.pb.h" namespace grpc { namespace testing { @@ -80,6 +81,7 @@ class InteropClient { void AssertOkOrPrintErrorStatus(const Status& s); std::shared_ptr<Channel> channel_; + std::unique_ptr<TestService::Stub> stub_; }; } // namespace testing diff --git a/test/cpp/interop/stress_interop_client.cc b/test/cpp/interop/stress_interop_client.cc new file mode 100644 index 0000000000..d1d0b0d4f3 --- /dev/null +++ b/test/cpp/interop/stress_interop_client.cc @@ -0,0 +1,161 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *is % allowed in string + */ + +#include <memory> +#include <string> +#include <vector> + +#include <grpc++/create_channel.h> + +#include "test/cpp/interop/interop_client.h" +#include "test/cpp/interop/stress_interop_client.h" + +namespace grpc { +namespace testing { + +using std::pair; +using std::string; +using std::vector; + +WeightedRandomTestSelector::WeightedRandomTestSelector( + const vector<pair<TestCaseType, int>>& tests) + : tests_(tests) { + total_weight_ = 0; + for (auto it = tests.begin(); it != tests.end(); it++) { + total_weight_ += it->second; + } +} + +// Returns a weighted-randomly selected test case based on the test weights +// passed in the constructror +TestCaseType WeightedRandomTestSelector::GetNextTest() const { + int random = 0; + TestCaseType selected_test = UNKNOWN_TEST; + + // Get a random number from [0 to the total_weight - 1] + random = rand() % total_weight_; + + int weight_sofar = 0; + for (auto it = tests_.begin(); it != tests_.end(); it++) { + weight_sofar += it->second; + if (random < weight_sofar) { + selected_test = it->first; + break; + } + } + + // It is a bug in the logic if no test is selected at this point + GPR_ASSERT(selected_test != UNKNOWN_TEST); + return selected_test; +} + +StressTestInteropClient::StressTestInteropClient( + int test_id, const string& server_address, + const WeightedRandomTestSelector& test_selector, long test_duration_secs, + long sleep_duration_ms) + : test_id_(test_id), + server_address_(server_address), + test_selector_(test_selector), + test_duration_secs_(test_duration_secs), + sleep_duration_ms_(sleep_duration_ms) { + // TODO(sreek): This will change once we add support for other tests + // that won't work with InsecureCredentials() + std::shared_ptr<Channel> channel( + CreateChannel(server_address, InsecureCredentials())); + interop_client_.reset(new InteropClient(channel)); +} + +void StressTestInteropClient::MainLoop() { + gpr_log(GPR_INFO, "Running test %d. ServerAddr: %s", test_id_, + server_address_.c_str()); + + gpr_timespec test_end_time = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_seconds(test_duration_secs_, GPR_TIMESPAN)); + + gpr_timespec current_time = gpr_now(GPR_CLOCK_REALTIME); + while (test_duration_secs_ < 0 || + gpr_time_cmp(current_time, test_end_time) < 0) { + // Select the test case to execute based on the weights and execute it + TestCaseType test_case = test_selector_.GetNextTest(); + gpr_log(GPR_INFO, "%d - Executing the test case %d", test_id_, test_case); + RunTest(test_case); + + // Sleep between successive calls if needed + if (sleep_duration_ms_ > 0) { + gpr_timespec sleep_time = gpr_time_add( + current_time, gpr_time_from_millis(sleep_duration_ms_, GPR_TIMESPAN)); + gpr_sleep_until(sleep_time); + } + + current_time = gpr_now(GPR_CLOCK_REALTIME); + } +} + +// TODO(sree): Add all interop tests +void StressTestInteropClient::RunTest(TestCaseType test_case) { + switch (test_case) { + case EMPTY_UNARY: { + interop_client_->DoEmpty(); + break; + } + case LARGE_UNARY: { + interop_client_->DoLargeUnary(); + break; + } + case LARGE_COMPRESSED_UNARY: { + interop_client_->DoLargeCompressedUnary(); + break; + } + case CLIENT_STREAMING: { + interop_client_->DoRequestStreaming(); + break; + } + case SERVER_STREAMING: { + interop_client_->DoResponseStreaming(); + break; + } + case EMPTY_STREAM: { + interop_client_->DoEmptyStream(); + break; + } + default: { + gpr_log(GPR_ERROR, "Invalid test case (%d)", test_case); + GPR_ASSERT(false); + break; + } + } +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/interop/stress_interop_client.h b/test/cpp/interop/stress_interop_client.h new file mode 100644 index 0000000000..36dfa7ed61 --- /dev/null +++ b/test/cpp/interop/stress_interop_client.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *is % allowed in string + */ + +#ifndef GRPC_TEST_CPP_STRESS_INTEROP_CLIENT_H +#define GRPC_TEST_CPP_STRESS_INTEROP_CLIENT_H + +#include <memory> +#include <string> +#include <vector> + +#include <grpc++/create_channel.h> + +#include "test/cpp/interop/interop_client.h" + +namespace grpc { +namespace testing { + +using std::pair; +using std::string; +using std::vector; + +// TODO(sreek): Add more test cases here in future +enum TestCaseType { + UNKNOWN_TEST = -1, + EMPTY_UNARY = 0, + LARGE_UNARY = 1, + LARGE_COMPRESSED_UNARY = 2, + CLIENT_STREAMING = 3, + SERVER_STREAMING = 4, + EMPTY_STREAM = 5 +}; + +const vector<pair<TestCaseType, string>> kTestCaseList = { + {EMPTY_UNARY, "empty_unary"}, + {LARGE_UNARY, "large_unary"}, + {LARGE_COMPRESSED_UNARY, "large_compressed_unary"}, + {CLIENT_STREAMING, "client_streaming"}, + {SERVER_STREAMING, "server_streaming"}, + {EMPTY_STREAM, "empty_stream"}}; + +class WeightedRandomTestSelector { + public: + // Takes a vector of <test_case, weight> pairs as the input + WeightedRandomTestSelector(const vector<pair<TestCaseType, int>>& tests); + + // Returns a weighted-randomly chosen test case based on the test cases and + // weights passed in the constructor + TestCaseType GetNextTest() const; + + private: + const vector<pair<TestCaseType, int>> tests_; + int total_weight_; +}; + +class StressTestInteropClient { + public: + StressTestInteropClient(int test_id, const string& server_address, + const WeightedRandomTestSelector& test_selector, + long test_duration_secs, long sleep_duration_ms); + + void MainLoop(); // The main function. Use this as the thread entry point. + + private: + void RunTest(TestCaseType test_case); + + int test_id_; + std::unique_ptr<InteropClient> interop_client_; + const string& server_address_; + const WeightedRandomTestSelector& test_selector_; + long test_duration_secs_; + long sleep_duration_ms_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_STRESS_INTEROP_CLIENT_H diff --git a/test/cpp/interop/stress_test.cc b/test/cpp/interop/stress_test.cc new file mode 100644 index 0000000000..596a8abadf --- /dev/null +++ b/test/cpp/interop/stress_test.cc @@ -0,0 +1,222 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *is % allowed in string + */ + +#include <memory> +#include <string> +#include <thread> +#include <utility> +#include <vector> + +#include <gflags/gflags.h> +#include <grpc/support/time.h> +#include <grpc++/create_channel.h> +#include <grpc++/grpc++.h> + +#include "test/cpp/interop/interop_client.h" +#include "test/cpp/interop/stress_interop_client.h" +#include "test/cpp/util/test_config.h" + +DEFINE_int32(sleep_duration_ms, 0, + "The duration (in millisec) between two" + " consecutive test calls (per server) issued by the server."); + +DEFINE_int32(test_duration_secs, -1, + "The length of time (in seconds) to run" + " the test. Enter -1 if the test should run continuously until" + " forcefully terminated."); + +DEFINE_string(server_addresses, "localhost:8080", + "The list of server" + " addresses in the format:\n" + " \"<name_1>:<port_1>,<name_2>:<port_1>...<name_N>:<port_N>\"\n" + " Note: <name> can be servername or IP address."); + +// TODO(sreek): Add more test cases here in future +DEFINE_string(test_cases, "", + "List of test cases to call along with the" + " relative weights in the following format:\n" + " \"<testcase_1:w_1>,<testcase_2:w_2>...<testcase_n:w_n>\"\n" + " The following testcases are currently supported:\n" + " empty_unary\n" + " large_unary\n" + " large_compressed_unary\n" + " client_streaming\n" + " server_streaming\n" + " empty_stream\n" + " Example: \"empty_unary:20,large_unary:10,empty_stream:70\"\n" + " The above will execute 'empty_unary', 20% of the time," + " 'large_unary', 10% of the time and 'empty_stream' the remaining" + " 70% of the time"); + +using std::make_pair; +using std::pair; +using std::string; +using std::thread; +using std::vector; + +using grpc::testing::kTestCaseList; +using grpc::testing::StressTestInteropClient; +using grpc::testing::TestCaseType; +using grpc::testing::WeightedRandomTestSelector; +using grpc::testing::UNKNOWN_TEST; + +TestCaseType GetTestTypeFromName(const string& test_name) { + TestCaseType test_case = UNKNOWN_TEST; + + for (auto it = kTestCaseList.begin(); it != kTestCaseList.end(); it++) { + if (test_name == it->second) { + test_case = it->first; + break; + } + } + + return test_case; +} + +// Converts a string of comma delimited tokens to a vector of tokens +bool ParseCommaDelimitedString(const string& comma_delimited_str, + vector<string>& tokens) { + size_t bpos = 0; + size_t epos = string::npos; + + while ((epos = comma_delimited_str.find(',', bpos)) != string::npos) { + tokens.emplace_back(comma_delimited_str.substr(bpos, epos - bpos)); + bpos = epos + 1; + } + + tokens.emplace_back(comma_delimited_str.substr(bpos)); // Last token + return true; +} + +// Input: Test case string "<testcase_name:weight>,<testcase_name:weight>...." +// Output: +// - Whether parsing was successful (return value) +// - Vector of (test_type_enum, weight) pairs returned via 'tests' parameter +bool ParseTestCasesString(const string& test_cases, + vector<pair<TestCaseType, int>>& tests) { + bool is_success = true; + + vector<string> tokens; + ParseCommaDelimitedString(test_cases, tokens); + + for (auto it = tokens.begin(); it != tokens.end(); it++) { + // Token is in the form <test_name>:<test_weight> + size_t colon_pos = it->find(':'); + if (colon_pos == string::npos) { + gpr_log(GPR_ERROR, "Error in parsing test case string: %s", it->c_str()); + is_success = false; + break; + } + + string test_name = it->substr(0, colon_pos); + int weight = std::stoi(it->substr(colon_pos + 1)); + TestCaseType test_case = GetTestTypeFromName(test_name); + + if (test_case == UNKNOWN_TEST) { + gpr_log(GPR_ERROR, "Unknown test case: %s", test_name.c_str()); + is_success = false; + break; + } + + tests.emplace_back(std::make_pair(test_case, weight)); + } + + return is_success; +} + +// For debugging purposes +void LogParameterInfo(const vector<string>& addresses, + const vector<pair<TestCaseType, int>>& tests) { + gpr_log(GPR_INFO, "server_addresses: %s", FLAGS_server_addresses.c_str()); + gpr_log(GPR_INFO, "test_cases : %s", FLAGS_test_cases.c_str()); + gpr_log(GPR_INFO, "sleep_duration_ms: %d", FLAGS_sleep_duration_ms); + gpr_log(GPR_INFO, "test_duration_secs: %d", FLAGS_test_duration_secs); + + int num = 0; + for (auto it = addresses.begin(); it != addresses.end(); it++) { + gpr_log(GPR_INFO, "%d:%s", ++num, it->c_str()); + } + + num = 0; + for (auto it = tests.begin(); it != tests.end(); it++) { + TestCaseType test_case = it->first; + int weight = it->second; + gpr_log(GPR_INFO, "%d. TestCaseType: %d, Weight: %d", ++num, test_case, + weight); + } +} + +int main(int argc, char** argv) { + grpc::testing::InitTest(&argc, &argv, true); + + srand(time(NULL)); + + // Parse the server addresses + vector<string> server_addresses; + ParseCommaDelimitedString(FLAGS_server_addresses, server_addresses); + + // Parse test cases and weights + if (FLAGS_test_cases.length() == 0) { + gpr_log(GPR_INFO, "Cannot start the tests. Test cases string is empty"); + return 1; + } + + vector<pair<TestCaseType, int>> tests; + if (!ParseTestCasesString(FLAGS_test_cases, tests)) { + gpr_log(GPR_ERROR, "Error in parsing test cases string %s ", + FLAGS_test_cases.c_str()); + return 1; + } + + LogParameterInfo(server_addresses, tests); + + WeightedRandomTestSelector test_selector(tests); + + gpr_log(GPR_INFO, "Starting test(s).."); + vector<thread> test_threads; + int thread_idx = 0; + for (auto it = server_addresses.begin(); it != server_addresses.end(); it++) { + StressTestInteropClient* client = new StressTestInteropClient( + ++thread_idx, *it, test_selector, FLAGS_test_duration_secs, + FLAGS_sleep_duration_ms); + + test_threads.emplace_back( + thread(&StressTestInteropClient::MainLoop, client)); + } + + for (auto it = test_threads.begin(); it != test_threads.end(); it++) { + it->join(); + } + + return 0; +} |