diff options
author | Sree Kuchibhotla <sreek@google.com> | 2018-09-28 16:38:53 -0700 |
---|---|---|
committer | Sree Kuchibhotla <sreek@google.com> | 2018-09-28 16:38:53 -0700 |
commit | 4f940918b126dc9348b1f2e21d66fc6aeae4b193 (patch) | |
tree | 0ff0f7f25d52c26b9f6053b54500ebbb6df74602 | |
parent | 82b2e2977d5cd144c6e2350d55b6f928fd8f6ce7 (diff) | |
parent | f15e3bbc357bd542d83d6b478621bc1f0c3b1f8c (diff) |
Merge branch 'master' into sreek-pe-doc
133 files changed, 1657 insertions, 176 deletions
diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 0000000000..119e4840be --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,2 @@ +daysUntilLock: 90 +lockComment: false diff --git a/doc/core/grpc-client-server-polling-engine-usage.md b/doc/core/grpc-client-server-polling-engine-usage.md new file mode 100644 index 0000000000..3a560e71a8 --- /dev/null +++ b/doc/core/grpc-client-server-polling-engine-usage.md @@ -0,0 +1,32 @@ +# Polling Engine Usage on gRPC client and Server + +_Author: Sree Kuchibhotla (@sreecha) - Sep 2018_ + + +This document talks about how polling engine is used in gRPC core (both on client and server code paths). + +## gRPC client + +### Relation between Call, Channel (sub-channels), Completion queue, `grpc_pollset` +- A gRPC Call is tied to a channel (more specifically a sub-channel) and a completion queue for the lifetime of the call. +- Once a _sub-channel_ is picked for the call, the file-descriptor (socket fd in case of TCP channels) is added to the pollset corresponding to call's completion queue. (Recall that as per [grpc-cq](grpc-cq.md), a completion queue has a pollset by default) + +![image](../images/grpc-call-channel-cq.png) + + +### Making progress on Async `connect()` on sub-channels (`grpc_pollset_set` usecase) +- A gRPC channel is created between a client and a 'target'. The 'target' may resolve in to one or more backend servers. +- A sub-channel is the 'connection' from a client to the backend server +- While establishing sub-cannels (i.e connections) to the backends, gRPC issues async [`connect()`](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/iomgr/tcp_client_posix.cc#L296) calls which may not complete right away. When the `connect()` eventually succeeds, the socket fd is make 'writable' + - This means that the polling engine must be monitoring all these sub-channel `fd`s for writable events and we need to make sure there is a polling thread that monitors all these fds + - To accomplish this, the `grpc_pollset_set` is used the following way (see picture below) + +![image](../images/grpc-client-lb-pss.png) + +## gRPC server + +- The listening fd (i.e., the socket fd corresponding to the server listening port) is added to each of the server completion queues. Note that in gRPC we use SO_REUSEPORT option and create multiple listening fds but all of them map to the same listening port +- A new incoming channel is assigned to some server completion queue picked randomly (note that we currently [round-robin](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/iomgr/tcp_server_posix.cc#L231) over the server completion queues) + +![image](../images/grpc-server-cq-fds.png) + diff --git a/doc/core/grpc-cq.md b/doc/core/grpc-cq.md new file mode 100644 index 0000000000..b485c35456 --- /dev/null +++ b/doc/core/grpc-cq.md @@ -0,0 +1,64 @@ +# gRPC Completion Queue + +_Author: Sree Kuchibhotla (@sreecha) - Sep 2018_ + +Code: [completion_queue.cc](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/surface/completion_queue.cc) + +This document gives an overview of completion queue architecture and focuses mainly on the interaction between completion queue and the Polling engine layer. + +## Completion queue attributes +Completion queue has two attributes + + - Completion_type: + - GRPC_CQ_NEXT: grpc_completion_queue_next() can be called (but not grpc_completion_queue_pluck()) + - GRPC_CQ_PLUCK: grpc_completion_queue_pluck() can be called (but not grpc_completion_queue_next()) + - GRPC_CQ_CALLBACK: The tags in the queue are function pointers to callbacks. Also, neither next() nor pluck() can be called on this + + - Polling_type: + - GRPC_CQ_NON_POLLING: Threads calling completion_queue_next/pluck do not do any polling + - GRPC_CQ_DEFAULT_POLLING: Threads calling completion_queue_next/pluck do polling + - GRPC_CQ_NON_LISTENING: Functionally similar to default polling except for a boolean attribute that states that the cq is non-listening. This is used by the grpc-server code to not associate any listening sockets with this completion-queue’s pollset + + +## Details + +![image](../images/grpc-cq.png) + + +### **grpc\_completion\_queue\_next()** & **grpc_completion_queue_pluck()** APIS + + +``` C++ +grpc_completion_queue_next(cq, deadline)/pluck(cq, deadline, tag) { + while(true) { + \\ 1. If an event is queued in the completion queue, dequeue and return + \\ (in case of pluck() dequeue only if the tag is the one we are interested in) + + \\ 2. If completion queue shutdown return + + \\ 3. In case of pluck, add (tag, worker) pair to the tag<->worker map on the cq + + \\ 4. Call grpc_pollset_work(cq’s-pollset, deadline) to do polling + \\ Note that if this function found some fds to be readable/writable/error, + \\ it would have scheduled those closures (which may queue completion events + \\ on SOME completion queue - not necessarily this one) + } +} +``` + +### Queuing a completion event (i.e., "tag") + +``` C++ +grpc_cq_end_op(cq, tag) { + \\ 1. Queue the tag in the event queue + + \\ 2. Find the pollset corresponding to the completion queue + \\ (i) If the cq is of type GRPC_CQ_NEXT, then KICK ANY worker + \\ i.e., call grpc_pollset_kick(pollset, nullptr) + \\ (ii) If the cq is of type GRPC_CQ_PLUCK, then search the tag<->worker + \\ map on the completion queue to find the worker. Then specifically + \\ kick that worker i.e call grpc_pollset_kick(pollset, worker) +} + +``` + diff --git a/doc/images/grpc-call-channel-cq.png b/doc/images/grpc-call-channel-cq.png Binary files differnew file mode 100644 index 0000000000..d73b987ee9 --- /dev/null +++ b/doc/images/grpc-call-channel-cq.png diff --git a/doc/images/grpc-client-lb-pss.png b/doc/images/grpc-client-lb-pss.png Binary files differnew file mode 100644 index 0000000000..188e3654ec --- /dev/null +++ b/doc/images/grpc-client-lb-pss.png diff --git a/doc/images/grpc-cq.png b/doc/images/grpc-cq.png Binary files differnew file mode 100644 index 0000000000..2d9e095862 --- /dev/null +++ b/doc/images/grpc-cq.png diff --git a/doc/images/grpc-server-cq-fds.png b/doc/images/grpc-server-cq-fds.png Binary files differnew file mode 100644 index 0000000000..e52bfac81c --- /dev/null +++ b/doc/images/grpc-server-cq-fds.png diff --git a/examples/python/interceptors/default_value/default_value_client_interceptor.py b/examples/python/interceptors/default_value/default_value_client_interceptor.py index c549f2b861..c935b95491 100644 --- a/examples/python/interceptors/default_value/default_value_client_interceptor.py +++ b/examples/python/interceptors/default_value/default_value_client_interceptor.py @@ -13,8 +13,6 @@ # limitations under the License. """Interceptor that adds headers to outgoing requests.""" -import collections - import grpc @@ -77,6 +77,7 @@ EXPORTS grpc_channelz_get_servers grpc_channelz_get_channel grpc_channelz_get_subchannel + grpc_channelz_get_socket grpc_insecure_channel_create_from_fd grpc_server_add_insecure_channel_from_fd grpc_use_signal diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 787d6ae6d7..a9beee1c9e 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -511,6 +511,10 @@ GRPCAPI char* grpc_channelz_get_channel(intptr_t channel_id); is allocated and must be freed by the application. */ GRPCAPI char* grpc_channelz_get_subchannel(intptr_t subchannel_id); +/* Returns a single Socket, or else a NOT_FOUND code. The returned string + is allocated and must be freed by the application. */ +GRPCAPI char* grpc_channelz_get_socket(intptr_t socket_id); + #ifdef __cplusplus } #endif diff --git a/src/core/ext/filters/client_channel/client_channel_channelz.cc b/src/core/ext/filters/client_channel/client_channel_channelz.cc index 7e8f59bcd3..b66c920b90 100644 --- a/src/core/ext/filters/client_channel/client_channel_channelz.cc +++ b/src/core/ext/filters/client_channel/client_channel_channelz.cc @@ -166,6 +166,17 @@ grpc_json* SubchannelNode::RenderJson() { } // ask CallCountingHelper to populate trace and call count data. call_counter_.PopulateCallCounts(json); + json = top_level_json; + // populate the child socket. + intptr_t socket_uuid = grpc_subchannel_get_child_socket_uuid(subchannel_); + if (socket_uuid != 0) { + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false); + json_iterator = grpc_json_create_child(json_iterator, array_parent, nullptr, + nullptr, GRPC_JSON_OBJECT, false); + grpc_json_add_number_string_child(json_iterator, nullptr, "socketId", + socket_uuid); + } return top_level_json; } diff --git a/src/core/ext/filters/client_channel/connector.h b/src/core/ext/filters/client_channel/connector.h index 556594929c..ea34dcdab5 100644 --- a/src/core/ext/filters/client_channel/connector.h +++ b/src/core/ext/filters/client_channel/connector.h @@ -47,6 +47,9 @@ typedef struct { /** channel arguments (to be passed to the filters) */ grpc_channel_args* channel_args; + + /** socket uuid of the connected transport. 0 if not available */ + intptr_t socket_uuid; } grpc_connect_out_args; struct grpc_connector_vtable { diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.h b/src/core/ext/filters/client_channel/lb_policy_factory.h index c07792d8a7..62bdbf2689 100644 --- a/src/core/ext/filters/client_channel/lb_policy_factory.h +++ b/src/core/ext/filters/client_channel/lb_policy_factory.h @@ -70,16 +70,14 @@ grpc_lb_addresses* grpc_lb_addresses_create( grpc_lb_addresses* grpc_lb_addresses_copy(const grpc_lb_addresses* addresses); /** Sets the value of the address at index \a index of \a addresses. - * \a address is a socket address of length \a address_len. - * Takes ownership of \a balancer_name. */ + * \a address is a socket address of length \a address_len. */ void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index, const void* address, size_t address_len, bool is_balancer, const char* balancer_name, void* user_data); /** Sets the value of the address at index \a index of \a addresses from \a uri. - * Returns true upon success, false otherwise. Takes ownership of \a - * balancer_name. */ + * Returns true upon success, false otherwise. */ bool grpc_lb_addresses_set_address_from_uri(grpc_lb_addresses* addresses, size_t index, const grpc_uri* uri, bool is_balancer, diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc index 57d0b3759f..2847f4bdc1 100644 --- a/src/core/ext/filters/client_channel/subchannel.cc +++ b/src/core/ext/filters/client_channel/subchannel.cc @@ -411,6 +411,14 @@ grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node( return subchannel->channelz_subchannel.get(); } +intptr_t grpc_subchannel_get_child_socket_uuid(grpc_subchannel* subchannel) { + if (subchannel->connected_subchannel != nullptr) { + return subchannel->connected_subchannel->socket_uuid(); + } else { + return 0; + } +} + static void continue_connect_locked(grpc_subchannel* c) { grpc_connect_in_args args; args.interested_parties = c->pollset_set; @@ -621,6 +629,7 @@ static bool publish_transport_locked(grpc_subchannel* c) { GRPC_ERROR_UNREF(error); return false; } + intptr_t socket_uuid = c->connecting_result.socket_uuid; memset(&c->connecting_result, 0, sizeof(c->connecting_result)); /* initialize state watcher */ @@ -641,7 +650,7 @@ static bool publish_transport_locked(grpc_subchannel* c) { /* publish */ c->connected_subchannel.reset(grpc_core::New<grpc_core::ConnectedSubchannel>( - stk, c->channelz_subchannel.get())); + stk, c->channelz_subchannel.get(), socket_uuid)); gpr_log(GPR_INFO, "New connected subchannel at %p for subchannel %p", c->connected_subchannel.get(), c); @@ -811,10 +820,11 @@ namespace grpc_core { ConnectedSubchannel::ConnectedSubchannel( grpc_channel_stack* channel_stack, - channelz::SubchannelNode* channelz_subchannel) + channelz::SubchannelNode* channelz_subchannel, intptr_t socket_uuid) : RefCountedWithTracing<ConnectedSubchannel>(&grpc_trace_stream_refcount), channel_stack_(channel_stack), - channelz_subchannel_(channelz_subchannel) {} + channelz_subchannel_(channelz_subchannel), + socket_uuid_(socket_uuid) {} ConnectedSubchannel::~ConnectedSubchannel() { GRPC_CHANNEL_STACK_UNREF(channel_stack_, "connected_subchannel_dtor"); diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h index 84febb5204..699f93a8e7 100644 --- a/src/core/ext/filters/client_channel/subchannel.h +++ b/src/core/ext/filters/client_channel/subchannel.h @@ -86,7 +86,8 @@ class ConnectedSubchannel : public RefCountedWithTracing<ConnectedSubchannel> { }; explicit ConnectedSubchannel(grpc_channel_stack* channel_stack, - channelz::SubchannelNode* channelz_subchannel); + channelz::SubchannelNode* channelz_subchannel, + intptr_t socket_uuid); ~ConnectedSubchannel(); grpc_channel_stack* channel_stack() { return channel_stack_; } @@ -98,12 +99,15 @@ class ConnectedSubchannel : public RefCountedWithTracing<ConnectedSubchannel> { channelz::SubchannelNode* channelz_subchannel() { return channelz_subchannel_; } + intptr_t socket_uuid() { return socket_uuid_; } private: grpc_channel_stack* channel_stack_; // backpointer to the channelz node in this connected subchannel's // owning subchannel. channelz::SubchannelNode* channelz_subchannel_; + // uuid of this subchannel's socket. 0 if this subchannel is not connected. + const intptr_t socket_uuid_; }; } // namespace grpc_core @@ -126,6 +130,8 @@ void grpc_subchannel_call_unref( grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node( grpc_subchannel* subchannel); +intptr_t grpc_subchannel_get_child_socket_uuid(grpc_subchannel* subchannel); + /** Returns a pointer to the parent data associated with \a subchannel_call. The data will be of the size specified in \a parent_data_size field of the args passed to \a grpc_connected_subchannel_create_call(). */ diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/src/core/ext/transport/chttp2/client/chttp2_connector.cc index e7522ffba8..0ac84032fd 100644 --- a/src/core/ext/transport/chttp2/client/chttp2_connector.cc +++ b/src/core/ext/transport/chttp2/client/chttp2_connector.cc @@ -117,6 +117,8 @@ static void on_handshake_done(void* arg, grpc_error* error) { c->args.interested_parties); c->result->transport = grpc_create_chttp2_transport(args->args, args->endpoint, true); + c->result->socket_uuid = + grpc_chttp2_transport_get_socket_uuid(c->result->transport); GPR_ASSERT(c->result->transport); // TODO(roth): We ideally want to wait until we receive HTTP/2 // settings from the server before we consider the connection diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 26cad2cc9a..776c15138b 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -157,6 +157,10 @@ bool g_flow_control_enabled = true; static void destruct_transport(grpc_chttp2_transport* t) { size_t i; + if (t->channelz_socket != nullptr) { + t->channelz_socket.reset(); + } + grpc_endpoint_destroy(t->ep); grpc_slice_buffer_destroy_internal(&t->qbuf); @@ -335,6 +339,10 @@ static bool read_channel_args(grpc_chttp2_transport* t, GRPC_ARG_OPTIMIZATION_TARGET, channel_args->args[i].value.string); } + } else if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_ENABLE_CHANNELZ)) { + t->channelz_socket = + grpc_core::MakeRefCounted<grpc_core::channelz::SocketNode>(); } else { static const struct { const char* channel_arg_name; @@ -720,6 +728,14 @@ static void destroy_stream_locked(void* sp, grpc_error* error) { grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(sp); grpc_chttp2_transport* t = s->t; + if (t->channelz_socket != nullptr) { + if ((t->is_client && s->eos_received) || (!t->is_client && s->eos_sent)) { + t->channelz_socket->RecordStreamSucceeded(); + } else { + t->channelz_socket->RecordStreamFailed(); + } + } + GPR_ASSERT((s->write_closed && s->read_closed) || s->id == 0); if (s->id != 0) { GPR_ASSERT(grpc_chttp2_stream_map_find(&t->stream_map, s->id) == nullptr); @@ -1407,6 +1423,9 @@ static void perform_stream_op_locked(void* stream_op, } if (op->send_initial_metadata) { + if (t->is_client && t->channelz_socket != nullptr) { + t->channelz_socket->RecordStreamStartedFromLocal(); + } GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA(); GPR_ASSERT(s->send_initial_metadata_finished == nullptr); on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; @@ -1492,6 +1511,7 @@ static void perform_stream_op_locked(void* stream_op, if (op->send_message) { GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE(); + t->num_messages_in_next_write++; GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE( op->payload->send_message.send_message->length()); on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; @@ -2707,6 +2727,9 @@ static void start_keepalive_ping_locked(void* arg, grpc_error* error) { if (error != GRPC_ERROR_NONE) { return; } + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordKeepaliveSent(); + } GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog"); grpc_timer_init(&t->keepalive_watchdog_timer, grpc_core::ExecCtx::Get()->Now() + t->keepalive_timeout, @@ -3147,6 +3170,16 @@ static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream), static const grpc_transport_vtable* get_vtable(void) { return &vtable; } +intptr_t grpc_chttp2_transport_get_socket_uuid(grpc_transport* transport) { + grpc_chttp2_transport* t = + reinterpret_cast<grpc_chttp2_transport*>(transport); + if (t->channelz_socket != nullptr) { + return t->channelz_socket->uuid(); + } else { + return 0; + } +} + grpc_transport* grpc_create_chttp2_transport( const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client) { grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>( diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h index 9d55b3f4b0..e5872fee43 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h @@ -34,6 +34,8 @@ extern bool g_flow_control_enabled; grpc_transport* grpc_create_chttp2_transport( const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client); +intptr_t grpc_chttp2_transport_get_socket_uuid(grpc_transport* transport); + /// Takes ownership of \a read_buffer, which (if non-NULL) contains /// leftover bytes previously read from the endpoint (e.g., by handshakers). /// If non-null, \a notify_on_receive_settings will be scheduled when diff --git a/src/core/ext/transport/chttp2/transport/frame_data.cc b/src/core/ext/transport/chttp2/transport/frame_data.cc index f8f06f6789..933b32c03c 100644 --- a/src/core/ext/transport/chttp2/transport/frame_data.cc +++ b/src/core/ext/transport/chttp2/transport/frame_data.cc @@ -62,6 +62,7 @@ grpc_error* grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser* parser, if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) { s->received_last_frame = true; + s->eos_received = true; } else { s->received_last_frame = false; } @@ -191,6 +192,9 @@ grpc_error* grpc_deframe_unprocessed_incoming_frames( GPR_ASSERT(stream_out != nullptr); GPR_ASSERT(p->parsing_frame == nullptr); p->frame_size |= (static_cast<uint32_t>(*cur)); + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordMessageReceived(); + } p->state = GRPC_CHTTP2_DATA_FRAME; ++cur; message_flags = 0; diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 6b5309bab4..ff26dd9255 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -36,6 +36,7 @@ #include "src/core/ext/transport/chttp2/transport/hpack_parser.h" #include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" #include "src/core/ext/transport/chttp2/transport/stream_map.h" +#include "src/core/lib/channel/channelz.h" #include "src/core/lib/compression/stream_compression.h" #include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/iomgr/combiner.h" @@ -471,6 +472,9 @@ struct grpc_chttp2_transport { bool keepalive_permit_without_calls; /** keep-alive state machine state */ grpc_chttp2_keepalive_state keepalive_state; + + grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> channelz_socket; + uint32_t num_messages_in_next_write; }; typedef enum { @@ -534,6 +538,10 @@ struct grpc_chttp2_stream { /** Has trailing metadata been received. */ bool received_trailing_metadata; + /* have we sent or received the EOS bit? */ + bool eos_received; + bool eos_sent; + /** the error that resulted in this stream being read-closed */ grpc_error* read_closed_error; /** the error that resulted in this stream being write-closed */ diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc index 8e9aa613a6..f532b084c9 100644 --- a/src/core/ext/transport/chttp2/transport/parsing.cc +++ b/src/core/ext/transport/chttp2/transport/parsing.cc @@ -623,6 +623,9 @@ static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t, gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted")); return init_skip_frame_parser(t, 1); } + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordStreamStartedFromRemote(); + } } else { t->incoming_stream = s; } @@ -636,6 +639,9 @@ static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t, } t->parser = grpc_chttp2_header_parser_parse; t->parser_data = &t->hpack_parser; + if (t->header_eof) { + s->eos_received = true; + } switch (s->header_frames_received) { case 0: if (t->is_client && t->header_eof) { diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc index 8b73b01dea..d533989444 100644 --- a/src/core/ext/transport/chttp2/transport/writing.cc +++ b/src/core/ext/transport/chttp2/transport/writing.cc @@ -569,6 +569,7 @@ class StreamWriteContext { void SentLastFrame() { s_->send_trailing_metadata = nullptr; s_->sent_trailing_metadata = true; + s_->eos_sent = true; if (!t_->is_client && !s_->read_closed) { grpc_slice_buffer_add( @@ -632,6 +633,11 @@ void grpc_chttp2_end_write(grpc_chttp2_transport* t, grpc_error* error) { GPR_TIMER_SCOPE("grpc_chttp2_end_write", 0); grpc_chttp2_stream* s; + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordMessagesSent(t->num_messages_in_next_write); + } + t->num_messages_in_next_write = 0; + while (grpc_chttp2_list_pop_writing_stream(t, &s)) { if (s->sending_bytes != 0) { update_list(t, s, static_cast<int64_t>(s->sending_bytes), diff --git a/src/core/lib/channel/channelz.cc b/src/core/lib/channel/channelz.cc index 375cf25cc6..339c827525 100644 --- a/src/core/lib/channel/channelz.cc +++ b/src/core/lib/channel/channelz.cc @@ -62,7 +62,7 @@ CallCountingHelper::CallCountingHelper() { CallCountingHelper::~CallCountingHelper() {} void CallCountingHelper::RecordCallStarted() { - gpr_atm_no_barrier_fetch_add(&calls_started_, (gpr_atm)1); + gpr_atm_no_barrier_fetch_add(&calls_started_, static_cast<gpr_atm>(1)); gpr_atm_no_barrier_store(&last_call_started_millis_, (gpr_atm)ExecCtx::Get()->Now()); } @@ -81,11 +81,13 @@ void CallCountingHelper::PopulateCallCounts(grpc_json* json) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "callsFailed", calls_failed_); } - gpr_timespec ts = - grpc_millis_to_timespec(last_call_started_millis_, GPR_CLOCK_REALTIME); - json_iterator = - grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp", - gpr_format_timespec(ts), GRPC_JSON_STRING, true); + if (calls_started_ != 0) { + gpr_timespec ts = + grpc_millis_to_timespec(last_call_started_millis_, GPR_CLOCK_REALTIME); + json_iterator = + grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } } ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes, @@ -180,7 +182,103 @@ grpc_json* ServerNode::RenderJson() { } // ask CallCountingHelper to populate trace and call count data. call_counter_.PopulateCallCounts(json); + return top_level_json; +} + +SocketNode::SocketNode() : BaseNode(EntityType::kSocket) {} + +void SocketNode::RecordStreamStartedFromLocal() { + gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1)); + gpr_atm_no_barrier_store(&last_local_stream_created_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +void SocketNode::RecordStreamStartedFromRemote() { + gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1)); + gpr_atm_no_barrier_store(&last_remote_stream_created_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +void SocketNode::RecordMessagesSent(uint32_t num_sent) { + gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast<gpr_atm>(num_sent)); + gpr_atm_no_barrier_store(&last_message_sent_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +void SocketNode::RecordMessageReceived() { + gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast<gpr_atm>(1)); + gpr_atm_no_barrier_store(&last_message_received_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +grpc_json* SocketNode::RenderJson() { + // We need to track these three json objects to build our object + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + // create and fill the ref child + json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_add_number_string_child(json, json_iterator, + "socketId", uuid()); + // reset json iterators to top level object json = top_level_json; + json_iterator = nullptr; + // create and fill the data child. + grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, + GRPC_JSON_OBJECT, false); + json = data; + json_iterator = nullptr; + gpr_timespec ts; + if (streams_started_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "streamsStarted", streams_started_); + if (last_local_stream_created_millis_ != 0) { + ts = grpc_millis_to_timespec(last_local_stream_created_millis_, + GPR_CLOCK_REALTIME); + json_iterator = grpc_json_create_child( + json_iterator, json, "lastLocalStreamCreatedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + if (last_remote_stream_created_millis_ != 0) { + ts = grpc_millis_to_timespec(last_remote_stream_created_millis_, + GPR_CLOCK_REALTIME); + json_iterator = grpc_json_create_child( + json_iterator, json, "lastRemoteStreamCreatedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + } + if (streams_succeeded_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "streamsSucceeded", streams_succeeded_); + } + if (streams_failed_) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "streamsFailed", streams_failed_); + } + if (messages_sent_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "messagesSent", messages_sent_); + ts = grpc_millis_to_timespec(last_message_sent_millis_, GPR_CLOCK_REALTIME); + json_iterator = + grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + if (messages_received_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "messagesReceived", messages_received_); + ts = grpc_millis_to_timespec(last_message_received_millis_, + GPR_CLOCK_REALTIME); + json_iterator = grpc_json_create_child( + json_iterator, json, "lastMessageReceivedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + if (keepalives_sent_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "keepAlivesSent", keepalives_sent_); + } return top_level_json; } diff --git a/src/core/lib/channel/channelz.h b/src/core/lib/channel/channelz.h index 9be256147b..b7ae101238 100644 --- a/src/core/lib/channel/channelz.h +++ b/src/core/lib/channel/channelz.h @@ -92,10 +92,10 @@ class CallCountingHelper { void RecordCallStarted(); void RecordCallFailed() { - gpr_atm_no_barrier_fetch_add(&calls_failed_, (gpr_atm(1))); + gpr_atm_no_barrier_fetch_add(&calls_failed_, static_cast<gpr_atm>(1)); } void RecordCallSucceeded() { - gpr_atm_no_barrier_fetch_add(&calls_succeeded_, (gpr_atm(1))); + gpr_atm_no_barrier_fetch_add(&calls_succeeded_, static_cast<gpr_atm>(1)); } // Common rendering of the call count data and last_call_started_timestamp. @@ -197,11 +197,39 @@ class ServerNode : public BaseNode { }; // Handles channelz bookkeeping for sockets -// TODO(ncteisen): implement in subsequent PR. class SocketNode : public BaseNode { public: - SocketNode() : BaseNode(EntityType::kSocket) {} + SocketNode(); ~SocketNode() override {} + + grpc_json* RenderJson() override; + + void RecordStreamStartedFromLocal(); + void RecordStreamStartedFromRemote(); + void RecordStreamSucceeded() { + gpr_atm_no_barrier_fetch_add(&streams_succeeded_, static_cast<gpr_atm>(1)); + } + void RecordStreamFailed() { + gpr_atm_no_barrier_fetch_add(&streams_failed_, static_cast<gpr_atm>(1)); + } + void RecordMessagesSent(uint32_t num_sent); + void RecordMessageReceived(); + void RecordKeepaliveSent() { + gpr_atm_no_barrier_fetch_add(&keepalives_sent_, static_cast<gpr_atm>(1)); + } + + private: + gpr_atm streams_started_ = 0; + gpr_atm streams_succeeded_ = 0; + gpr_atm streams_failed_ = 0; + gpr_atm messages_sent_ = 0; + gpr_atm messages_received_ = 0; + gpr_atm keepalives_sent_ = 0; + gpr_atm last_local_stream_created_millis_ = 0; + gpr_atm last_remote_stream_created_millis_ = 0; + gpr_atm last_message_sent_millis_ = 0; + gpr_atm last_message_received_millis_ = 0; + UniquePtr<char> peer_string_; }; // Creation functions diff --git a/src/core/lib/channel/channelz_registry.cc b/src/core/lib/channel/channelz_registry.cc index adc7b6ba44..841f1c6104 100644 --- a/src/core/lib/channel/channelz_registry.cc +++ b/src/core/lib/channel/channelz_registry.cc @@ -197,3 +197,21 @@ char* grpc_channelz_get_subchannel(intptr_t subchannel_id) { grpc_json_destroy(top_level_json); return json_str; } + +char* grpc_channelz_get_socket(intptr_t socket_id) { + grpc_core::channelz::BaseNode* socket_node = + grpc_core::channelz::ChannelzRegistry::Get(socket_id); + if (socket_node == nullptr || + socket_node->type() != + grpc_core::channelz::BaseNode::EntityType::kSocket) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* socket_json = socket_node->RenderJson(); + socket_json->key = "socket"; + grpc_json_link_child(json, socket_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc index 13bc69ffb6..146a539027 100644 --- a/src/core/lib/iomgr/error.cc +++ b/src/core/lib/iomgr/error.cc @@ -454,7 +454,7 @@ typedef struct { grpc_status_code code; const char* msg; } special_error_status_map; -static special_error_status_map error_status_map[] = { +static const special_error_status_map error_status_map[] = { {GRPC_ERROR_NONE, GRPC_STATUS_OK, ""}, {GRPC_ERROR_CANCELLED, GRPC_STATUS_CANCELLED, "Cancelled"}, {GRPC_ERROR_OOM, GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"}, @@ -463,15 +463,13 @@ static special_error_status_map error_status_map[] = { bool grpc_error_get_int(grpc_error* err, grpc_error_ints which, intptr_t* p) { GPR_TIMER_SCOPE("grpc_error_get_int", 0); if (grpc_error_is_special(err)) { - if (which == GRPC_ERROR_INT_GRPC_STATUS) { - for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { - if (error_status_map[i].error == err) { - if (p != nullptr) *p = error_status_map[i].code; - return true; - } + for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { + if (error_status_map[i].error == err) { + if (which != GRPC_ERROR_INT_GRPC_STATUS) return false; + if (p != nullptr) *p = error_status_map[i].code; + return true; } } - return false; } uint8_t slot = err->ints[which]; if (slot != UINT8_MAX) { @@ -492,15 +490,13 @@ grpc_error* grpc_error_set_str(grpc_error* src, grpc_error_strs which, bool grpc_error_get_str(grpc_error* err, grpc_error_strs which, grpc_slice* str) { if (grpc_error_is_special(err)) { - if (which == GRPC_ERROR_STR_GRPC_MESSAGE) { - for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { - if (error_status_map[i].error == err) { - *str = grpc_slice_from_static_string(error_status_map[i].msg); - return true; - } + for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { + if (error_status_map[i].error == err) { + if (which != GRPC_ERROR_STR_GRPC_MESSAGE) return false; + *str = grpc_slice_from_static_string(error_status_map[i].msg); + return true; } } - return false; } uint8_t slot = err->strs[which]; if (slot != UINT8_MAX) { diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc index aa5016bd8f..6ef889b0fe 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.cc +++ b/src/core/lib/iomgr/ev_epoll1_linux.cc @@ -273,6 +273,10 @@ static gpr_mu fork_fd_list_mu; static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } static void fd_global_shutdown(void) { + // TODO(guantaol): We don't have a reasonable explanation about this + // lock()/unlock() pattern. It can be a valid barrier if there is at most one + // pending lock() at this point. Otherwise, there is still a possibility of + // use-after-free race. Need to reason about the code and/or clean it up. gpr_mu_lock(&fd_freelist_mu); gpr_mu_unlock(&fd_freelist_mu); while (fd_freelist != nullptr) { diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc index b082634af1..06a382c556 100644 --- a/src/core/lib/iomgr/ev_epollex_linux.cc +++ b/src/core/lib/iomgr/ev_epollex_linux.cc @@ -403,6 +403,10 @@ static void unref_by(grpc_fd* fd, int n) { static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } static void fd_global_shutdown(void) { + // TODO(guantaol): We don't have a reasonable explanation about this + // lock()/unlock() pattern. It can be a valid barrier if there is at most one + // pending lock() at this point. Otherwise, there is still a possibility of + // use-after-free race. Need to reason about the code and/or clean it up. gpr_mu_lock(&fd_freelist_mu); gpr_mu_unlock(&fd_freelist_mu); while (fd_freelist != nullptr) { diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc index 8a7dc7b004..764fb08142 100644 --- a/src/core/lib/iomgr/ev_posix.cc +++ b/src/core/lib/iomgr/ev_posix.cc @@ -395,4 +395,6 @@ void grpc_pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) { g_event_engine->pollset_set_del_fd(pollset_set, fd); } +void grpc_use_signal(int signum) {} + #endif // GRPC_POSIX_SOCKET_EV diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc index 008d37119a..aba5539199 100644 --- a/src/core/lib/iomgr/timer_generic.cc +++ b/src/core/lib/iomgr/timer_generic.cc @@ -256,7 +256,7 @@ static grpc_millis compute_min_deadline(timer_shard* shard) { static void timer_list_init() { uint32_t i; - g_num_shards = GPR_MIN(1, 2 * gpr_cpu_num_cores()); + g_num_shards = GPR_CLAMP(2 * gpr_cpu_num_cores(), 1, 32); g_shards = static_cast<timer_shard*>(gpr_zalloc(g_num_shards * sizeof(*g_shards))); g_shard_queue = static_cast<timer_shard**>( diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc index c59059f045..31c02893b1 100644 --- a/src/cpp/client/channel_cc.cc +++ b/src/cpp/client/channel_cc.cc @@ -20,6 +20,7 @@ #include <chrono> #include <condition_variable> +#include <cstring> #include <memory> #include <mutex> @@ -64,6 +65,10 @@ Channel::~Channel() { namespace { +inline grpc_slice SliceFromArray(const char* arr, size_t len) { + return g_core_codegen_interface->grpc_slice_from_copied_buffer(arr, len); +} + grpc::string GetChannelInfoField(grpc_channel* channel, grpc_channel_info* channel_info, char*** channel_info_field) { @@ -110,16 +115,17 @@ internal::Call Channel::CreateCall(const internal::RpcMethod& method, context->propagation_options_.c_bitmask(), cq->cq(), method.channel_tag(), context->raw_deadline(), nullptr); } else { - const char* host_str = nullptr; - if (!context->authority().empty()) { - host_str = context->authority_.c_str(); + const string* host_str = nullptr; + if (!context->authority_.empty()) { + host_str = &context->authority_; } else if (!host_.empty()) { - host_str = host_.c_str(); + host_str = &host_; } - grpc_slice method_slice = SliceFromCopiedString(method.name()); + grpc_slice method_slice = + SliceFromArray(method.name(), strlen(method.name())); grpc_slice host_slice; if (host_str != nullptr) { - host_slice = SliceFromCopiedString(host_str); + host_slice = SliceFromCopiedString(*host_str); } c_call = grpc_channel_create_call( c_channel_, context->propagate_from_call_, diff --git a/src/cpp/server/channelz/channelz_service.cc b/src/cpp/server/channelz/channelz_service.cc index e096c1f421..4e3fe8c1c9 100644 --- a/src/cpp/server/channelz/channelz_service.cc +++ b/src/cpp/server/channelz/channelz_service.cc @@ -92,4 +92,21 @@ Status ChannelzService::GetSubchannel( return Status::OK; } +Status ChannelzService::GetSocket(ServerContext* unused, + const channelz::v1::GetSocketRequest* request, + channelz::v1::GetSocketResponse* response) { + char* json_str = grpc_channelz_get_socket(request->socket_id()); + gpr_log(GPR_ERROR, "%s", json_str); + if (json_str == nullptr) { + return Status(NOT_FOUND, "No object found for that SocketId"); + } + google::protobuf::util::Status s = + google::protobuf::util::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (s != google::protobuf::util::Status::OK) { + return Status(INTERNAL, s.ToString()); + } + return Status::OK; +} + } // namespace grpc diff --git a/src/cpp/server/channelz/channelz_service.h b/src/cpp/server/channelz/channelz_service.h index 9e0b5b6ead..1be4e01c73 100644 --- a/src/cpp/server/channelz/channelz_service.h +++ b/src/cpp/server/channelz/channelz_service.h @@ -44,6 +44,10 @@ class ChannelzService final : public channelz::v1::Channelz::Service { Status GetSubchannel(ServerContext* unused, const channelz::v1::GetSubchannelRequest* request, channelz::v1::GetSubchannelResponse* response) override; + // implementation of GetSocket rpc + Status GetSocket(ServerContext* unused, + const channelz::v1::GetSocketRequest* request, + channelz::v1::GetSocketResponse* response) override; }; } // namespace grpc diff --git a/src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs b/src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs new file mode 100644 index 0000000000..c3aee726f2 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs @@ -0,0 +1,119 @@ +#region Copyright notice and license + +// Copyright 2018 The 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ContextualMarshallerTest + { + const string Host = "127.0.0.1"; + + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + var contextualMarshaller = new Marshaller<string>( + (str, serializationContext) => + { + if (str == "UNSERIALIZABLE_VALUE") + { + // Google.Protobuf throws exception inherited from IOException + throw new IOException("Error serializing the message."); + } + if (str == "SERIALIZE_TO_NULL") + { + return; + } + var bytes = System.Text.Encoding.UTF8.GetBytes(str); + serializationContext.Complete(bytes); + }, + (deserializationContext) => + { + var buffer = deserializationContext.PayloadAsNewBuffer(); + Assert.AreEqual(buffer.Length, deserializationContext.PayloadLength); + var s = System.Text.Encoding.UTF8.GetString(buffer); + if (s == "UNPARSEABLE_VALUE") + { + // Google.Protobuf throws exception inherited from IOException + throw new IOException("Error parsing the message."); + } + return s; + }); + helper = new MockServiceHelper(Host, contextualMarshaller); + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void UnaryCall() + { + helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => + { + return Task.FromResult(request); + }); + Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC")); + } + + [Test] + public void ResponseParsingError_UnaryResponse() + { + helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => + { + return Task.FromResult("UNPARSEABLE_VALUE"); + }); + + var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "REQUEST")); + Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode); + } + + [Test] + public void RequestSerializationError_BlockingUnary() + { + Assert.Throws<IOException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "UNSERIALIZABLE_VALUE")); + } + + [Test] + public void SerializationResultIsNull_BlockingUnary() + { + Assert.Throws<NullReferenceException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "SERIALIZE_TO_NULL")); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/MarshallerTest.cs b/src/csharp/Grpc.Core.Tests/MarshallerTest.cs new file mode 100644 index 0000000000..97f64a0575 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/MarshallerTest.cs @@ -0,0 +1,105 @@ +#region Copyright notice and license + +// Copyright 2018 The 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class MarshallerTest + { + [Test] + public void ContextualSerializerEmulation() + { + Func<string, byte[]> simpleSerializer = System.Text.Encoding.UTF8.GetBytes; + Func<byte[], string> simpleDeserializer = System.Text.Encoding.UTF8.GetString; + var marshaller = new Marshaller<string>(simpleSerializer, + simpleDeserializer); + + Assert.AreSame(simpleSerializer, marshaller.Serializer); + Assert.AreSame(simpleDeserializer, marshaller.Deserializer); + + // test that emulated contextual serializer and deserializer work + string origMsg = "abc"; + var serializationContext = new FakeSerializationContext(); + marshaller.ContextualSerializer(origMsg, serializationContext); + + var deserializationContext = new FakeDeserializationContext(serializationContext.Payload); + Assert.AreEqual(origMsg, marshaller.ContextualDeserializer(deserializationContext)); + } + + [Test] + public void SimpleSerializerEmulation() + { + Action<string, SerializationContext> contextualSerializer = (str, context) => + { + var bytes = System.Text.Encoding.UTF8.GetBytes(str); + context.Complete(bytes); + }; + Func<DeserializationContext, string> contextualDeserializer = (context) => + { + return System.Text.Encoding.UTF8.GetString(context.PayloadAsNewBuffer()); + }; + var marshaller = new Marshaller<string>(contextualSerializer, contextualDeserializer); + + Assert.AreSame(contextualSerializer, marshaller.ContextualSerializer); + Assert.AreSame(contextualDeserializer, marshaller.ContextualDeserializer); + + // test that emulated serializer and deserializer work + var origMsg = "abc"; + var serialized = marshaller.Serializer(origMsg); + Assert.AreEqual(origMsg, marshaller.Deserializer(serialized)); + } + + class FakeSerializationContext : SerializationContext + { + public byte[] Payload; + public override void Complete(byte[] payload) + { + this.Payload = payload; + } + } + + class FakeDeserializationContext : DeserializationContext + { + public byte[] payload; + + public FakeDeserializationContext(byte[] payload) + { + this.payload = payload; + } + + public override int PayloadLength => payload.Length; + + public override byte[] PayloadAsNewBuffer() + { + return payload; + } + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs index 171c5c470e..d85d7572a6 100644 --- a/src/csharp/Grpc.Core.Tests/MetadataTest.cs +++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs @@ -73,6 +73,23 @@ namespace Grpc.Core.Tests } [Test] + public void KeysAreNormalized_UppercaseKey() + { + var uppercaseKey = "ABC"; + var entry = new Metadata.Entry(uppercaseKey, "XYZ"); + Assert.AreEqual("abc", entry.Key); + } + + [Test] + public void KeysAreNormalized_LowercaseKey() + { + var lowercaseKey = "abc"; + var entry = new Metadata.Entry(lowercaseKey, "XYZ"); + // no allocation if key already lowercase + Assert.AreSame(lowercaseKey, entry.Key); + } + + [Test] public void Entry_ConstructionPreconditions() { Assert.Throws(typeof(ArgumentNullException), () => new Metadata.Entry(null, "xyz")); diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index fac34071be..05edce7467 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -151,12 +151,12 @@ namespace Grpc.Core { private class ClientBaseConfigurationInterceptor : Interceptor { - readonly Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor; + readonly Func<IMethod, string, CallOptions, ClientBaseConfigurationInfo> interceptor; /// <summary> /// Creates a new instance of ClientBaseConfigurationInterceptor given the specified header and host interceptor function. /// </summary> - public ClientBaseConfigurationInterceptor(Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor) + public ClientBaseConfigurationInterceptor(Func<IMethod, string, CallOptions, ClientBaseConfigurationInfo> interceptor) { this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor)); } @@ -166,7 +166,7 @@ namespace Grpc.Core where TResponse : class { var newHostAndCallOptions = interceptor(context.Method, context.Host, context.Options); - return new ClientInterceptorContext<TRequest, TResponse>(context.Method, newHostAndCallOptions.Item1, newHostAndCallOptions.Item2); + return new ClientInterceptorContext<TRequest, TResponse>(context.Method, newHostAndCallOptions.Host, newHostAndCallOptions.CallOptions); } public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation) @@ -195,6 +195,18 @@ namespace Grpc.Core } } + internal struct ClientBaseConfigurationInfo + { + internal readonly string Host; + internal readonly CallOptions CallOptions; + + internal ClientBaseConfigurationInfo(string host, CallOptions callOptions) + { + Host = host; + CallOptions = callOptions; + } + } + readonly CallInvoker undecoratedCallInvoker; readonly string host; @@ -206,7 +218,7 @@ namespace Grpc.Core internal CallInvoker CreateDecoratedCallInvoker() { - return undecoratedCallInvoker.Intercept(new ClientBaseConfigurationInterceptor((method, host, options) => Tuple.Create(this.host, options))); + return undecoratedCallInvoker.Intercept(new ClientBaseConfigurationInterceptor((method, host, options) => new ClientBaseConfigurationInfo(this.host, options))); } internal ClientBaseConfiguration WithHost(string host) diff --git a/src/csharp/Grpc.Core/DeserializationContext.cs b/src/csharp/Grpc.Core/DeserializationContext.cs new file mode 100644 index 0000000000..5b6372ef85 --- /dev/null +++ b/src/csharp/Grpc.Core/DeserializationContext.cs @@ -0,0 +1,46 @@ +#region Copyright notice and license + +// Copyright 2018 The 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. + +#endregion + +namespace Grpc.Core +{ + /// <summary> + /// Provides access to the payload being deserialized when deserializing messages. + /// </summary> + public abstract class DeserializationContext + { + /// <summary> + /// Get the total length of the payload in bytes. + /// </summary> + public abstract int PayloadLength { get; } + + /// <summary> + /// Gets the entire payload as a newly allocated byte array. + /// Once the byte array is returned, the byte array becomes owned by the caller and won't be ever accessed or reused by gRPC again. + /// NOTE: Obtaining the buffer as a newly allocated byte array is the simplest way of accessing the payload, + /// but it can have important consequences in high-performance scenarios. + /// In particular, using this method usually requires copying of the entire buffer one extra time. + /// Also, allocating a new buffer each time can put excessive pressure on GC, especially if + /// the payload is more than 86700 bytes large (which means the newly allocated buffer will be placed in LOH, + /// and LOH object can only be garbage collected via a full ("stop the world") GC run). + /// NOTE: Deserializers are expected not to call this method more than once per received message + /// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so. + /// </summary> + /// <returns>byte array containing the entire payload.</returns> + public abstract byte[] PayloadAsNewBuffer(); + } +} diff --git a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs index 3f9605bfdd..09ded1a036 100644 --- a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs +++ b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs @@ -35,6 +35,13 @@ namespace Grpc.Core.Internal /// </summary> public static string PtrToStringUTF8(IntPtr ptr, int len) { + if (len == 0) + { + return ""; + } + + // TODO(jtattermusch): once Span dependency is added, + // use Span-based API to decode the string without copying the buffer. var bytes = new byte[len]; Marshal.Copy(ptr, bytes, 0, len); return EncodingUTF8.GetString(bytes); diff --git a/src/csharp/Grpc.Core/Marshaller.cs b/src/csharp/Grpc.Core/Marshaller.cs index 1d758ca935..0af9aa586b 100644 --- a/src/csharp/Grpc.Core/Marshaller.cs +++ b/src/csharp/Grpc.Core/Marshaller.cs @@ -29,36 +29,129 @@ namespace Grpc.Core readonly Func<T, byte[]> serializer; readonly Func<byte[], T> deserializer; + readonly Action<T, SerializationContext> contextualSerializer; + readonly Func<DeserializationContext, T> contextualDeserializer; + /// <summary> - /// Initializes a new marshaller. + /// Initializes a new marshaller from simple serialize/deserialize functions. /// </summary> /// <param name="serializer">Function that will be used to serialize messages.</param> /// <param name="deserializer">Function that will be used to deserialize messages.</param> public Marshaller(Func<T, byte[]> serializer, Func<byte[], T> deserializer) { - this.serializer = GrpcPreconditions.CheckNotNull(serializer, "serializer"); - this.deserializer = GrpcPreconditions.CheckNotNull(deserializer, "deserializer"); + this.serializer = GrpcPreconditions.CheckNotNull(serializer, nameof(serializer)); + this.deserializer = GrpcPreconditions.CheckNotNull(deserializer, nameof(deserializer)); + this.contextualSerializer = EmulateContextualSerializer; + this.contextualDeserializer = EmulateContextualDeserializer; } /// <summary> - /// Gets the serializer function. + /// Initializes a new marshaller from serialize/deserialize fuctions that can access serialization and deserialization + /// context. Compared to the simple serializer/deserializer functions, using the contextual version provides more + /// flexibility and can lead to increased efficiency (and better performance). + /// Note: This constructor is part of an experimental API that can change or be removed without any prior notice. /// </summary> - public Func<T, byte[]> Serializer + /// <param name="serializer">Function that will be used to serialize messages.</param> + /// <param name="deserializer">Function that will be used to deserialize messages.</param> + public Marshaller(Action<T, SerializationContext> serializer, Func<DeserializationContext, T> deserializer) { - get - { - return this.serializer; - } + this.contextualSerializer = GrpcPreconditions.CheckNotNull(serializer, nameof(serializer)); + this.contextualDeserializer = GrpcPreconditions.CheckNotNull(deserializer, nameof(deserializer)); + // TODO(jtattermusch): once gRPC C# library switches to using contextual (de)serializer, + // emulating the simple (de)serializer will become unnecessary. + this.serializer = EmulateSimpleSerializer; + this.deserializer = EmulateSimpleDeserializer; } /// <summary> + /// Gets the serializer function. + /// </summary> + public Func<T, byte[]> Serializer => this.serializer; + + /// <summary> /// Gets the deserializer function. /// </summary> - public Func<byte[], T> Deserializer + public Func<byte[], T> Deserializer => this.deserializer; + + /// <summary> + /// Gets the serializer function. + /// Note: experimental API that can change or be removed without any prior notice. + /// </summary> + public Action<T, SerializationContext> ContextualSerializer => this.contextualSerializer; + + /// <summary> + /// Gets the serializer function. + /// Note: experimental API that can change or be removed without any prior notice. + /// </summary> + public Func<DeserializationContext, T> ContextualDeserializer => this.contextualDeserializer; + + // for backward compatibility, emulate the simple serializer using the contextual one + private byte[] EmulateSimpleSerializer(T msg) { - get + // TODO(jtattermusch): avoid the allocation by passing a thread-local instance + // This code will become unnecessary once gRPC C# library switches to using contextual (de)serializer. + var context = new EmulatedSerializationContext(); + this.contextualSerializer(msg, context); + return context.GetPayload(); + } + + // for backward compatibility, emulate the simple deserializer using the contextual one + private T EmulateSimpleDeserializer(byte[] payload) + { + // TODO(jtattermusch): avoid the allocation by passing a thread-local instance + // This code will become unnecessary once gRPC C# library switches to using contextual (de)serializer. + var context = new EmulatedDeserializationContext(payload); + return this.contextualDeserializer(context); + } + + // for backward compatibility, emulate the contextual serializer using the simple one + private void EmulateContextualSerializer(T message, SerializationContext context) + { + var payload = this.serializer(message); + context.Complete(payload); + } + + // for backward compatibility, emulate the contextual deserializer using the simple one + private T EmulateContextualDeserializer(DeserializationContext context) + { + return this.deserializer(context.PayloadAsNewBuffer()); + } + + internal class EmulatedSerializationContext : SerializationContext + { + bool isComplete; + byte[] payload; + + public override void Complete(byte[] payload) + { + GrpcPreconditions.CheckState(!isComplete); + this.isComplete = true; + this.payload = payload; + } + + internal byte[] GetPayload() + { + return this.payload; + } + } + + internal class EmulatedDeserializationContext : DeserializationContext + { + readonly byte[] payload; + bool alreadyCalledPayloadAsNewBuffer; + + public EmulatedDeserializationContext(byte[] payload) + { + this.payload = GrpcPreconditions.CheckNotNull(payload); + } + + public override int PayloadLength => payload.Length; + + public override byte[] PayloadAsNewBuffer() { - return this.deserializer; + GrpcPreconditions.CheckState(!alreadyCalledPayloadAsNewBuffer); + alreadyCalledPayloadAsNewBuffer = true; + return payload; } } } @@ -77,6 +170,15 @@ namespace Grpc.Core } /// <summary> + /// Creates a marshaller from specified contextual serializer and deserializer. + /// Note: This method is part of an experimental API that can change or be removed without any prior notice. + /// </summary> + public static Marshaller<T> Create<T>(Action<T, SerializationContext> serializer, Func<DeserializationContext, T> deserializer) + { + return new Marshaller<T>(serializer, deserializer); + } + + /// <summary> /// Returns a marshaller for <c>string</c> type. This is useful for testing. /// </summary> public static Marshaller<string> StringMarshaller diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index 281952d6d4..bc263c3469 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -135,7 +135,7 @@ namespace Grpc.Core } /// <summary> - /// <see cref="T:IList`1"/> + /// Adds a new ASCII-valued metadata entry. See <c>Metadata.Entry</c> constructor for params. /// </summary> public void Add(string key, string value) { @@ -143,7 +143,7 @@ namespace Grpc.Core } /// <summary> - /// <see cref="T:IList`1"/> + /// Adds a new binary-valued metadata entry. See <c>Metadata.Entry</c> constructor for params. /// </summary> public void Add(string key, byte[] valueBytes) { @@ -225,8 +225,6 @@ namespace Grpc.Core /// </summary> public class Entry { - private static readonly Regex ValidKeyRegex = new Regex("^[.a-z0-9_-]+$"); - readonly string key; readonly string value; readonly byte[] valueBytes; @@ -241,7 +239,7 @@ namespace Grpc.Core /// <summary> /// Initializes a new instance of the <see cref="Grpc.Core.Metadata.Entry"/> struct with a binary value. /// </summary> - /// <param name="key">Metadata key, needs to have suffix indicating a binary valued metadata entry.</param> + /// <param name="key">Metadata key. Gets converted to lowercase. Needs to have suffix indicating a binary valued metadata entry. Can only contain lowercase alphanumeric characters, underscores, hyphens and dots.</param> /// <param name="valueBytes">Value bytes.</param> public Entry(string key, byte[] valueBytes) { @@ -255,9 +253,9 @@ namespace Grpc.Core } /// <summary> - /// Initializes a new instance of the <see cref="Grpc.Core.Metadata.Entry"/> struct holding an ASCII value. + /// Initializes a new instance of the <see cref="Grpc.Core.Metadata.Entry"/> struct with an ASCII value. /// </summary> - /// <param name="key">Metadata key, must not use suffix indicating a binary valued metadata entry.</param> + /// <param name="key">Metadata key. Gets converted to lowercase. Must not use suffix indicating a binary valued metadata entry. Can only contain lowercase alphanumeric characters, underscores, hyphens and dots.</param> /// <param name="value">Value string. Only ASCII characters are allowed.</param> public Entry(string key, string value) { @@ -358,10 +356,42 @@ namespace Grpc.Core private static string NormalizeKey(string key) { - var normalized = GrpcPreconditions.CheckNotNull(key, "key").ToLowerInvariant(); - GrpcPreconditions.CheckArgument(ValidKeyRegex.IsMatch(normalized), + GrpcPreconditions.CheckNotNull(key, "key"); + + GrpcPreconditions.CheckArgument(IsValidKey(key, out bool isLowercase), "Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores, hyphens and dots."); - return normalized; + if (isLowercase) + { + // save allocation of a new string if already lowercase + return key; + } + + return key.ToLowerInvariant(); + } + + private static bool IsValidKey(string input, out bool isLowercase) + { + isLowercase = true; + for (int i = 0; i < input.Length; i++) + { + char c = input[i]; + if ('a' <= c && c <= 'z' || + '0' <= c && c <= '9' || + c == '.' || + c == '_' || + c == '-' ) + continue; + + if ('A' <= c && c <= 'Z') + { + isLowercase = false; + continue; + } + + return false; + } + + return true; } /// <summary> diff --git a/src/csharp/Grpc.Core/SerializationContext.cs b/src/csharp/Grpc.Core/SerializationContext.cs new file mode 100644 index 0000000000..cf4d1595da --- /dev/null +++ b/src/csharp/Grpc.Core/SerializationContext.cs @@ -0,0 +1,34 @@ +#region Copyright notice and license + +// Copyright 2018 The 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. + +#endregion + +namespace Grpc.Core +{ + /// <summary> + /// Provides storage for payload when serializing a message. + /// </summary> + public abstract class SerializationContext + { + /// <summary> + /// Use the byte array as serialized form of current message and mark serialization process as complete. + /// Complete() can only be called once. By calling this method the caller gives up the ownership of the + /// payload which must not be accessed afterwards. + /// </summary> + /// <param name="payload">the serialized form of current message</param> + public abstract void Complete(byte[] payload); + } +} diff --git a/src/csharp/tests.json b/src/csharp/tests.json index c2f243fe0a..5683d164c6 100644 --- a/src/csharp/tests.json +++ b/src/csharp/tests.json @@ -23,8 +23,10 @@ "Grpc.Core.Tests.ClientServerTest", "Grpc.Core.Tests.CompressionTest", "Grpc.Core.Tests.ContextPropagationTest", + "Grpc.Core.Tests.ContextualMarshallerTest", "Grpc.Core.Tests.GrpcEnvironmentTest", "Grpc.Core.Tests.HalfcloseTest", + "Grpc.Core.Tests.MarshallerTest", "Grpc.Core.Tests.MarshallingErrorsTest", "Grpc.Core.Tests.MetadataTest", "Grpc.Core.Tests.PerformanceTest", diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/NSError+GRPC.h index a63e76ee4d..fb05638b78 100644 --- a/src/objective-c/GRPCClient/private/NSError+GRPC.h +++ b/src/objective-c/GRPCClient/private/NSError+GRPC.h @@ -25,6 +25,6 @@ * and whose domain is |kGRPCErrorDomain|. */ + (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode - details:(char *)details + details:(const char *)details errorString:(const char *)errorString; @end diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.m b/src/objective-c/GRPCClient/private/NSError+GRPC.m index 199b2ebb6c..3eefed88d6 100644 --- a/src/objective-c/GRPCClient/private/NSError+GRPC.m +++ b/src/objective-c/GRPCClient/private/NSError+GRPC.m @@ -24,18 +24,20 @@ NSString *const kGRPCErrorDomain = @"io.grpc"; @implementation NSError (GRPC) + (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode - details:(char *)details + details:(const char *)details errorString:(const char *)errorString { if (statusCode == GRPC_STATUS_OK) { return nil; } - NSString *message = [NSString stringWithCString:details encoding:NSUTF8StringEncoding]; - NSString *debugMessage = [NSString stringWithCString:errorString encoding:NSUTF8StringEncoding]; - return [NSError errorWithDomain:kGRPCErrorDomain - code:statusCode - userInfo:@{ - NSLocalizedDescriptionKey : message, - NSDebugDescriptionErrorKey : debugMessage - }]; + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (details) { + userInfo[NSLocalizedDescriptionKey] = + [NSString stringWithCString:details encoding:NSUTF8StringEncoding]; + } + if (errorString) { + userInfo[NSDebugDescriptionErrorKey] = + [NSString stringWithCString:errorString encoding:NSUTF8StringEncoding]; + } + return [NSError errorWithDomain:kGRPCErrorDomain code:statusCode userInfo:userInfo]; } @end diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index 507d251b48..5d2f1340da 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -14,6 +14,7 @@ GRPC_LOCAL_SRC = '../../..' InteropTestsLocalSSL InteropTestsLocalCleartext InteropTestsRemoteWithCronet + UnitTests ).each do |target_name| target target_name do pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf", :inhibit_warnings => true diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 0e0e8babc0..f0d8123263 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 16A9E77B6E336B3C0B9BA6E0 /* libPods-InteropTestsLocalSSL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DBEDE45BDA60DF1E1C8950C0 /* libPods-InteropTestsLocalSSL.a */; }; 20DFDF829DD993A4A00D5662 /* libPods-RxLibraryUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */; }; 333E8FC01C8285B7C547D799 /* libPods-InteropTestsLocalCleartext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD346DB2C23F676C4842F3FF /* libPods-InteropTestsLocalCleartext.a */; }; + 5E0282E9215AA697007AC99D /* UnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E0282E8215AA697007AC99D /* UnitTests.m */; }; + 5E0282EB215AA697007AC99D /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.mm */; }; 5E8A5DA91D3840B4000F8BC4 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; 5EAD6D271E27047400002378 /* CronetUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EAD6D261E27047400002378 /* CronetUnitTests.m */; }; @@ -54,10 +56,18 @@ 91D4B3C85B6D8562F409CB48 /* libPods-InteropTestsLocalSSLCFStream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F3AB031E0E26AC8EF30A2A2A /* libPods-InteropTestsLocalSSLCFStream.a */; }; BC111C80CBF7068B62869352 /* libPods-InteropTestsRemoteCFStream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F44AC3F44E3491A8C0D890FE /* libPods-InteropTestsRemoteCFStream.a */; }; C3D6F4270A2FFF634D8849ED /* libPods-InteropTestsLocalCleartextCFStream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BDA4BA011779D5D25B5618C /* libPods-InteropTestsLocalCleartextCFStream.a */; }; + CCF5C0719EF608276AE16374 /* libPods-UnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22A3EBB488699C8CEA19707B /* libPods-UnitTests.a */; }; F15EF7852DC70770EFDB1D2C /* libPods-AllTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CAE086D5B470DA367D415AB0 /* libPods-AllTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 5E0282EC215AA697007AC99D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 635697BF1B14FC11007A7283 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 635697C61B14FC11007A7283; + remoteInfo = Tests; + }; 5E8A5DAA1D3840B4000F8BC4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 635697BF1B14FC11007A7283 /* Project object */; @@ -138,6 +148,7 @@ 1588C85DEAF7FC0ACDEA4C02 /* Pods-InteropTestsLocalCleartext.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.test.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.test.xcconfig"; sourceTree = "<group>"; }; 17F60BF2871F6AF85FB3FA12 /* Pods-InteropTestsRemoteWithCronet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.debug.xcconfig"; sourceTree = "<group>"; }; 20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 22A3EBB488699C8CEA19707B /* libPods-UnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2B89F3037963E6EDDD48D8C3 /* Pods-InteropTestsRemoteWithCronet.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.test.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.test.xcconfig"; sourceTree = "<group>"; }; 303F4A17EB1650FC44603D17 /* Pods-InteropTestsRemoteCFStream.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteCFStream.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteCFStream/Pods-InteropTestsRemoteCFStream.release.xcconfig"; sourceTree = "<group>"; }; 32748C4078AEB05F8F954361 /* Pods-InteropTestsRemoteCFStream.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteCFStream.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteCFStream/Pods-InteropTestsRemoteCFStream.debug.xcconfig"; sourceTree = "<group>"; }; @@ -155,6 +166,9 @@ 55B630C1FF8C36D1EFC4E0A4 /* Pods-InteropTestsLocalSSLCFStream.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSLCFStream.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSLCFStream/Pods-InteropTestsLocalSSLCFStream.cronet.xcconfig"; sourceTree = "<group>"; }; 573450F334B331D0BED8B961 /* Pods-CoreCronetEnd2EndTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreCronetEnd2EndTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests.cronet.xcconfig"; sourceTree = "<group>"; }; 5761E98978DDDF136A58CB7E /* Pods-AllTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.release.xcconfig"; sourceTree = "<group>"; }; + 5E0282E6215AA697007AC99D /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E0282E8215AA697007AC99D /* UnitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnitTests.m; sourceTree = "<group>"; }; + 5E0282EA215AA697007AC99D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CoreCronetEnd2EndTests.mm; sourceTree = "<group>"; }; 5EA908CF4CDA4CE218352A06 /* Pods-InteropTestsLocalSSLCFStream.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSLCFStream.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSLCFStream/Pods-InteropTestsLocalSSLCFStream.release.xcconfig"; sourceTree = "<group>"; }; @@ -192,7 +206,9 @@ 7BA53C6D224288D5870FE6F3 /* Pods-InteropTestsLocalCleartextCFStream.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartextCFStream.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartextCFStream/Pods-InteropTestsLocalCleartextCFStream.release.xcconfig"; sourceTree = "<group>"; }; 8B498B05C6DA0818B2FA91D4 /* Pods-InteropTestsLocalCleartextCFStream.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartextCFStream.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartextCFStream/Pods-InteropTestsLocalCleartextCFStream.cronet.xcconfig"; sourceTree = "<group>"; }; 943138072A9605B5B8DC1FC0 /* Pods-InteropTestsLocalCleartextCFStream.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartextCFStream.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartextCFStream/Pods-InteropTestsLocalCleartextCFStream.debug.xcconfig"; sourceTree = "<group>"; }; + 94D7A5FAA13480E9A5166D7A /* Pods-UnitTests.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.test.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.test.xcconfig"; sourceTree = "<group>"; }; 9E9444C764F0FFF64A7EB58E /* libPods-InteropTestsRemoteWithCronet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsRemoteWithCronet.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + A2DCF2570BE515B62CB924CA /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = "<group>"; }; A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RxLibraryUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A6F832FCEFA6F6881E620F12 /* Pods-InteropTestsRemote.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.test.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.test.xcconfig"; sourceTree = "<group>"; }; AA7CB64B4DD9915AE7C03163 /* Pods-InteropTestsLocalCleartext.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.cronet.xcconfig"; sourceTree = "<group>"; }; @@ -208,9 +224,11 @@ DBEDE45BDA60DF1E1C8950C0 /* libPods-InteropTestsLocalSSL.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsLocalSSL.a"; sourceTree = BUILT_PRODUCTS_DIR; }; DC3CA1D948F068E76957A861 /* Pods-InteropTestsRemote.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.debug.xcconfig"; sourceTree = "<group>"; }; E1486220285AF123EB124008 /* Pods-InteropTestsLocalCleartext.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.debug.xcconfig"; sourceTree = "<group>"; }; + E1E7660656D902104F728892 /* Pods-UnitTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.cronet.xcconfig"; sourceTree = "<group>"; }; E4275A759BDBDF143B9B438F /* Pods-InteropTestsRemote.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.release.xcconfig"; sourceTree = "<group>"; }; E4FD4606D4AB8D5A314D72F0 /* Pods-InteropTestsLocalCleartextCFStream.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartextCFStream.test.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartextCFStream/Pods-InteropTestsLocalCleartextCFStream.test.xcconfig"; sourceTree = "<group>"; }; E7E4D3FD76E3B745D992AF5F /* Pods-AllTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.cronet.xcconfig"; sourceTree = "<group>"; }; + EBFFEC04B514CB0D4922DC40 /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = "<group>"; }; F3AB031E0E26AC8EF30A2A2A /* libPods-InteropTestsLocalSSLCFStream.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsLocalSSLCFStream.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F44AC3F44E3491A8C0D890FE /* libPods-InteropTestsRemoteCFStream.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsRemoteCFStream.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CoreCronetEnd2EndTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -219,6 +237,15 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 5E0282E3215AA697007AC99D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E0282EB215AA697007AC99D /* libTests.a in Frameworks */, + CCF5C0719EF608276AE16374 /* libPods-UnitTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5E8A5DA11D3840B4000F8BC4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -341,6 +368,7 @@ F44AC3F44E3491A8C0D890FE /* libPods-InteropTestsRemoteCFStream.a */, 0BDA4BA011779D5D25B5618C /* libPods-InteropTestsLocalCleartextCFStream.a */, F3AB031E0E26AC8EF30A2A2A /* libPods-InteropTestsLocalSSLCFStream.a */, + 22A3EBB488699C8CEA19707B /* libPods-UnitTests.a */, ); name = Frameworks; sourceTree = "<group>"; @@ -394,10 +422,23 @@ 41AA59529240A6BBBD3DB904 /* Pods-InteropTestsLocalSSLCFStream.test.xcconfig */, 55B630C1FF8C36D1EFC4E0A4 /* Pods-InteropTestsLocalSSLCFStream.cronet.xcconfig */, 5EA908CF4CDA4CE218352A06 /* Pods-InteropTestsLocalSSLCFStream.release.xcconfig */, + A2DCF2570BE515B62CB924CA /* Pods-UnitTests.debug.xcconfig */, + 94D7A5FAA13480E9A5166D7A /* Pods-UnitTests.test.xcconfig */, + E1E7660656D902104F728892 /* Pods-UnitTests.cronet.xcconfig */, + EBFFEC04B514CB0D4922DC40 /* Pods-UnitTests.release.xcconfig */, ); name = Pods; sourceTree = "<group>"; }; + 5E0282E7215AA697007AC99D /* UnitTests */ = { + isa = PBXGroup; + children = ( + 5E0282E8215AA697007AC99D /* UnitTests.m */, + 5E0282EA215AA697007AC99D /* Info.plist */, + ); + path = UnitTests; + sourceTree = "<group>"; + }; 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { isa = PBXGroup; children = ( @@ -432,6 +473,7 @@ 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */, 5EE84BF21D4717E40050C6CC /* InteropTestsRemoteWithCronet */, 5EAD6D251E27047400002378 /* CronetUnitTests */, + 5E0282E7215AA697007AC99D /* UnitTests */, 635697C81B14FC11007A7283 /* Products */, 51E4650F34F854F41FF053B3 /* Pods */, 136D535E19727099B941D7B1 /* Frameworks */, @@ -453,6 +495,7 @@ 5EC5E421208177CC000EF4AD /* InteropTestsRemoteCFStream.xctest */, 5EC5E4312081856B000EF4AD /* InteropTestsLocalCleartextCFStream.xctest */, 5EC5E442208185CE000EF4AD /* InteropTestsLocalSSLCFStream.xctest */, + 5E0282E6215AA697007AC99D /* UnitTests.xctest */, ); name = Products; sourceTree = "<group>"; @@ -485,6 +528,26 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 5E0282E5215AA697007AC99D /* UnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E0282F2215AA697007AC99D /* Build configuration list for PBXNativeTarget "UnitTests" */; + buildPhases = ( + F07941C0BAF6A7C67AA60C48 /* [CP] Check Pods Manifest.lock */, + 5E0282E2215AA697007AC99D /* Sources */, + 5E0282E3215AA697007AC99D /* Frameworks */, + 5E0282E4215AA697007AC99D /* Resources */, + 9AD0B5E94F2AA5962EA6AA36 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5E0282ED215AA697007AC99D /* PBXTargetDependency */, + ); + name = UnitTests; + productName = UnitTests; + productReference = 5E0282E6215AA697007AC99D /* UnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 5E8A5DA31D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { isa = PBXNativeTarget; buildConfigurationList = 5E8A5DAE1D3840B4000F8BC4 /* Build configuration list for PBXNativeTarget "CoreCronetEnd2EndTests" */; @@ -729,6 +792,10 @@ LastUpgradeCheck = 0630; ORGANIZATIONNAME = gRPC; TargetAttributes = { + 5E0282E5215AA697007AC99D = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; 5E8A5DA31D3840B4000F8BC4 = { CreatedOnToolsVersion = 7.3.1; }; @@ -794,11 +861,19 @@ 5EC5E420208177CC000EF4AD /* InteropTestsRemoteCFStream */, 5EC5E4302081856B000EF4AD /* InteropTestsLocalCleartextCFStream */, 5EC5E441208185CE000EF4AD /* InteropTestsLocalSSLCFStream */, + 5E0282E5215AA697007AC99D /* UnitTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 5E0282E4215AA697007AC99D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5E8A5DA21D3840B4000F8BC4 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1098,6 +1173,24 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemoteCFStream/Pods-InteropTestsRemoteCFStream-resources.sh\"\n"; showEnvVarsInLog = 0; }; + 9AD0B5E94F2AA5962EA6AA36 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; A023FB55205A7EA37D413549 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1260,6 +1353,24 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + F07941C0BAF6A7C67AA60C48 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-UnitTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; F3D5B2CDA172580341682830 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1299,6 +1410,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 5E0282E2215AA697007AC99D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E0282E9215AA697007AC99D /* UnitTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5E8A5DA01D3840B4000F8BC4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1412,6 +1531,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 5E0282ED215AA697007AC99D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 635697C61B14FC11007A7283 /* Tests */; + targetProxy = 5E0282EC215AA697007AC99D /* PBXContainerItemProxy */; + }; 5E8A5DAB1D3840B4000F8BC4 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 635697C61B14FC11007A7283 /* Tests */; @@ -1455,6 +1579,128 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 5E0282EE215AA697007AC99D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A2DCF2570BE515B62CB924CA /* Pods-UnitTests.debug.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = UnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/../../../..\""; + }; + name = Debug; + }; + 5E0282EF215AA697007AC99D /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 94D7A5FAA13480E9A5166D7A /* Pods-UnitTests.test.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = UnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/../../../..\""; + }; + name = Test; + }; + 5E0282F0215AA697007AC99D /* Cronet */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E1E7660656D902104F728892 /* Pods-UnitTests.cronet.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = UnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/../../../..\""; + }; + name = Cronet; + }; + 5E0282F1215AA697007AC99D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EBFFEC04B514CB0D4922DC40 /* Pods-UnitTests.release.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = UnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/../../../..\""; + }; + name = Release; + }; 5E1228981E4D400F00E8504F /* Test */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2602,6 +2848,17 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 5E0282F2215AA697007AC99D /* Build configuration list for PBXNativeTarget "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E0282EE215AA697007AC99D /* Debug */, + 5E0282EF215AA697007AC99D /* Test */, + 5E0282F0215AA697007AC99D /* Cronet */, + 5E0282F1215AA697007AC99D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 5E8A5DAE1D3840B4000F8BC4 /* Build configuration list for PBXNativeTarget "CoreCronetEnd2EndTests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme new file mode 100644 index 0000000000..3af3555f48 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0920" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E0282E5215AA697007AC99D" + BuildableName = "UnitTests.xctest" + BlueprintName = "UnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E0282E5215AA697007AC99D" + BuildableName = "UnitTests.xctest" + BlueprintName = "UnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E0282E5215AA697007AC99D" + BuildableName = "UnitTests.xctest" + BlueprintName = "UnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E0282E5215AA697007AC99D" + BuildableName = "UnitTests.xctest" + BlueprintName = "UnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/src/objective-c/tests/UnitTests/Info.plist b/src/objective-c/tests/UnitTests/Info.plist new file mode 100644 index 0000000000..6c40a6cd0c --- /dev/null +++ b/src/objective-c/tests/UnitTests/Info.plist @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> +</dict> +</plist> diff --git a/src/objective-c/tests/UnitTests/UnitTests.m b/src/objective-c/tests/UnitTests/UnitTests.m new file mode 100644 index 0000000000..57e686d1b6 --- /dev/null +++ b/src/objective-c/tests/UnitTests/UnitTests.m @@ -0,0 +1,62 @@ +/* + * + * Copyright 2018 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. + * + */ + +#import <XCTest/XCTest.h> + +#import <GRPCClient/GRPCCall.h> + +#import "src/objective-c/GRPCClient/private/NSError+GRPC.h" + +@interface UnitTests : XCTestCase + +@end + +@implementation UnitTests + +- (void)testNSError { + const char *kDetails = "test details"; + const char *kErrorString = "test errorString"; + NSError *error1 = [NSError grpc_errorFromStatusCode:GRPC_STATUS_OK details:nil errorString:nil]; + NSError *error2 = [NSError grpc_errorFromStatusCode:GRPC_STATUS_CANCELLED + details:kDetails + errorString:kErrorString]; + NSError *error3 = [NSError grpc_errorFromStatusCode:GRPC_STATUS_UNAUTHENTICATED + details:kDetails + errorString:nil]; + NSError *error4 = + [NSError grpc_errorFromStatusCode:GRPC_STATUS_UNAVAILABLE details:nil errorString:nil]; + + XCTAssertNil(error1); + XCTAssertEqual(error2.code, 1); + XCTAssertEqualObjects(error2.domain, @"io.grpc"); + XCTAssertEqualObjects(error2.userInfo[NSLocalizedDescriptionKey], + [NSString stringWithUTF8String:kDetails]); + XCTAssertEqualObjects(error2.userInfo[NSDebugDescriptionErrorKey], + [NSString stringWithUTF8String:kErrorString]); + XCTAssertEqual(error3.code, 16); + XCTAssertEqualObjects(error3.domain, @"io.grpc"); + XCTAssertEqualObjects(error3.userInfo[NSLocalizedDescriptionKey], + [NSString stringWithUTF8String:kDetails]); + XCTAssertNil(error3.userInfo[NSDebugDescriptionErrorKey]); + XCTAssertEqual(error4.code, 14); + XCTAssertEqualObjects(error4.domain, @"io.grpc"); + XCTAssertNil(error4.userInfo[NSLocalizedDescriptionKey]); + XCTAssertNil(error4.userInfo[NSDebugDescriptionErrorKey]); +} + +@end diff --git a/src/objective-c/tests/run_tests.sh b/src/objective-c/tests/run_tests.sh index 9dde07d55c..f37c6cf9f6 100755 --- a/src/objective-c/tests/run_tests.sh +++ b/src/objective-c/tests/run_tests.sh @@ -163,4 +163,14 @@ xcodebuild \ | egrep -v '^$' \ | egrep -v "(GPBDictionary|GPBArray)" - +echo "TIME: $(date)" +xcodebuild \ + -workspace Tests.xcworkspace \ + -scheme UnitTests \ + -destination name="iPhone 8" \ + test \ + | egrep -v "$XCODEBUILD_FILTER" \ + | egrep -v '^$' \ + | egrep -v "(GPBDictionary|GPBArray)" - + exit 0 diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c index 0c46f6c85a..1e7d7f687f 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c @@ -100,6 +100,7 @@ grpc_channelz_get_top_channels_type grpc_channelz_get_top_channels_import; grpc_channelz_get_servers_type grpc_channelz_get_servers_import; grpc_channelz_get_channel_type grpc_channelz_get_channel_import; grpc_channelz_get_subchannel_type grpc_channelz_get_subchannel_import; +grpc_channelz_get_socket_type grpc_channelz_get_socket_import; grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import; grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import; grpc_use_signal_type grpc_use_signal_import; @@ -356,6 +357,7 @@ void grpc_rb_load_imports(HMODULE library) { grpc_channelz_get_servers_import = (grpc_channelz_get_servers_type) GetProcAddress(library, "grpc_channelz_get_servers"); grpc_channelz_get_channel_import = (grpc_channelz_get_channel_type) GetProcAddress(library, "grpc_channelz_get_channel"); grpc_channelz_get_subchannel_import = (grpc_channelz_get_subchannel_type) GetProcAddress(library, "grpc_channelz_get_subchannel"); + grpc_channelz_get_socket_import = (grpc_channelz_get_socket_type) GetProcAddress(library, "grpc_channelz_get_socket"); grpc_insecure_channel_create_from_fd_import = (grpc_insecure_channel_create_from_fd_type) GetProcAddress(library, "grpc_insecure_channel_create_from_fd"); grpc_server_add_insecure_channel_from_fd_import = (grpc_server_add_insecure_channel_from_fd_type) GetProcAddress(library, "grpc_server_add_insecure_channel_from_fd"); grpc_use_signal_import = (grpc_use_signal_type) GetProcAddress(library, "grpc_use_signal"); diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index 8ea5807e4f..ed4b6264b0 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -275,6 +275,9 @@ extern grpc_channelz_get_channel_type grpc_channelz_get_channel_import; typedef char*(*grpc_channelz_get_subchannel_type)(intptr_t subchannel_id); extern grpc_channelz_get_subchannel_type grpc_channelz_get_subchannel_import; #define grpc_channelz_get_subchannel grpc_channelz_get_subchannel_import +typedef char*(*grpc_channelz_get_socket_type)(intptr_t socket_id); +extern grpc_channelz_get_socket_type grpc_channelz_get_socket_import; +#define grpc_channelz_get_socket grpc_channelz_get_socket_import typedef grpc_channel*(*grpc_insecure_channel_create_from_fd_type)(const char* target, int fd, const grpc_channel_args* args); extern grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import; #define grpc_insecure_channel_create_from_fd grpc_insecure_channel_create_from_fd_import diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb index cfed7ca12a..7710d34657 100755 --- a/src/ruby/pb/test/client.rb +++ b/src/ruby/pb/test/client.rb @@ -95,7 +95,7 @@ end # creates a test stub that accesses host:port securely. def create_stub(opts) - address = "#{opts.host}:#{opts.port}" + address = "#{opts.server_host}:#{opts.server_port}" # Provide channel args that request compression by default # for compression interop tests @@ -703,8 +703,8 @@ class NamedTests end # Args is used to hold the command line info. -Args = Struct.new(:default_service_account, :host, :host_override, - :oauth_scope, :port, :secure, :test_case, +Args = Struct.new(:default_service_account, :server_host, :host_override, + :oauth_scope, :server_port, :secure, :test_case, :use_test_ca) # validates the command line options, returning them as a Hash. @@ -715,7 +715,7 @@ def parse_args opts.on('--oauth_scope scope', 'Scope for OAuth tokens') { |v| args['oauth_scope'] = v } opts.on('--server_host SERVER_HOST', 'server hostname') do |v| - args['host'] = v + args['server_host'] = v end opts.on('--default_service_account email_address', 'email address of the default service account') do |v| @@ -725,7 +725,9 @@ def parse_args 'override host via a HTTP header') do |v| args['host_override'] = v end - opts.on('--server_port SERVER_PORT', 'server port') { |v| args['port'] = v } + opts.on('--server_port SERVER_PORT', 'server port') do |v| + args['server_port'] = v + end # instance_methods(false) gives only the methods defined in that class test_cases = NamedTests.instance_methods(false).map(&:to_s) test_case_list = test_cases.join(',') @@ -744,7 +746,7 @@ def parse_args end def _check_args(args) - %w(host port test_case).each do |a| + %w(server_host server_port test_case).each do |a| if args[a].nil? fail(OptionParser::MissingArgument, "please specify --#{a}") end diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c index b0af788796..3ebdb88a08 100644 --- a/test/core/surface/public_headers_must_be_c89.c +++ b/test/core/surface/public_headers_must_be_c89.c @@ -139,6 +139,7 @@ int main(int argc, char **argv) { printf("%lx", (unsigned long) grpc_channelz_get_servers); printf("%lx", (unsigned long) grpc_channelz_get_channel); printf("%lx", (unsigned long) grpc_channelz_get_subchannel); + printf("%lx", (unsigned long) grpc_channelz_get_socket); printf("%lx", (unsigned long) grpc_auth_property_iterator_next); printf("%lx", (unsigned long) grpc_auth_context_property_iterator); printf("%lx", (unsigned long) grpc_auth_context_peer_identity); diff --git a/test/cpp/end2end/channelz_service_test.cc b/test/cpp/end2end/channelz_service_test.cc index e96d68f93b..a597fd9c4b 100644 --- a/test/cpp/end2end/channelz_service_test.cc +++ b/test/cpp/end2end/channelz_service_test.cc @@ -43,6 +43,8 @@ using grpc::channelz::v1::GetChannelRequest; using grpc::channelz::v1::GetChannelResponse; using grpc::channelz::v1::GetServersRequest; using grpc::channelz::v1::GetServersResponse; +using grpc::channelz::v1::GetSocketRequest; +using grpc::channelz::v1::GetSocketResponse; using grpc::channelz::v1::GetSubchannelRequest; using grpc::channelz::v1::GetSubchannelResponse; using grpc::channelz::v1::GetTopChannelsRequest; @@ -71,6 +73,26 @@ class Proxy : public ::grpc::testing::EchoTestService::Service { return stubs_[idx]->Echo(client_context.get(), *request, response); } + Status BidiStream(ServerContext* server_context, + ServerReaderWriter<EchoResponse, EchoRequest>* + stream_from_client) override { + EchoRequest request; + EchoResponse response; + std::unique_ptr<ClientContext> client_context = + ClientContext::FromServerContext(*server_context); + + // always use the first proxy for streaming + auto stream_to_backend = stubs_[0]->BidiStream(client_context.get()); + while (stream_from_client->Read(&request)) { + stream_to_backend->Write(request); + stream_to_backend->Read(&response); + stream_from_client->Write(response); + } + + stream_to_backend->WritesDone(); + return stream_to_backend->Finish(); + } + private: std::vector<std::unique_ptr<::grpc::testing::EchoTestService::Stub>> stubs_; }; @@ -149,6 +171,21 @@ class ChannelzServerTest : public ::testing::Test { EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); } + void SendSuccessfulStream(int num_messages) { + EchoRequest request; + EchoResponse response; + request.set_message("Hello channelz"); + ClientContext context; + auto stream_to_proxy = echo_stub_->BidiStream(&context); + for (int i = 0; i < num_messages; ++i) { + EXPECT_TRUE(stream_to_proxy->Write(request)); + EXPECT_TRUE(stream_to_proxy->Read(&response)); + } + stream_to_proxy->WritesDone(); + Status s = stream_to_proxy->Finish(); + EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); + } + void SendFailedEcho(int channel_idx) { EchoRequest request; EchoResponse response; @@ -448,6 +485,121 @@ TEST_F(ChannelzServerTest, ServerCallTest) { kNumSuccess + kNumFailed + 1); } +TEST_F(ChannelzServerTest, ManySubchannelsAndSockets) { + ResetStubs(); + const int kNumChannels = 4; + ConfigureProxy(kNumChannels); + const int kNumSuccess = 10; + const int kNumFailed = 11; + for (int i = 0; i < kNumSuccess; ++i) { + SendSuccessfulEcho(0); + SendSuccessfulEcho(2); + } + for (int i = 0; i < kNumFailed; ++i) { + SendFailedEcho(1); + SendFailedEcho(2); + } + GetTopChannelsRequest gtc_request; + GetTopChannelsResponse gtc_response; + gtc_request.set_start_channel_id(0); + ClientContext context; + Status s = + channelz_stub_->GetTopChannels(&context, gtc_request, >c_response); + EXPECT_TRUE(s.ok()) << s.error_message(); + EXPECT_EQ(gtc_response.channel_size(), kNumChannels); + for (int i = 0; i < gtc_response.channel_size(); ++i) { + // if the channel sent no RPCs, then expect no subchannels to have been + // created. + if (gtc_response.channel(i).data().calls_started() == 0) { + EXPECT_EQ(gtc_response.channel(i).subchannel_ref_size(), 0); + continue; + } + // The resolver must return at least one address. + ASSERT_GT(gtc_response.channel(i).subchannel_ref_size(), 0); + // First grab the subchannel + GetSubchannelRequest get_subchannel_req; + GetSubchannelResponse get_subchannel_resp; + get_subchannel_req.set_subchannel_id( + gtc_response.channel(i).subchannel_ref(0).subchannel_id()); + ClientContext get_subchannel_ctx; + Status s = channelz_stub_->GetSubchannel( + &get_subchannel_ctx, get_subchannel_req, &get_subchannel_resp); + EXPECT_TRUE(s.ok()) << s.error_message(); + EXPECT_EQ(get_subchannel_resp.subchannel().socket_ref_size(), 1); + // Now grab the socket. + GetSocketRequest get_socket_req; + GetSocketResponse get_socket_resp; + ClientContext get_socket_ctx; + get_socket_req.set_socket_id( + get_subchannel_resp.subchannel().socket_ref(0).socket_id()); + s = channelz_stub_->GetSocket(&get_socket_ctx, get_socket_req, + &get_socket_resp); + EXPECT_TRUE(s.ok()) << s.error_message(); + // calls started == streams started AND stream succeeded. Since none of + // these RPCs were canceled, all of the streams will succeeded even though + // the RPCs they represent might have failed. + EXPECT_EQ(get_subchannel_resp.subchannel().data().calls_started(), + get_socket_resp.socket().data().streams_started()); + EXPECT_EQ(get_subchannel_resp.subchannel().data().calls_started(), + get_socket_resp.socket().data().streams_succeeded()); + // All of the calls were unary, so calls started == messages sent. + EXPECT_EQ(get_subchannel_resp.subchannel().data().calls_started(), + get_socket_resp.socket().data().messages_sent()); + // We only get responses when the RPC was successful, so + // calls succeeded == messages received. + EXPECT_EQ(get_subchannel_resp.subchannel().data().calls_succeeded(), + get_socket_resp.socket().data().messages_received()); + } +} + +TEST_F(ChannelzServerTest, StreamingRPC) { + ResetStubs(); + ConfigureProxy(1); + const int kNumMessages = 5; + SendSuccessfulStream(kNumMessages); + // Get the channel + GetChannelRequest get_channel_request; + GetChannelResponse get_channel_response; + get_channel_request.set_channel_id(GetChannelId(0)); + ClientContext get_channel_context; + Status s = channelz_stub_->GetChannel( + &get_channel_context, get_channel_request, &get_channel_response); + EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); + EXPECT_EQ(get_channel_response.channel().data().calls_started(), 1); + EXPECT_EQ(get_channel_response.channel().data().calls_succeeded(), 1); + EXPECT_EQ(get_channel_response.channel().data().calls_failed(), 0); + // Get the subchannel + ASSERT_GT(get_channel_response.channel().subchannel_ref_size(), 0); + GetSubchannelRequest get_subchannel_request; + GetSubchannelResponse get_subchannel_response; + ClientContext get_subchannel_context; + get_subchannel_request.set_subchannel_id( + get_channel_response.channel().subchannel_ref(0).subchannel_id()); + s = channelz_stub_->GetSubchannel(&get_subchannel_context, + get_subchannel_request, + &get_subchannel_response); + EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); + EXPECT_EQ(get_subchannel_response.subchannel().data().calls_started(), 1); + EXPECT_EQ(get_subchannel_response.subchannel().data().calls_succeeded(), 1); + EXPECT_EQ(get_subchannel_response.subchannel().data().calls_failed(), 0); + // Get the socket + ASSERT_GT(get_subchannel_response.subchannel().socket_ref_size(), 0); + GetSocketRequest get_socket_request; + GetSocketResponse get_socket_response; + ClientContext get_socket_context; + get_socket_request.set_socket_id( + get_subchannel_response.subchannel().socket_ref(0).socket_id()); + s = channelz_stub_->GetSocket(&get_socket_context, get_socket_request, + &get_socket_response); + EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); + EXPECT_EQ(get_socket_response.socket().data().streams_started(), 1); + EXPECT_EQ(get_socket_response.socket().data().streams_succeeded(), 1); + EXPECT_EQ(get_socket_response.socket().data().streams_failed(), 0); + EXPECT_EQ(get_socket_response.socket().data().messages_sent(), kNumMessages); + EXPECT_EQ(get_socket_response.socket().data().messages_received(), + kNumMessages); +} + } // namespace testing } // namespace grpc diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core index 9f7c2c9140..0c2569d0d8 100644 --- a/tools/doxygen/Doxyfile.core +++ b/tools/doxygen/Doxyfile.core @@ -771,6 +771,8 @@ doc/compression_cookbook.md \ doc/connection-backoff-interop-test-description.md \ doc/connection-backoff.md \ doc/connectivity-semantics-and-api.md \ +doc/core/grpc-client-server-polling-engine-usage.md \ +doc/core/grpc-cq.md \ doc/core/grpc-error.md \ doc/core/grpc-polling-engines.md \ doc/core/moving-to-c++.md \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 2ce6412d2c..a4206600c0 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -771,6 +771,8 @@ doc/compression_cookbook.md \ doc/connection-backoff-interop-test-description.md \ doc/connection-backoff.md \ doc/connectivity-semantics-and-api.md \ +doc/core/grpc-client-server-polling-engine-usage.md \ +doc/core/grpc-cq.md \ doc/core/grpc-error.md \ doc/core/grpc-polling-engines.md \ doc/core/moving-to-c++.md \ diff --git a/tools/internal_ci/helper_scripts/delete_nonartifacts.sh b/tools/internal_ci/helper_scripts/delete_nonartifacts.sh index c7d6ba6d44..01e9427e1c 100755 --- a/tools/internal_ci/helper_scripts/delete_nonartifacts.sh +++ b/tools/internal_ci/helper_scripts/delete_nonartifacts.sh @@ -24,4 +24,4 @@ cd "$(dirname "$0")/../../.." # after finishing each build. We only leave files we want to keep: # - reports and artifacts # - directory containing the kokoro scripts to prevent deleting a script while being executed. -time find . -type f -not -iname "*sponge_log.xml" -not -path "./reports/*" -not -path "./artifacts/*" -not -path "./tools/internal_ci/*" -exec rm -f {} + +time find . -type f -not -iname "*sponge_log.*" -not -path "./reports/*" -not -path "./artifacts/*" -not -path "./tools/internal_ci/*" -exec rm -f {} + diff --git a/tools/internal_ci/linux/grpc_basictests_c_cpp_dbg.cfg b/tools/internal_ci/linux/grpc_basictests_c_cpp_dbg.cfg index 4a0badf43b..07f7f0c659 100644 --- a/tools/internal_ci/linux/grpc_basictests_c_cpp_dbg.cfg +++ b/tools/internal_ci/linux/grpc_basictests_c_cpp_dbg.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_basictests_c_cpp_opt.cfg b/tools/internal_ci/linux/grpc_basictests_c_cpp_opt.cfg index a2cfe021e1..8f2813febf 100644 --- a/tools/internal_ci/linux/grpc_basictests_c_cpp_opt.cfg +++ b/tools/internal_ci/linux/grpc_basictests_c_cpp_opt.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_basictests_multilang.cfg b/tools/internal_ci/linux/grpc_basictests_multilang.cfg index 4433d14cd7..f8a5a4aea5 100644 --- a/tools/internal_ci/linux/grpc_basictests_multilang.cfg +++ b/tools/internal_ci/linux/grpc_basictests_multilang.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_build_artifacts.cfg b/tools/internal_ci/linux/grpc_build_artifacts.cfg index 88fc6b7b35..1e04a1e788 100644 --- a/tools/internal_ci/linux/grpc_build_artifacts.cfg +++ b/tools/internal_ci/linux/grpc_build_artifacts.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_build_artifacts.sh" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/linux/grpc_build_artifacts_extra.cfg b/tools/internal_ci/linux/grpc_build_artifacts_extra.cfg index 619e3ea3a9..2737e2f345 100644 --- a/tools/internal_ci/linux/grpc_build_artifacts_extra.cfg +++ b/tools/internal_ci/linux/grpc_build_artifacts_extra.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_build_artifacts_extra.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/linux/grpc_build_artifacts_extra_release.cfg b/tools/internal_ci/linux/grpc_build_artifacts_extra_release.cfg index 619e3ea3a9..2737e2f345 100644 --- a/tools/internal_ci/linux/grpc_build_artifacts_extra_release.cfg +++ b/tools/internal_ci/linux/grpc_build_artifacts_extra_release.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_build_artifacts_extra.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg b/tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg index 11c211fd2b..9a430db0f9 100644 --- a/tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg +++ b/tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_build_submodule_at_head.sh" timeout_mins: 180 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_build_packages.cfg b/tools/internal_ci/linux/grpc_build_packages.cfg index 6a4a163dfc..23a676cf72 100644 --- a/tools/internal_ci/linux/grpc_build_packages.cfg +++ b/tools/internal_ci/linux/grpc_build_packages.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_build_packages.sh" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg b/tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg index 2f08e15e63..aef6a7a1dc 100644 --- a/tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg +++ b/tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_build_submodule_at_head.sh" timeout_mins: 180 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_coverage.cfg b/tools/internal_ci/linux/grpc_coverage.cfg index 794a51d3f1..6eb37b7dec 100644 --- a/tools/internal_ci/linux/grpc_coverage.cfg +++ b/tools/internal_ci/linux/grpc_coverage.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_coverage.sh" timeout_mins: 420 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_distribtests.cfg b/tools/internal_ci/linux/grpc_distribtests.cfg index 0f1d79355a..848d571333 100644 --- a/tools/internal_ci/linux/grpc_distribtests.cfg +++ b/tools/internal_ci/linux/grpc_distribtests.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_distribtests.sh" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/linux/grpc_distribtests_standalone.cfg b/tools/internal_ci/linux/grpc_distribtests_standalone.cfg index bc6c8e8f80..734bdfd78a 100644 --- a/tools/internal_ci/linux/grpc_distribtests_standalone.cfg +++ b/tools/internal_ci/linux/grpc_distribtests_standalone.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_distribtests_standalone.sh" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/linux/grpc_full_performance_master.cfg b/tools/internal_ci/linux/grpc_full_performance_master.cfg index 8852130a13..8ad93ee052 100644 --- a/tools/internal_ci/linux/grpc_full_performance_master.cfg +++ b/tools/internal_ci/linux/grpc_full_performance_master.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_full_performance_master.sh" timeout_mins: 600 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "**/perf_reports/**" } } diff --git a/tools/internal_ci/linux/grpc_full_performance_release.cfg b/tools/internal_ci/linux/grpc_full_performance_release.cfg index e9a4bcdcaf..11a95e889f 100644 --- a/tools/internal_ci/linux/grpc_full_performance_release.cfg +++ b/tools/internal_ci/linux/grpc_full_performance_release.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_full_performance_release.sh" timeout_mins: 600 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "**/perf_reports/**" } } diff --git a/tools/internal_ci/linux/grpc_interop_alts.cfg b/tools/internal_ci/linux/grpc_interop_alts.cfg index bda76faf44..4684aba96b 100644 --- a/tools/internal_ci/linux/grpc_interop_alts.cfg +++ b/tools/internal_ci/linux/grpc_interop_alts.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_interop_tests.sh" timeout_mins: 60 action { define_artifacts { - regex: "**/sponge_log.xml" + regex: "**/sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_interop_matrix.cfg b/tools/internal_ci/linux/grpc_interop_matrix.cfg index ae59e930f7..696a55c0df 100644 --- a/tools/internal_ci/linux/grpc_interop_matrix.cfg +++ b/tools/internal_ci/linux/grpc_interop_matrix.cfg @@ -20,7 +20,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_interop_matrix.sh" timeout_mins: 300 action { define_artifacts { - regex: "**/sponge_log.xml" + regex: "**/sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_interop_tocloud.cfg b/tools/internal_ci/linux/grpc_interop_tocloud.cfg index 81f2fe9dec..9b35adcece 100644 --- a/tools/internal_ci/linux/grpc_interop_tocloud.cfg +++ b/tools/internal_ci/linux/grpc_interop_tocloud.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_interop_tests.sh" timeout_mins: 60 action { define_artifacts { - regex: "**/sponge_log.xml" + regex: "**/sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_interop_toprod.cfg b/tools/internal_ci/linux/grpc_interop_toprod.cfg index 8dfc529947..de4db81e6b 100644 --- a/tools/internal_ci/linux/grpc_interop_toprod.cfg +++ b/tools/internal_ci/linux/grpc_interop_toprod.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_interop_tests.sh" timeout_mins: 60 action { define_artifacts { - regex: "**/sponge_log.xml" + regex: "**/sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_portability.cfg b/tools/internal_ci/linux/grpc_portability.cfg index 76e5028477..f417f24bb1 100644 --- a/tools/internal_ci/linux/grpc_portability.cfg +++ b/tools/internal_ci/linux/grpc_portability.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_portability_build_only.cfg b/tools/internal_ci/linux/grpc_portability_build_only.cfg index 4acd9353fb..fab9dde3e5 100644 --- a/tools/internal_ci/linux/grpc_portability_build_only.cfg +++ b/tools/internal_ci/linux/grpc_portability_build_only.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 180 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_publish_packages.cfg b/tools/internal_ci/linux/grpc_publish_packages.cfg index 82d571d642..dc9fe7d0a7 100644 --- a/tools/internal_ci/linux/grpc_publish_packages.cfg +++ b/tools/internal_ci/linux/grpc_publish_packages.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_publish_packages.sh" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/linux/grpc_pull_request_sanity.cfg b/tools/internal_ci/linux/grpc_pull_request_sanity.cfg index b20d2ffba8..704d5c6cbc 100644 --- a/tools/internal_ci/linux/grpc_pull_request_sanity.cfg +++ b/tools/internal_ci/linux/grpc_pull_request_sanity.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 30 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/grpc_sanity.cfg b/tools/internal_ci/linux/grpc_sanity.cfg index 9f65918e23..341471bbb5 100644 --- a/tools/internal_ci/linux/grpc_sanity.cfg +++ b/tools/internal_ci/linux/grpc_sanity.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 40 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_dbg.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_dbg.cfg index 8124f5c1b3..8a67d28ce4 100644 --- a/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_dbg.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_dbg.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_opt.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_opt.cfg index ecedc73e44..a681978b6e 100644 --- a/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_opt.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_opt.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_c_dbg.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_c_dbg.cfg index 577cb28ae5..249ecc99ce 100644 --- a/tools/internal_ci/linux/pull_request/grpc_basictests_c_dbg.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_basictests_c_dbg.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_c_opt.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_c_opt.cfg index 9e0b724b2e..665d7f3d2c 100644 --- a/tools/internal_ci/linux/pull_request/grpc_basictests_c_opt.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_basictests_c_opt.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_dbg.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_dbg.cfg index 0fda74cf44..163274d4dc 100644 --- a/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_dbg.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_dbg.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_opt.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_opt.cfg index 199a8905d9..b65cd3e05c 100644 --- a/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_opt.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_basictests_cpp_opt.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_multilang.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_multilang.cfg index f7e8d8ad33..59f38f0d1b 100644 --- a/tools/internal_ci/linux/pull_request/grpc_basictests_multilang.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_basictests_multilang.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_interop_alts.cfg b/tools/internal_ci/linux/pull_request/grpc_interop_alts.cfg index c1253b30f7..e91a612878 100644 --- a/tools/internal_ci/linux/pull_request/grpc_interop_alts.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_interop_alts.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_interop_tests.sh" timeout_mins: 60 action { define_artifacts { - regex: "**/sponge_log.xml" + regex: "**/sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_interop_tocloud.cfg b/tools/internal_ci/linux/pull_request/grpc_interop_tocloud.cfg index cb18e8e868..b1eb575605 100644 --- a/tools/internal_ci/linux/pull_request/grpc_interop_tocloud.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_interop_tocloud.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_interop_tests.sh" timeout_mins: 60 action { define_artifacts { - regex: "**/sponge_log.xml" + regex: "**/sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_interop_toprod.cfg b/tools/internal_ci/linux/pull_request/grpc_interop_toprod.cfg index d14c79a1f6..7321effc12 100644 --- a/tools/internal_ci/linux/pull_request/grpc_interop_toprod.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_interop_toprod.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_interop_tests.sh" timeout_mins: 60 action { define_artifacts { - regex: "**/sponge_log.xml" + regex: "**/sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_microbenchmark_diff.cfg b/tools/internal_ci/linux/pull_request/grpc_microbenchmark_diff.cfg index 9269c345f0..47301d6141 100644 --- a/tools/internal_ci/linux/pull_request/grpc_microbenchmark_diff.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_microbenchmark_diff.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_microbenchmark_diff.sh" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_sanity.cfg b/tools/internal_ci/linux/pull_request/grpc_sanity.cfg index 0f83299aab..276c34f0cf 100644 --- a/tools/internal_ci/linux/pull_request/grpc_sanity.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_sanity.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 40 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/pull_request/grpc_trickle_diff.cfg b/tools/internal_ci/linux/pull_request/grpc_trickle_diff.cfg index e86b3ab475..78358eac28 100644 --- a/tools/internal_ci/linux/pull_request/grpc_trickle_diff.cfg +++ b/tools/internal_ci/linux/pull_request/grpc_trickle_diff.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_trickle_diff.sh" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/grpc_c_asan.cfg b/tools/internal_ci/linux/sanitizer/grpc_c_asan.cfg index 06a4372ce2..d6d70677dd 100644 --- a/tools/internal_ci/linux/sanitizer/grpc_c_asan.cfg +++ b/tools/internal_ci/linux/sanitizer/grpc_c_asan.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/grpc_c_msan.cfg b/tools/internal_ci/linux/sanitizer/grpc_c_msan.cfg index f875327c1b..7b22c6afef 100644 --- a/tools/internal_ci/linux/sanitizer/grpc_c_msan.cfg +++ b/tools/internal_ci/linux/sanitizer/grpc_c_msan.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/grpc_c_tsan.cfg b/tools/internal_ci/linux/sanitizer/grpc_c_tsan.cfg index 6658a804d8..6c9dd6ef8d 100644 --- a/tools/internal_ci/linux/sanitizer/grpc_c_tsan.cfg +++ b/tools/internal_ci/linux/sanitizer/grpc_c_tsan.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/grpc_c_ubsan.cfg b/tools/internal_ci/linux/sanitizer/grpc_c_ubsan.cfg index 957a91ef2b..8700c74c8d 100644 --- a/tools/internal_ci/linux/sanitizer/grpc_c_ubsan.cfg +++ b/tools/internal_ci/linux/sanitizer/grpc_c_ubsan.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.cfg b/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.cfg index dbbfce90cb..02162d860b 100644 --- a/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.cfg +++ b/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.cfg b/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.cfg index fb0cefa160..95582184ed 100644 --- a/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.cfg +++ b/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_asan.cfg b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_asan.cfg index 1daf7a514e..d444596a81 100644 --- a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_asan.cfg +++ b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_asan.cfg @@ -20,7 +20,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_msan.cfg b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_msan.cfg index a8503b7bcb..3891cc37e8 100644 --- a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_msan.cfg +++ b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_msan.cfg @@ -20,7 +20,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_tsan.cfg b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_tsan.cfg index 12af4581eb..91ce1627af 100644 --- a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_tsan.cfg +++ b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_tsan.cfg @@ -20,7 +20,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_ubsan.cfg b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_ubsan.cfg index 0d3803bf23..fcdc2de37b 100644 --- a/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_ubsan.cfg +++ b/tools/internal_ci/linux/sanitizer/pull_request/grpc_c_ubsan.cfg @@ -20,7 +20,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_asan.cfg b/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_asan.cfg index 557561810b..de2a74051b 100644 --- a/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_asan.cfg +++ b/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_asan.cfg @@ -20,7 +20,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_tsan.cfg b/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_tsan.cfg index cb15ca34fd..51c291f7df 100644 --- a/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_tsan.cfg +++ b/tools/internal_ci/linux/sanitizer/pull_request/grpc_cpp_tsan.cfg @@ -20,7 +20,7 @@ build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" timeout_mins: 1440 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/grpc_basictests_dbg.cfg b/tools/internal_ci/macos/grpc_basictests_dbg.cfg index 53bda1ff0a..e6f9c7ec87 100644 --- a/tools/internal_ci/macos/grpc_basictests_dbg.cfg +++ b/tools/internal_ci/macos/grpc_basictests_dbg.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/grpc_basictests_opt.cfg b/tools/internal_ci/macos/grpc_basictests_opt.cfg index d359eb601a..f2a83fe95a 100644 --- a/tools/internal_ci/macos/grpc_basictests_opt.cfg +++ b/tools/internal_ci/macos/grpc_basictests_opt.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/grpc_build_artifacts.cfg b/tools/internal_ci/macos/grpc_build_artifacts.cfg index 4da61faed3..c73cf4359f 100644 --- a/tools/internal_ci/macos/grpc_build_artifacts.cfg +++ b/tools/internal_ci/macos/grpc_build_artifacts.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/macos/grpc_distribtests.cfg b/tools/internal_ci/macos/grpc_distribtests.cfg index ae88f39b90..156ec6fe33 100644 --- a/tools/internal_ci/macos/grpc_distribtests.cfg +++ b/tools/internal_ci/macos/grpc_distribtests.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/macos/grpc_interop.cfg b/tools/internal_ci/macos/grpc_interop.cfg index b4b1b15cb4..434ecd19c4 100644 --- a/tools/internal_ci/macos/grpc_interop.cfg +++ b/tools/internal_ci/macos/grpc_interop.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/grpc_interop_toprod.cfg b/tools/internal_ci/macos/grpc_interop_toprod.cfg index c92c397daa..2cfc8a2d6d 100644 --- a/tools/internal_ci/macos/grpc_interop_toprod.cfg +++ b/tools/internal_ci/macos/grpc_interop_toprod.cfg @@ -21,7 +21,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/interop/service_account/GrpcTes timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg index 30c01d3e2f..c759397b78 100644 --- a/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg +++ b/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_opt.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_opt.cfg index b63ee713bc..4d68341405 100644 --- a/tools/internal_ci/macos/pull_request/grpc_basictests_opt.cfg +++ b/tools/internal_ci/macos/pull_request/grpc_basictests_opt.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/pull_request/grpc_interop.cfg b/tools/internal_ci/macos/pull_request/grpc_interop.cfg index b4b1b15cb4..434ecd19c4 100644 --- a/tools/internal_ci/macos/pull_request/grpc_interop.cfg +++ b/tools/internal_ci/macos/pull_request/grpc_interop.cfg @@ -20,7 +20,7 @@ gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0e timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/macos/pull_request/grpc_ios_binary_size.cfg b/tools/internal_ci/macos/pull_request/grpc_ios_binary_size.cfg index 8942bc7ba7..fb215bdf99 100644 --- a/tools/internal_ci/macos/pull_request/grpc_ios_binary_size.cfg +++ b/tools/internal_ci/macos/pull_request/grpc_ios_binary_size.cfg @@ -20,7 +20,7 @@ timeout_mins: 60 gfile_resources: "/bigstore/grpc-testing-secrets/github_credentials/oauth_token.txt" action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/grpc_basictests.cfg b/tools/internal_ci/windows/grpc_basictests.cfg index 8e644e4c5e..fcf5237bf3 100644 --- a/tools/internal_ci/windows/grpc_basictests.cfg +++ b/tools/internal_ci/windows/grpc_basictests.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/grpc_basictests_dbg.cfg b/tools/internal_ci/windows/grpc_basictests_dbg.cfg index 28d53cdc7b..4e5e7b6545 100644 --- a/tools/internal_ci/windows/grpc_basictests_dbg.cfg +++ b/tools/internal_ci/windows/grpc_basictests_dbg.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/grpc_basictests_opt.cfg b/tools/internal_ci/windows/grpc_basictests_opt.cfg index 4b7a965977..f5db6a9897 100644 --- a/tools/internal_ci/windows/grpc_basictests_opt.cfg +++ b/tools/internal_ci/windows/grpc_basictests_opt.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/grpc_build_artifacts.cfg b/tools/internal_ci/windows/grpc_build_artifacts.cfg index 38b0abd519..f45cfda121 100644 --- a/tools/internal_ci/windows/grpc_build_artifacts.cfg +++ b/tools/internal_ci/windows/grpc_build_artifacts.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_build_artifacts.bat" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/windows/grpc_build_packages.cfg b/tools/internal_ci/windows/grpc_build_packages.cfg index 65a8b1eef3..b351bbb11b 100644 --- a/tools/internal_ci/windows/grpc_build_packages.cfg +++ b/tools/internal_ci/windows/grpc_build_packages.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_build_packages.bat" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/windows/grpc_distribtests.cfg b/tools/internal_ci/windows/grpc_distribtests.cfg index 1766e601e5..e12d4bf98d 100644 --- a/tools/internal_ci/windows/grpc_distribtests.cfg +++ b/tools/internal_ci/windows/grpc_distribtests.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_distribtests.bat" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/windows/grpc_distribtests_standalone.cfg b/tools/internal_ci/windows/grpc_distribtests_standalone.cfg index 33a50fdc45..7f32ba4c70 100644 --- a/tools/internal_ci/windows/grpc_distribtests_standalone.cfg +++ b/tools/internal_ci/windows/grpc_distribtests_standalone.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_distribtests_standalone.bat" timeout_mins: 120 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" regex: "github/grpc/artifacts/**" } diff --git a/tools/internal_ci/windows/grpc_portability.cfg b/tools/internal_ci/windows/grpc_portability.cfg index 94e71753ef..aa86c06632 100644 --- a/tools/internal_ci/windows/grpc_portability.cfg +++ b/tools/internal_ci/windows/grpc_portability.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/grpc_portability_build_only.cfg b/tools/internal_ci/windows/grpc_portability_build_only.cfg index 3bc27f1f24..3dace627eb 100644 --- a/tools/internal_ci/windows/grpc_portability_build_only.cfg +++ b/tools/internal_ci/windows/grpc_portability_build_only.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests.cfg index 91777cd7cb..a8e6f0675b 100644 --- a/tools/internal_ci/windows/pull_request/grpc_basictests.cfg +++ b/tools/internal_ci/windows/pull_request/grpc_basictests.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests_dbg.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests_dbg.cfg index 81a1e9d3c8..ce957232fd 100644 --- a/tools/internal_ci/windows/pull_request/grpc_basictests_dbg.cfg +++ b/tools/internal_ci/windows/pull_request/grpc_basictests_dbg.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests_opt.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests_opt.cfg index 3bb6510ca7..7d2abbfb19 100644 --- a/tools/internal_ci/windows/pull_request/grpc_basictests_opt.cfg +++ b/tools/internal_ci/windows/pull_request/grpc_basictests_opt.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/internal_ci/windows/pull_request/grpc_portability.cfg b/tools/internal_ci/windows/pull_request/grpc_portability.cfg index 2bda487629..6f332416fe 100644 --- a/tools/internal_ci/windows/pull_request/grpc_portability.cfg +++ b/tools/internal_ci/windows/pull_request/grpc_portability.cfg @@ -19,7 +19,7 @@ build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" timeout_mins: 240 action { define_artifacts { - regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.*" regex: "github/grpc/reports/**" } } diff --git a/tools/run_tests/python_utils/jobset.py b/tools/run_tests/python_utils/jobset.py index 561f453da7..b732e1e03e 100755 --- a/tools/run_tests/python_utils/jobset.py +++ b/tools/run_tests/python_utils/jobset.py @@ -176,13 +176,15 @@ class JobSpec(object): timeout_retries=0, kill_handler=None, cpu_cost=1.0, - verbose_success=False): + verbose_success=False, + logfilename=None): """ Arguments: cmdline: a list of arguments to pass as the command line environ: a dictionary of environment variables to set in the child process kill_handler: a handler that will be called whenever job.kill() is invoked cpu_cost: number of cores per second this job needs + logfilename: use given file to store job's output, rather than using a temporary file """ if environ is None: environ = {} @@ -197,6 +199,11 @@ class JobSpec(object): self.kill_handler = kill_handler self.cpu_cost = cpu_cost self.verbose_success = verbose_success + self.logfilename = logfilename + if self.logfilename and self.flake_retries != 0 and self.timeout_retries != 0: + # Forbidden to avoid overwriting the test log when retrying. + raise Exception( + 'Cannot use custom logfile when retries are enabled') def identity(self): return '%r %r' % (self.cmdline, self.environ) @@ -261,7 +268,15 @@ class Job(object): return self._spec def start(self): - self._tempfile = tempfile.TemporaryFile() + if self._spec.logfilename: + # make sure the log directory exists + logfile_dir = os.path.dirname( + os.path.abspath(self._spec.logfilename)) + if not os.path.exists(logfile_dir): + os.makedirs(logfile_dir) + self._logfile = open(self._spec.logfilename, 'w+') + else: + self._logfile = tempfile.TemporaryFile() env = dict(os.environ) env.update(self._spec.environ) env.update(self._add_env) @@ -277,7 +292,7 @@ class Job(object): measure_cpu_costs = False try_start = lambda: subprocess.Popen(args=cmdline, stderr=subprocess.STDOUT, - stdout=self._tempfile, + stdout=self._logfile, cwd=self._spec.cwd, shell=self._spec.shell, env=env) @@ -300,7 +315,7 @@ class Job(object): """Poll current state of the job. Prints messages at completion.""" def stdout(self=self): - stdout = read_from_start(self._tempfile) + stdout = read_from_start(self._logfile) self.result.message = stdout[-_MAX_RESULT_SIZE:] return stdout diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py index 9bbc2e3e0e..e8d6b59687 100755 --- a/tools/run_tests/run_interop_tests.py +++ b/tools/run_tests/run_interop_tests.py @@ -777,12 +777,14 @@ def cloud_to_prod_jobspec(language, ] if transport_security == 'tls': transport_security_options = ['--use_tls=true'] - elif transport_security == 'google_default_credentials' and language == 'c++': + elif transport_security == 'google_default_credentials' and str( + language) in ['c++', 'go']: transport_security_options = [ '--custom_credentials_type=google_default_credentials' ] else: - print('Invalid transport security option.') + print('Invalid transport security option %s in cloud_to_prod_jobspec.' % + transport_security) sys.exit(1) cmdargs = cmdargs + transport_security_options environ = dict(language.cloud_to_prod_env(), **language.global_env()) @@ -817,8 +819,9 @@ def cloud_to_prod_jobspec(language, cmdline=cmdline, cwd=cwd, environ=environ, - shortname='%s:%s:%s:%s' % (suite_name, language, server_host_nickname, - test_case), + shortname='%s:%s:%s:%s:%s' % + (suite_name, language, server_host_nickname, test_case, + transport_security), timeout_seconds=_TEST_TIMEOUT, flake_retries=4 if args.allow_flakes else 0, timeout_retries=2 if args.allow_flakes else 0, @@ -848,7 +851,8 @@ def cloud_to_cloud_jobspec(language, elif transport_security == 'insecure': interop_only_options += ['--use_tls=false'] else: - print('Invalid transport security option.') + print('Invalid transport security option %s in cloud_to_cloud_jobspec.' + % transport_security) sys.exit(1) client_test_case = test_case @@ -903,8 +907,8 @@ def cloud_to_cloud_jobspec(language, cmdline=cmdline, cwd=cwd, environ=environ, - shortname='cloud_to_cloud:%s:%s_server:%s' % (language, server_name, - test_case), + shortname='cloud_to_cloud:%s:%s_server:%s:%s' % + (language, server_name, test_case, transport_security), timeout_seconds=_TEST_TIMEOUT, flake_retries=4 if args.allow_flakes else 0, timeout_retries=2 if args.allow_flakes else 0, @@ -929,7 +933,8 @@ def server_jobspec(language, elif transport_security == 'insecure': server_cmd += ['--use_tls=false'] else: - print('Invalid transport security option.') + print('Invalid transport security option %s in server_jobspec.' % + transport_security) sys.exit(1) cmdline = bash_cmdline(language.server_cmd(server_cmd)) environ = language.global_env() @@ -1318,7 +1323,7 @@ try: service_account_key_file, transport_security='tls') jobs.append(tls_test_job) - if language == 'c++': + if str(language) in ['c++', 'go']: google_default_creds_test_job = cloud_to_prod_jobspec( language, test_case, @@ -1370,7 +1375,9 @@ try: service_account_key_file, transport_security='tls') jobs.append(tls_test_job) - if language == 'c++': + if str(language) in [ + 'go' + ]: # Add more languages to the list to turn on tests. google_default_creds_test_job = cloud_to_prod_jobspec( language, test_case, @@ -1378,6 +1385,7 @@ try: prod_servers[server_host_nickname], docker_image=docker_images.get( str(language)), + auth=True, manual_cmd_log=client_manual_cmd_log, service_account_key_file=args. service_account_key_file, diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py index 1bbec94ee1..00fc68ad17 100755 --- a/tools/run_tests/run_tests_matrix.py +++ b/tools/run_tests/run_tests_matrix.py @@ -43,9 +43,6 @@ _OBJC_RUNTESTS_TIMEOUT = 90 * 60 # Number of jobs assigned to each run_tests.py instance _DEFAULT_INNER_JOBS = 2 -# report suffix is important for reports to get picked up by internal CI -_REPORT_SUFFIX = 'sponge_log.xml' - def _safe_report_name(name): """Reports with '+' in target name won't show correctly in ResultStore""" @@ -54,7 +51,15 @@ def _safe_report_name(name): def _report_filename(name): """Generates report file name with directory structure that leads to better presentation by internal CI""" - return '%s/%s' % (_safe_report_name(name), _REPORT_SUFFIX) + # 'sponge_log.xml' suffix must be there for results to get recognized by kokoro. + return '%s/%s' % (_safe_report_name(name), 'sponge_log.xml') + + +def _report_logfilename(name): + """Generates log file name that corresponds to name generated by _report_filename""" + # 'sponge_log.log' suffix must be there for log to get recognized as "target log" + # for the corresponding 'sponge_log.xml' report. + return '%s/%s' % (_safe_report_name(name), 'sponge_log.log') def _docker_jobspec(name, @@ -75,7 +80,8 @@ def _docker_jobspec(name, ] + runtests_args, environ=runtests_envs, shortname='run_tests_%s' % name, - timeout_seconds=timeout_seconds) + timeout_seconds=timeout_seconds, + logfilename=_report_logfilename(name)) return test_job @@ -102,7 +108,8 @@ def _workspace_jobspec(name, ] + runtests_args, environ=env, shortname='run_tests_%s' % name, - timeout_seconds=timeout_seconds) + timeout_seconds=timeout_seconds, + logfilename=_report_logfilename(name)) return test_job |