diff options
34 files changed, 470 insertions, 487 deletions
diff --git a/examples/tips/client.h b/examples/tips/client.h index 3f4f1fd836..6ae9d50658 100644 --- a/examples/tips/client.h +++ b/examples/tips/client.h @@ -31,6 +31,9 @@ * */ +#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_ +#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_ + #include <grpc++/channel_interface.h> #include <grpc++/status.h> @@ -52,3 +55,5 @@ class Client { } // namespace tips } // namespace examples } // namespace grpc + +#endif // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_ diff --git a/examples/tips/client_main.cc b/examples/tips/client_main.cc index 17567b6f17..f4a3b09601 100644 --- a/examples/tips/client_main.cc +++ b/examples/tips/client_main.cc @@ -41,30 +41,29 @@ #include "examples/tips/client.h" #include "test/cpp/util/create_test_channel.h" -DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls."); -DEFINE_bool(use_prod_roots, true, "True to use SSL roots for production GFE"); -DEFINE_int32(server_port, 0, "Server port."); -DEFINE_string(server_host, "127.0.0.1", "Server host to connect to"); -DEFINE_string(server_host_override, "foo.test.google.com", - "Override the server host which is sent in HTTP header"); +DEFINE_int32(server_port, 443, "Server port."); +DEFINE_string(server_host, + "pubsub-staging.googleapis.com", "Server host to connect to"); int main(int argc, char** argv) { grpc_init(); google::ParseCommandLineFlags(&argc, &argv, true); gpr_log(GPR_INFO, "Start TIPS client"); - GPR_ASSERT(FLAGS_server_port); const int host_port_buf_size = 1024; char host_port[host_port_buf_size]; snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(), FLAGS_server_port); std::shared_ptr<grpc::ChannelInterface> channel( - grpc::CreateTestChannel(host_port, FLAGS_server_host_override, - FLAGS_enable_ssl, FLAGS_use_prod_roots)); + grpc::CreateTestChannel(host_port, + FLAGS_server_host, + true, // enable SSL + true)); // use prod roots grpc::examples::tips::Client client(channel); grpc::Status s = client.CreateTopic("test"); + gpr_log(GPR_INFO, "return code %d", s.code()); GPR_ASSERT(s.IsOk()); channel.reset(); diff --git a/src/core/channel/call_op_string.c b/src/core/channel/call_op_string.c index 789913901a..d36d51e789 100644 --- a/src/core/channel/call_op_string.c +++ b/src/core/channel/call_op_string.c @@ -41,110 +41,86 @@ #include <grpc/support/alloc.h> #include <grpc/support/useful.h> -#define MAX_APPEND 1024 +static void put_metadata(gpr_strvec *b, grpc_mdelem *md) { + gpr_strvec_add(b, gpr_strdup(" key=")); + gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice), + GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT)); -typedef struct { - size_t cap; - size_t len; - char *buffer; -} buf; - -static void bprintf(buf *b, const char *fmt, ...) { - va_list arg; - if (b->len + MAX_APPEND > b->cap) { - b->cap = GPR_MAX(b->len + MAX_APPEND, b->cap * 3 / 2); - b->buffer = gpr_realloc(b->buffer, b->cap); - } - va_start(arg, fmt); - b->len += vsprintf(b->buffer + b->len, fmt, arg); - va_end(arg); -} - -static void bputs(buf *b, const char *s) { - size_t slen = strlen(s); - if (b->len + slen + 1 > b->cap) { - b->cap = GPR_MAX(b->len + slen + 1, b->cap * 3 / 2); - b->buffer = gpr_realloc(b->buffer, b->cap); - } - strcat(b->buffer, s); - b->len += slen; -} - -static void put_metadata(buf *b, grpc_mdelem *md) { - char *txt; - - txt = gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice), - GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT); - bputs(b, " key="); - bputs(b, txt); - gpr_free(txt); - - txt = gpr_hexdump((char *)GPR_SLICE_START_PTR(md->value->slice), - GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT); - bputs(b, " value="); - bputs(b, txt); - gpr_free(txt); + gpr_strvec_add(b, gpr_strdup(" value=")); + gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->value->slice), + GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT)); } char *grpc_call_op_string(grpc_call_op *op) { - buf b = {0, 0, 0}; + char *tmp; + char *out; + + gpr_strvec b; + gpr_strvec_init(&b); switch (op->dir) { case GRPC_CALL_DOWN: - bprintf(&b, ">"); + gpr_strvec_add(&b, gpr_strdup(">")); break; case GRPC_CALL_UP: - bprintf(&b, "<"); + gpr_strvec_add(&b, gpr_strdup("<")); break; } switch (op->type) { case GRPC_SEND_METADATA: - bprintf(&b, "SEND_METADATA"); + gpr_strvec_add(&b, gpr_strdup("SEND_METADATA")); put_metadata(&b, op->data.metadata); break; case GRPC_SEND_DEADLINE: - bprintf(&b, "SEND_DEADLINE %d.%09d", op->data.deadline.tv_sec, + gpr_asprintf(&tmp, "SEND_DEADLINE %d.%09d", op->data.deadline.tv_sec, op->data.deadline.tv_nsec); + gpr_strvec_add(&b, tmp); break; case GRPC_SEND_START: - bprintf(&b, "SEND_START pollset=%p", op->data.start.pollset); + gpr_asprintf(&tmp, "SEND_START pollset=%p", op->data.start.pollset); + gpr_strvec_add(&b, tmp); break; case GRPC_SEND_MESSAGE: - bprintf(&b, "SEND_MESSAGE"); + gpr_strvec_add(&b, gpr_strdup("SEND_MESSAGE")); break; case GRPC_SEND_FINISH: - bprintf(&b, "SEND_FINISH"); + gpr_strvec_add(&b, gpr_strdup("SEND_FINISH")); break; case GRPC_REQUEST_DATA: - bprintf(&b, "REQUEST_DATA"); + gpr_strvec_add(&b, gpr_strdup("REQUEST_DATA")); break; case GRPC_RECV_METADATA: - bprintf(&b, "RECV_METADATA"); + gpr_strvec_add(&b, gpr_strdup("RECV_METADATA")); put_metadata(&b, op->data.metadata); break; case GRPC_RECV_DEADLINE: - bprintf(&b, "RECV_DEADLINE %d.%09d", op->data.deadline.tv_sec, + gpr_asprintf(&tmp, "RECV_DEADLINE %d.%09d", op->data.deadline.tv_sec, op->data.deadline.tv_nsec); + gpr_strvec_add(&b, tmp); break; case GRPC_RECV_END_OF_INITIAL_METADATA: - bprintf(&b, "RECV_END_OF_INITIAL_METADATA"); + gpr_strvec_add(&b, gpr_strdup("RECV_END_OF_INITIAL_METADATA")); break; case GRPC_RECV_MESSAGE: - bprintf(&b, "RECV_MESSAGE"); + gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE")); break; case GRPC_RECV_HALF_CLOSE: - bprintf(&b, "RECV_HALF_CLOSE"); + gpr_strvec_add(&b, gpr_strdup("RECV_HALF_CLOSE")); break; case GRPC_RECV_FINISH: - bprintf(&b, "RECV_FINISH"); + gpr_strvec_add(&b, gpr_strdup("RECV_FINISH")); break; case GRPC_CANCEL_OP: - bprintf(&b, "CANCEL_OP"); + gpr_strvec_add(&b, gpr_strdup("CANCEL_OP")); break; } - bprintf(&b, " flags=0x%08x", op->flags); + gpr_asprintf(&tmp, " flags=0x%08x", op->flags); + gpr_strvec_add(&b, tmp); + + out = gpr_strvec_flatten(&b, NULL); + gpr_strvec_destroy(&b); - return b.buffer; + return out; } void grpc_call_log_op(char *file, int line, gpr_log_severity severity, diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index 47c0ed3b88..55486ed5c3 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -450,6 +450,7 @@ static void recv_batch(void *user_data, grpc_transport *transport, (int)calld->incoming_message.length, (int)calld->incoming_message_length); recv_error(chand, calld, __LINE__, message); + gpr_free(message); } call_op.type = GRPC_RECV_HALF_CLOSE; call_op.dir = GRPC_CALL_UP; diff --git a/src/core/httpcli/format_request.c b/src/core/httpcli/format_request.c index 7a44f1266f..58bb7c740e 100644 --- a/src/core/httpcli/format_request.c +++ b/src/core/httpcli/format_request.c @@ -37,67 +37,57 @@ #include <stdio.h> #include <string.h> +#include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/slice.h> #include <grpc/support/useful.h> -typedef struct { - size_t length; - size_t capacity; - char *data; -} sbuf; - -static void sbuf_append(sbuf *buf, const char *bytes, size_t len) { - if (buf->length + len > buf->capacity) { - buf->capacity = GPR_MAX(buf->length + len, buf->capacity * 3 / 2); - buf->data = gpr_realloc(buf->data, buf->capacity); - } - memcpy(buf->data + buf->length, bytes, len); - buf->length += len; -} - -static void sbprintf(sbuf *buf, const char *fmt, ...) { - char temp[GRPC_HTTPCLI_MAX_HEADER_LENGTH]; - size_t len; - va_list args; - - va_start(args, fmt); - len = vsprintf(temp, fmt, args); - va_end(args); - - sbuf_append(buf, temp, len); -} - -static void fill_common_header(const grpc_httpcli_request *request, sbuf *buf) { +static void fill_common_header(const grpc_httpcli_request *request, gpr_strvec *buf) { size_t i; - sbprintf(buf, "%s HTTP/1.0\r\n", request->path); + gpr_strvec_add(buf, gpr_strdup(request->path)); + gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n")); /* just in case some crazy server really expects HTTP/1.1 */ - sbprintf(buf, "Host: %s\r\n", request->host); - sbprintf(buf, "Connection: close\r\n"); - sbprintf(buf, "User-Agent: %s\r\n", GRPC_HTTPCLI_USER_AGENT); + gpr_strvec_add(buf, gpr_strdup("Host: ")); + gpr_strvec_add(buf, gpr_strdup(request->host)); + gpr_strvec_add(buf, gpr_strdup("\r\n")); + gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n")); + gpr_strvec_add(buf, gpr_strdup("User-Agent: "GRPC_HTTPCLI_USER_AGENT"\r\n")); /* user supplied headers */ for (i = 0; i < request->hdr_count; i++) { - sbprintf(buf, "%s: %s\r\n", request->hdrs[i].key, request->hdrs[i].value); + gpr_strvec_add(buf, gpr_strdup(request->hdrs[i].key)); + gpr_strvec_add(buf, gpr_strdup(": ")); + gpr_strvec_add(buf, gpr_strdup(request->hdrs[i].value)); + gpr_strvec_add(buf, gpr_strdup("\r\n")); } } gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request) { - sbuf out = {0, 0, NULL}; + gpr_strvec out; + char *flat; + size_t flat_len; - sbprintf(&out, "GET "); + gpr_strvec_init(&out); + gpr_strvec_add(&out, gpr_strdup("GET ")); fill_common_header(request, &out); - sbprintf(&out, "\r\n"); + gpr_strvec_add(&out, gpr_strdup("\r\n")); - return gpr_slice_new(out.data, out.length, gpr_free); + flat = gpr_strvec_flatten(&out, &flat_len); + gpr_strvec_destroy(&out); + + return gpr_slice_new(flat, flat_len, gpr_free); } gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, const char *body_bytes, size_t body_size) { - sbuf out = {0, 0, NULL}; + gpr_strvec out; + char *tmp; + size_t out_len; size_t i; - sbprintf(&out, "POST "); + gpr_strvec_init(&out); + + gpr_strvec_add(&out, gpr_strdup("POST ")); fill_common_header(request, &out); if (body_bytes) { gpr_uint8 has_content_type = 0; @@ -108,14 +98,18 @@ gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, } } if (!has_content_type) { - sbprintf(&out, "Content-Type: text/plain\r\n"); + gpr_strvec_add(&out, gpr_strdup("Content-Type: text/plain\r\n")); } - sbprintf(&out, "Content-Length: %lu\r\n", (unsigned long)body_size); + gpr_asprintf(&tmp, "Content-Length: %lu\r\n", (unsigned long)body_size); + gpr_strvec_add(&out, tmp); } - sbprintf(&out, "\r\n"); + gpr_strvec_add(&out, gpr_strdup("\r\n")); + tmp = gpr_strvec_flatten(&out, &out_len); if (body_bytes) { - sbuf_append(&out, body_bytes, body_size); + tmp = gpr_realloc(tmp, out_len + body_size); + memcpy(tmp + out_len, body_bytes, body_size); + out_len += body_size; } - return gpr_slice_new(out.data, out.length, gpr_free); + return gpr_slice_new(tmp, out_len, gpr_free); } diff --git a/src/core/support/string.c b/src/core/support/string.c index 9b5cac7596..97bce60f94 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -14,7 +14,7 @@ * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from + * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -152,3 +152,49 @@ int gpr_ltoa(long value, char *string) { string[i] = 0; return i; } + +char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { + size_t out_length = 0; + size_t i; + char *out; + for (i = 0; i < nstrs; i++) { + out_length += strlen(strs[i]); + } + out_length += 1; /* null terminator */ + out = gpr_malloc(out_length); + out_length = 0; + for (i = 0; i < nstrs; i++) { + size_t slen = strlen(strs[i]); + memcpy(out + out_length, strs[i], slen); + out_length += slen; + } + out[out_length] = 0; + if (final_length != NULL) { + *final_length = out_length; + } + return out; +} + +void gpr_strvec_init(gpr_strvec *sv) { + memset(sv, 0, sizeof(*sv)); +} + +void gpr_strvec_destroy(gpr_strvec *sv) { + size_t i; + for (i = 0; i < sv->count; i++) { + gpr_free(sv->strs[i]); + } + gpr_free(sv->strs); +} + +void gpr_strvec_add(gpr_strvec *sv, char *str) { + if (sv->count == sv->capacity) { + sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2); + sv->strs = gpr_realloc(sv->strs, sizeof(char*) * sv->capacity); + } + sv->strs[sv->count++] = str; +} + +char *gpr_strvec_flatten(gpr_strvec *sv, size_t *final_length) { + return gpr_strjoin((const char**)sv->strs, sv->count, final_length); +} diff --git a/src/core/support/string.h b/src/core/support/string.h index 28b7029ecd..64e06d3b6a 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -81,6 +81,27 @@ void gpr_reverse_bytes(char *str, int len); the result is undefined. */ int gpr_asprintf(char **strp, const char *format, ...); +/* Join a set of strings, returning the resulting string. + Total combined length (excluding null terminator) is returned in total_length + if it is non-null. */ +char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length); + +/* A vector of strings... for building up a final string one piece at a time */ +typedef struct { + char **strs; + size_t count; + size_t capacity; +} gpr_strvec; + +/* Initialize/destroy */ +void gpr_strvec_init(gpr_strvec *strs); +void gpr_strvec_destroy(gpr_strvec *strs); +/* Add a string to a strvec, takes ownership of the string */ +void gpr_strvec_add(gpr_strvec *strs, char *add); +/* Return a joined string with all added substrings, optionally setting + total_length as per gpr_strjoin */ +char *gpr_strvec_flatten(gpr_strvec *strs, size_t *total_length); + #ifdef __cplusplus } #endif diff --git a/src/core/surface/event_string.c b/src/core/surface/event_string.c index e38ef06c9f..8975d312ee 100644 --- a/src/core/surface/event_string.c +++ b/src/core/surface/event_string.c @@ -38,8 +38,10 @@ #include "src/core/support/string.h" #include <grpc/byte_buffer.h> -static size_t addhdr(char *p, grpc_event *ev) { - return sprintf(p, "tag:%p call:%p", ev->tag, (void *)ev->call); +static void addhdr(gpr_strvec *buf, grpc_event *ev) { + char *tmp; + gpr_asprintf(&tmp, "tag:%p call:%p", ev->tag, (void *)ev->call); + gpr_strvec_add(buf, tmp); } static const char *errstr(grpc_op_error err) { @@ -52,72 +54,84 @@ static const char *errstr(grpc_op_error err) { return "UNKNOWN_UNKNOWN"; } -static size_t adderr(char *p, grpc_op_error err) { - return sprintf(p, " err=%s", errstr(err)); +static void adderr(gpr_strvec *buf, grpc_op_error err) { + char *tmp; + gpr_asprintf(&tmp, " err=%s", errstr(err)); + gpr_strvec_add(buf, tmp); } char *grpc_event_string(grpc_event *ev) { - char buffer[1024]; - char *p = buffer; + char *out; + char *tmp; + gpr_strvec buf; if (ev == NULL) return gpr_strdup("null"); + gpr_strvec_init(&buf); + switch (ev->type) { case GRPC_SERVER_SHUTDOWN: - p += sprintf(p, "SERVER_SHUTDOWN"); + gpr_strvec_add(&buf, gpr_strdup("SERVER_SHUTDOWN")); break; case GRPC_QUEUE_SHUTDOWN: - p += sprintf(p, "QUEUE_SHUTDOWN"); + gpr_strvec_add(&buf, gpr_strdup("QUEUE_SHUTDOWN")); break; case GRPC_READ: - p += sprintf(p, "READ: "); - p += addhdr(p, ev); + gpr_strvec_add(&buf, gpr_strdup("READ: ")); + addhdr(&buf, ev); if (ev->data.read) { - p += sprintf(p, " %d bytes", + gpr_asprintf(&tmp, " %d bytes", (int)grpc_byte_buffer_length(ev->data.read)); + gpr_strvec_add(&buf, tmp); } else { - p += sprintf(p, " end-of-stream"); + gpr_strvec_add(&buf, gpr_strdup(" end-of-stream")); } break; case GRPC_INVOKE_ACCEPTED: - p += sprintf(p, "INVOKE_ACCEPTED: "); - p += addhdr(p, ev); - p += adderr(p, ev->data.invoke_accepted); + gpr_strvec_add(&buf, gpr_strdup("INVOKE_ACCEPTED: ")); + addhdr(&buf, ev); + adderr(&buf, ev->data.invoke_accepted); break; case GRPC_WRITE_ACCEPTED: - p += sprintf(p, "WRITE_ACCEPTED: "); - p += addhdr(p, ev); - p += adderr(p, ev->data.write_accepted); + gpr_strvec_add(&buf, gpr_strdup("WRITE_ACCEPTED: ")); + addhdr(&buf, ev); + adderr(&buf, ev->data.write_accepted); break; case GRPC_FINISH_ACCEPTED: - p += sprintf(p, "FINISH_ACCEPTED: "); - p += addhdr(p, ev); - p += adderr(p, ev->data.write_accepted); + gpr_strvec_add(&buf, gpr_strdup("FINISH_ACCEPTED: ")); + addhdr(&buf, ev); + adderr(&buf, ev->data.write_accepted); break; case GRPC_CLIENT_METADATA_READ: - p += sprintf(p, "CLIENT_METADATA_READ: "); - p += addhdr(p, ev); - p += sprintf(p, " %d elements", (int)ev->data.client_metadata_read.count); + gpr_strvec_add(&buf, gpr_strdup("CLIENT_METADATA_READ: ")); + addhdr(&buf, ev); + gpr_asprintf(&tmp, " %d elements", + (int)ev->data.client_metadata_read.count); + gpr_strvec_add(&buf, tmp); break; case GRPC_FINISHED: - p += sprintf(p, "FINISHED: "); - p += addhdr(p, ev); - p += sprintf(p, " status=%d details='%s' %d metadata elements", + gpr_strvec_add(&buf, gpr_strdup("FINISHED: ")); + addhdr(&buf, ev); + gpr_asprintf(&tmp, " status=%d details='%s' %d metadata elements", ev->data.finished.status, ev->data.finished.details, (int)ev->data.finished.metadata_count); + gpr_strvec_add(&buf, tmp); break; case GRPC_SERVER_RPC_NEW: - p += sprintf(p, "SERVER_RPC_NEW: "); - p += addhdr(p, ev); - p += sprintf(p, " method='%s' host='%s' %d metadata elements", + gpr_strvec_add(&buf, gpr_strdup("SERVER_RPC_NEW: ")); + addhdr(&buf, ev); + gpr_asprintf(&tmp, " method='%s' host='%s' %d metadata elements", ev->data.server_rpc_new.method, ev->data.server_rpc_new.host, (int)ev->data.server_rpc_new.metadata_count); + gpr_strvec_add(&buf, tmp); break; case GRPC_COMPLETION_DO_NOT_USE: - p += sprintf(p, "DO_NOT_USE (this is a bug)"); - p += addhdr(p, ev); + gpr_strvec_add(&buf, gpr_strdup("DO_NOT_USE (this is a bug)")); + addhdr(&buf, ev); break; } - return gpr_strdup(buffer); + out = gpr_strvec_flatten(&buf, NULL); + gpr_strvec_destroy(&buf); + return out; } diff --git a/src/node/client.js b/src/node/client.js index 2fefd14bbc..7007852b93 100644 --- a/src/node/client.js +++ b/src/node/client.js @@ -105,7 +105,7 @@ function GrpcClientStream(call, serialize, deserialize) { return; } var data = event.data; - if (self.push(data) && data != null) { + if (self.push(self.deserialize(data)) && data != null) { self._call.startRead(readCallback); } else { reading = false; @@ -155,7 +155,7 @@ GrpcClientStream.prototype._read = function(size) { */ GrpcClientStream.prototype._write = function(chunk, encoding, callback) { var self = this; - self._call.startWrite(chunk, function(event) { + self._call.startWrite(self.serialize(chunk), function(event) { callback(); }, 0); }; @@ -185,7 +185,7 @@ function makeRequest(channel, if (metadata) { call.addMetadata(metadata); } - return new GrpcClientStream(call); + return new GrpcClientStream(call, serialize, deserialize); } /** diff --git a/src/node/examples/math_server.js b/src/node/examples/math_server.js index d649b4fd6d..e65cfe3002 100644 --- a/src/node/examples/math_server.js +++ b/src/node/examples/math_server.js @@ -52,7 +52,8 @@ var Server = grpc.buildServer([math.Math.service]); */ function mathDiv(call, cb) { var req = call.request; - if (req.divisor == 0) { + // Unary + is explicit coersion to integer + if (+req.divisor === 0) { cb(new Error('cannot divide by zero')); } cb(null, { @@ -89,7 +90,7 @@ function mathSum(call, cb) { // Here, call is a standard readable Node object Stream var sum = 0; call.on('data', function(data) { - sum += data.num | 0; + sum += (+data.num); }); call.on('end', function() { cb(null, {num: sum}); @@ -104,7 +105,7 @@ function mathDivMany(stream) { Transform.call(this, options); } DivTransform.prototype._transform = function(div_args, encoding, callback) { - if (div_args.divisor == 0) { + if (+div_args.divisor === 0) { callback(new Error('cannot divide by zero')); } callback(null, { diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js index cf75b9a77a..9306317b68 100644 --- a/src/node/interop/interop_client.js +++ b/src/node/interop/interop_client.js @@ -183,7 +183,7 @@ function pingPong(client, done) { assert.equal(response.payload.body.limit - response.payload.body.offset, response_sizes[index]); index += 1; - if (index == 4) { + if (index === 4) { call.end(); } else { call.write({ diff --git a/src/node/server.js b/src/node/server.js index eca20aa5fd..fe50acb5a1 100644 --- a/src/node/server.js +++ b/src/node/server.js @@ -151,7 +151,7 @@ function GrpcServerStream(call, serialize, deserialize) { return; } var data = event.data; - if (self.push(deserialize(data)) && data != null) { + if (self.push(self.deserialize(data)) && data != null) { self._call.startRead(readCallback); } else { reading = false; @@ -233,7 +233,7 @@ function Server(options) { function handleNewCall(event) { var call = event.call; var data = event.data; - if (data == null) { + if (data === null) { return; } server.requestCall(handleNewCall); diff --git a/src/node/surface_client.js b/src/node/surface_client.js index 996e3d101f..b63ae13e8d 100644 --- a/src/node/surface_client.js +++ b/src/node/surface_client.js @@ -63,114 +63,70 @@ util.inherits(ClientReadableObjectStream, Readable); * client side. Extends from stream.Readable. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options */ -function ClientReadableObjectStream(stream, deserialize, options) { - options = _.extend(options, {objectMode: true}); +function ClientReadableObjectStream(stream) { + var options = {objectMode: true}; Readable.call(this, options); this._stream = stream; var self = this; forwardEvent(stream, this, 'status'); forwardEvent(stream, this, 'metadata'); this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { + if (!self.push(chunk)) { self._stream.pause(); } }); this._stream.pause(); } -util.inherits(ClientWritableObjectStream, Writable); - /** - * Class for representing a gRPC client streaming call as a Node stream on the - * client side. Extends from stream.Writable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {object} options Stream options + * _read implementation for both types of streams that allow reading. + * @this {ClientReadableObjectStream} + * @param {number} size Ignored */ -function ClientWritableObjectStream(stream, serialize, options) { - options = _.extend(options, {objectMode: true}); - Writable.call(this, options); - this._stream = stream; - this._serialize = serialize; - forwardEvent(stream, this, 'status'); - forwardEvent(stream, this, 'metadata'); - this.on('finish', function() { - this._stream.end(); - }); +function _read(size) { + this._stream.resume(); } +/** + * See docs for _read + */ +ClientReadableObjectStream.prototype._read = _read; -util.inherits(ClientBidiObjectStream, Duplex); +util.inherits(ClientWritableObjectStream, Writable); /** - * Class for representing a gRPC bidi streaming call as a Node stream on the - * client side. Extends from stream.Duplex. + * Class for representing a gRPC client streaming call as a Node stream on the + * client side. Extends from stream.Writable. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options */ -function ClientBidiObjectStream(stream, serialize, deserialize, options) { - options = _.extend(options, {objectMode: true}); - Duplex.call(this, options); +function ClientWritableObjectStream(stream) { + var options = {objectMode: true}; + Writable.call(this, options); this._stream = stream; - this._serialize = serialize; - var self = this; forwardEvent(stream, this, 'status'); forwardEvent(stream, this, 'metadata'); - this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { - self._stream.pause(); - } - }); - this._stream.pause(); this.on('finish', function() { this._stream.end(); }); } /** - * _read implementation for both types of streams that allow reading. - * @this {ClientReadableObjectStream|ClientBidiObjectStream} - * @param {number} size Ignored - */ -function _read(size) { - this._stream.resume(); -} - -/** - * See docs for _read - */ -ClientReadableObjectStream.prototype._read = _read; -/** - * See docs for _read - */ -ClientBidiObjectStream.prototype._read = _read; - -/** * _write implementation for both types of streams that allow writing - * @this {ClientWritableObjectStream|ClientBidiObjectStream} + * @this {ClientWritableObjectStream} * @param {*} chunk The value to write to the stream * @param {string} encoding Ignored * @param {function(Error)} callback Callback to call when finished writing */ function _write(chunk, encoding, callback) { - this._stream.write(this._serialize(chunk), encoding, callback); + this._stream.write(chunk, encoding, callback); } /** * See docs for _write */ ClientWritableObjectStream.prototype._write = _write; -/** - * See docs for _write - */ -ClientBidiObjectStream.prototype._write = _write; /** * Get a function that can make unary requests to the specified method. @@ -196,15 +152,16 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeUnaryRequest(argument, callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); + var stream = client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); var emitter = new EventEmitter(); forwardEvent(stream, emitter, 'status'); forwardEvent(stream, emitter, 'metadata'); - stream.write(serialize(argument)); + stream.write(argument); stream.end(); stream.on('data', function forwardData(chunk) { try { - callback(null, deserialize(chunk)); + callback(null, chunk); } catch (e) { callback(e); } @@ -236,11 +193,12 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeClientStreamRequest(callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); - var obj_stream = new ClientWritableObjectStream(stream, serialize, {}); + var stream = client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); + var obj_stream = new ClientWritableObjectStream(stream); stream.on('data', function forwardData(chunk) { try { - callback(null, deserialize(chunk)); + callback(null, chunk); } catch (e) { callback(e); } @@ -272,9 +230,10 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeServerStreamRequest(argument, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); - var obj_stream = new ClientReadableObjectStream(stream, deserialize, {}); - stream.write(serialize(argument)); + var stream = client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); + var obj_stream = new ClientReadableObjectStream(stream); + stream.write(argument); stream.end(); return obj_stream; } @@ -301,12 +260,8 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeBidiStreamRequest(metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); - var obj_stream = new ClientBidiObjectStream(stream, - serialize, - deserialize, - {}); - return obj_stream; + return client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); } return makeBidiStreamRequest; } diff --git a/src/node/surface_server.js b/src/node/surface_server.js index bc688839fe..07c5339f62 100644 --- a/src/node/surface_server.js +++ b/src/node/surface_server.js @@ -54,11 +54,9 @@ util.inherits(ServerReadableObjectStream, Readable); * server side. Extends from stream.Readable. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options */ -function ServerReadableObjectStream(stream, deserialize, options) { - options = _.extend(options, {objectMode: true}); +function ServerReadableObjectStream(stream) { + var options = {objectMode: true}; Readable.call(this, options); this._stream = stream; Object.defineProperty(this, 'cancelled', { @@ -66,7 +64,7 @@ function ServerReadableObjectStream(stream, deserialize, options) { }); var self = this; this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { + if (!self.push(chunk)) { self._stream.pause(); } }); @@ -76,57 +74,6 @@ function ServerReadableObjectStream(stream, deserialize, options) { this._stream.pause(); } -util.inherits(ServerWritableObjectStream, Writable); - -/** - * Class for representing a gRPC server streaming call as a Node stream on the - * server side. Extends from stream.Writable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {object} options Stream options - */ -function ServerWritableObjectStream(stream, serialize, options) { - options = _.extend(options, {objectMode: true}); - Writable.call(this, options); - this._stream = stream; - this._serialize = serialize; - this.on('finish', function() { - this._stream.end(); - }); -} - -util.inherits(ServerBidiObjectStream, Duplex); - -/** - * Class for representing a gRPC bidi streaming call as a Node stream on the - * server side. Extends from stream.Duplex. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options - */ -function ServerBidiObjectStream(stream, serialize, deserialize, options) { - options = _.extend(options, {objectMode: true}); - Duplex.call(this, options); - this._stream = stream; - this._serialize = serialize; - var self = this; - this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { - self._stream.pause(); - } - }); - this._stream.on('end', function forwardEnd() { - self.push(null); - }); - this._stream.pause(); - this.on('finish', function() { - this._stream.end(); - }); -} - /** * _read implementation for both types of streams that allow reading. * @this {ServerReadableObjectStream|ServerBidiObjectStream} @@ -140,39 +87,46 @@ function _read(size) { * See docs for _read */ ServerReadableObjectStream.prototype._read = _read; + +util.inherits(ServerWritableObjectStream, Writable); + /** - * See docs for _read + * Class for representing a gRPC server streaming call as a Node stream on the + * server side. Extends from stream.Writable. + * @constructor + * @param {stream} stream Underlying binary Duplex stream for the call */ -ServerBidiObjectStream.prototype._read = _read; +function ServerWritableObjectStream(stream) { + var options = {objectMode: true}; + Writable.call(this, options); + this._stream = stream; + this.on('finish', function() { + this._stream.end(); + }); +} /** * _write implementation for both types of streams that allow writing - * @this {ServerWritableObjectStream|ServerBidiObjectStream} + * @this {ServerWritableObjectStream} * @param {*} chunk The value to write to the stream * @param {string} encoding Ignored * @param {function(Error)} callback Callback to call when finished writing */ function _write(chunk, encoding, callback) { - this._stream.write(this._serialize(chunk), encoding, callback); + this._stream.write(chunk, encoding, callback); } /** * See docs for _write */ ServerWritableObjectStream.prototype._write = _write; -/** - * See docs for _write - */ -ServerBidiObjectStream.prototype._write = _write; /** * Creates a binary stream handler function from a unary handler function * @param {function(Object, function(Error, *))} handler Unary call handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeUnaryHandler(handler, serialize, deserialize) { +function makeUnaryHandler(handler) { /** * Handles a stream by reading a single data value, passing it to the handler, * and writing the response back to the stream. @@ -180,7 +134,7 @@ function makeUnaryHandler(handler, serialize, deserialize) { */ return function handleUnaryCall(stream) { stream.on('data', function handleUnaryData(value) { - var call = {request: deserialize(value)}; + var call = {request: value}; Object.defineProperty(call, 'cancelled', { get: function() { return stream.cancelled;} }); @@ -188,7 +142,7 @@ function makeUnaryHandler(handler, serialize, deserialize) { if (err) { stream.emit('error', err); } else { - stream.write(serialize(value)); + stream.write(value); stream.end(); } }); @@ -201,23 +155,21 @@ function makeUnaryHandler(handler, serialize, deserialize) { * function * @param {function(Readable, function(Error, *))} handler Client stream call * handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeClientStreamHandler(handler, serialize, deserialize) { +function makeClientStreamHandler(handler) { /** * Handles a stream by passing a deserializing stream to the handler and * writing the response back to the stream. * @param {stream} stream Binary data stream */ return function handleClientStreamCall(stream) { - var object_stream = new ServerReadableObjectStream(stream, deserialize, {}); + var object_stream = new ServerReadableObjectStream(stream); handler(object_stream, function sendClientStreamData(err, value) { if (err) { stream.emit('error', err); } else { - stream.write(serialize(value)); + stream.write(value); stream.end(); } }); @@ -228,11 +180,9 @@ function makeClientStreamHandler(handler, serialize, deserialize) { * Creates a binary stream handler function from a server stream handler * function * @param {function(Writable)} handler Server stream call handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeServerStreamHandler(handler, serialize, deserialize) { +function makeServerStreamHandler(handler) { /** * Handles a stream by attaching it to a serializing stream, and passing it to * the handler. @@ -240,10 +190,8 @@ function makeServerStreamHandler(handler, serialize, deserialize) { */ return function handleServerStreamCall(stream) { stream.on('data', function handleClientData(value) { - var object_stream = new ServerWritableObjectStream(stream, - serialize, - {}); - object_stream.request = deserialize(value); + var object_stream = new ServerWritableObjectStream(stream); + object_stream.request = value; handler(object_stream); }); }; @@ -252,23 +200,10 @@ function makeServerStreamHandler(handler, serialize, deserialize) { /** * Creates a binary stream handler function from a bidi stream handler function * @param {function(Duplex)} handler Unary call handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeBidiStreamHandler(handler, serialize, deserialize) { - /** - * Handles a stream by wrapping it in a serializing and deserializing object - * stream, and passing it to the handler. - * @param {stream} stream Binary data stream - */ - return function handleBidiStreamCall(stream) { - var object_stream = new ServerBidiObjectStream(stream, - serialize, - deserialize, - {}); - handler(object_stream); - }; +function makeBidiStreamHandler(handler) { + return handler; } /** @@ -341,10 +276,13 @@ function makeServerConstructor(services) { common.fullyQualifiedName(method) + ' not provided.'); } var binary_handler = handler_makers[method_type]( - service_handlers[service_name][decapitalize(method.name)], - common.serializeCls(method.resolvedResponseType.build()), - common.deserializeCls(method.resolvedRequestType.build())); - server.register(prefix + capitalize(method.name), binary_handler); + service_handlers[service_name][decapitalize(method.name)]); + var serialize = common.serializeCls( + method.resolvedResponseType.build()); + var deserialize = common.deserializeCls( + method.resolvedRequestType.build()); + server.register(prefix + capitalize(method.name), binary_handler, + serialize, deserialize); }); }, this); } diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 3c062b9788..6cc7d444cd 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -48,6 +48,9 @@ describe('Interop tests', function() { port = 'localhost:' + server_obj.port; done(); }); + after(function() { + server.shutdown(); + }); // This depends on not using a binary stream it('should pass empty_unary', function(done) { interop_client.runTest(port, name_override, 'empty_unary', true, done); @@ -65,7 +68,7 @@ describe('Interop tests', function() { it('should pass ping_pong', function(done) { interop_client.runTest(port, name_override, 'ping_pong', true, done); }); - it.skip('should pass empty_stream', function(done) { + it('should pass empty_stream', function(done) { interop_client.runTest(port, name_override, 'empty_stream', true, done); }); }); diff --git a/test/core/channel/metadata_buffer_test.c b/test/core/channel/metadata_buffer_test.c index aa2399272f..d2bc30cc3c 100644 --- a/test/core/channel/metadata_buffer_test.c +++ b/test/core/channel/metadata_buffer_test.c @@ -32,6 +32,7 @@ */ #include "src/core/channel/metadata_buffer.h" +#include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include "test/core/util/test_config.h" @@ -42,12 +43,12 @@ /* construct a buffer with some prefix followed by an integer converted to a string */ static gpr_slice construct_buffer(size_t prefix_length, size_t index) { - gpr_slice buffer = gpr_slice_malloc(prefix_length + 32); + gpr_slice buffer = gpr_slice_malloc(prefix_length + GPR_LTOA_MIN_BUFSIZE); memset(GPR_SLICE_START_PTR(buffer), 'a', prefix_length); GPR_SLICE_SET_LENGTH( - buffer, prefix_length + - sprintf((char *)GPR_SLICE_START_PTR(buffer) + prefix_length, - "%d", (int)index)); + buffer, + prefix_length + + gpr_ltoa(index, (char *)GPR_SLICE_START_PTR(buffer) + prefix_length)); return buffer; } diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c index 49b131c236..287f83eebc 100644 --- a/test/core/end2end/cq_verifier.c +++ b/test/core/end2end/cq_verifier.c @@ -231,100 +231,91 @@ static void verify_matches(expectation *e, grpc_event *ev) { } } -static char *metadata_expectation_string(metadata *md) { - size_t len; +static void metadata_expectation(gpr_strvec *buf, metadata *md) { size_t i; - char *out; - char *p; - - if (!md) return gpr_strdup("nil"); - - for (len = 0, i = 0; i < md->count; i++) { - len += strlen(md->keys[i]); - len += strlen(md->values[i]); - } - len += 3 + md->count; - - p = out = gpr_malloc(len); - *p++ = '{'; - for (i = 0; i < md->count; i++) { - if (i) *p++ = ','; - p += sprintf(p, "%s:%s", md->keys[i], md->values[i]); + char *tmp; + + if (!md) { + gpr_strvec_add(buf, gpr_strdup("nil")); + } else { + for (i = 0; i < md->count; i++) { + gpr_asprintf(&tmp, "%c%s:%s", i ? ',' : '{', md->keys[i], md->values[i]); + gpr_strvec_add(buf, tmp); + } + gpr_strvec_add(buf, gpr_strdup("}")); } - *p++ = '}'; - *p++ = 0; - return out; } -static size_t expectation_to_string(char *out, expectation *e) { +static void expectation_to_strvec(gpr_strvec *buf, expectation *e) { gpr_timespec timeout; - char *str = NULL; - size_t len; + char *tmp; switch (e->type) { case GRPC_FINISH_ACCEPTED: - return sprintf(out, "GRPC_FINISH_ACCEPTED result=%d", + gpr_asprintf(&tmp, "GRPC_FINISH_ACCEPTED result=%d", e->data.finish_accepted); + gpr_strvec_add(buf, tmp); + break; case GRPC_WRITE_ACCEPTED: - return sprintf(out, "GRPC_WRITE_ACCEPTED result=%d", + gpr_asprintf(&tmp, "GRPC_WRITE_ACCEPTED result=%d", e->data.write_accepted); + gpr_strvec_add(buf, tmp); + break; case GRPC_INVOKE_ACCEPTED: - return sprintf(out, "GRPC_INVOKE_ACCEPTED"); + gpr_strvec_add(buf, gpr_strdup("GRPC_INVOKE_ACCEPTED")); + break; case GRPC_SERVER_RPC_NEW: timeout = gpr_time_sub(e->data.server_rpc_new.deadline, gpr_now()); - return sprintf(out, "GRPC_SERVER_RPC_NEW method=%s host=%s timeout=%fsec", + gpr_asprintf(&tmp, "GRPC_SERVER_RPC_NEW method=%s host=%s timeout=%fsec", e->data.server_rpc_new.method, e->data.server_rpc_new.host, timeout.tv_sec + 1e-9 * timeout.tv_nsec); + gpr_strvec_add(buf, tmp); + break; case GRPC_CLIENT_METADATA_READ: - str = metadata_expectation_string(e->data.client_metadata_read); - len = sprintf(out, "GRPC_CLIENT_METADATA_READ %s", str); - gpr_free(str); - return len; + gpr_strvec_add(buf, gpr_strdup("GRPC_CLIENT_METADATA_READ ")); + metadata_expectation(buf, e->data.client_metadata_read); + break; case GRPC_FINISHED: - str = metadata_expectation_string(e->data.finished.metadata); - len = sprintf(out, "GRPC_FINISHED status=%d details=%s %s", - e->data.finished.status, e->data.finished.details, str); - gpr_free(str); - return len; + gpr_asprintf(&tmp, "GRPC_FINISHED status=%d details=%s ", + e->data.finished.status, e->data.finished.details); + gpr_strvec_add(buf, tmp); + metadata_expectation(buf, e->data.finished.metadata); + break; case GRPC_READ: - if (e->data.read) { - str = - gpr_hexdump((char *)GPR_SLICE_START_PTR(*e->data.read), - GPR_SLICE_LENGTH(*e->data.read), GPR_HEXDUMP_PLAINTEXT); - } - len = sprintf(out, "GRPC_READ data=%s", str); - gpr_free(str); - return len; + gpr_strvec_add(buf, gpr_strdup("GRPC_READ data=")); + gpr_strvec_add(buf, gpr_hexdump((char *)GPR_SLICE_START_PTR(*e->data.read), + GPR_SLICE_LENGTH(*e->data.read), GPR_HEXDUMP_PLAINTEXT)); + break; case GRPC_SERVER_SHUTDOWN: - return sprintf(out, "GRPC_SERVER_SHUTDOWN"); + gpr_strvec_add(buf, gpr_strdup("GRPC_SERVER_SHUTDOWN")); + break; case GRPC_COMPLETION_DO_NOT_USE: case GRPC_QUEUE_SHUTDOWN: gpr_log(GPR_ERROR, "not implemented"); abort(); break; } - return 0; } -static char *expectations_to_string(cq_verifier *v) { - /* allocate a large buffer: we're about to crash anyway */ - char *buffer = gpr_malloc(32 * 1024 * 1024); - char *p = buffer; +static void expectations_to_strvec(gpr_strvec *buf, cq_verifier *v) { expectation *e; for (e = v->expect.next; e != &v->expect; e = e->next) { - p += expectation_to_string(p, e); - *p++ = '\n'; + expectation_to_strvec(buf, e); + gpr_strvec_add(buf, gpr_strdup("\n")); } - - *p = 0; - return buffer; } static void fail_no_event_received(cq_verifier *v) { - char *expectations = expectations_to_string(v); - gpr_log(GPR_ERROR, "no event received, but expected:\n%s", expectations); - gpr_free(expectations); + gpr_strvec buf; + char *msg; + gpr_strvec_init(&buf); + gpr_strvec_add(&buf, gpr_strdup("no event received, but expected:\n")); + expectations_to_strvec(&buf, v); + msg = gpr_strvec_flatten(&buf, NULL); + gpr_log(GPR_ERROR, "%s", msg); + gpr_strvec_destroy(&buf); + gpr_free(msg); abort(); } @@ -333,9 +324,10 @@ void cq_verify(cq_verifier *v) { gpr_time_add(gpr_now(), gpr_time_from_micros(10 * GPR_US_PER_SEC)); grpc_event *ev; expectation *e; + char *s; + gpr_strvec have_tags; - char have_tags[512] = {0}; - char *phave = have_tags; + gpr_strvec_init(&have_tags); while (v->expect.next != &v->expect) { ev = grpc_completion_queue_next(v->cq, deadline); @@ -344,7 +336,8 @@ void cq_verify(cq_verifier *v) { } for (e = v->expect.next; e != &v->expect; e = e->next) { - phave += sprintf(phave, " %p", e->tag); + gpr_asprintf(&s, " %p", e->tag); + gpr_strvec_add(&have_tags, s); if (e->tag == ev->tag) { verify_matches(e, ev); e->next->prev = e->prev; @@ -354,15 +347,20 @@ void cq_verify(cq_verifier *v) { } } if (e == &v->expect) { - char *s = grpc_event_string(ev); + s = grpc_event_string(ev); gpr_log(GPR_ERROR, "event not found: %s", s); - gpr_log(GPR_ERROR, "have tags:%s", have_tags); gpr_free(s); + s = gpr_strvec_flatten(&have_tags, NULL); + gpr_log(GPR_ERROR, "have tags:%s", s); + gpr_free(s); + gpr_strvec_destroy(&have_tags); abort(); } grpc_event_finish(ev); } + + gpr_strvec_destroy(&have_tags); } void cq_verify_empty(cq_verifier *v) { diff --git a/test/core/end2end/tests/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c index 719f0fe662..86cef437be 100644 --- a/test/core/end2end/tests/census_simple_request.c +++ b/test/core/end2end/tests/census_simple_request.c @@ -37,6 +37,7 @@ #include <string.h> #include <unistd.h> +#include "src/core/support/string.h" #include <grpc/byte_buffer.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -145,7 +146,7 @@ static void test_body(grpc_end2end_test_fixture f) { static void test_invoke_request_with_census( grpc_end2end_test_config config, const char *name, void (*body)(grpc_end2end_test_fixture f)) { - char fullname[64]; + char *fullname; grpc_end2end_test_fixture f; grpc_arg client_arg, server_arg; grpc_channel_args client_args, server_args; @@ -163,11 +164,12 @@ static void test_invoke_request_with_census( server_args.num_args = 1; server_args.args = &server_arg; - sprintf(fullname, "%s/%s", __FUNCTION__, name); + gpr_asprintf(&fullname, "%s/%s", __FUNCTION__, name); f = begin_test(config, fullname, &client_args, &server_args); body(f); end_test(&f); config.tear_down_data(&f); + gpr_free(fullname); } void grpc_end2end_tests(grpc_end2end_test_config config) { diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c index 23fc201d84..f8894a8ba9 100644 --- a/test/core/end2end/tests/simple_request.c +++ b/test/core/end2end/tests/simple_request.c @@ -37,6 +37,7 @@ #include <string.h> #include <unistd.h> +#include "src/core/support/string.h" #include <grpc/byte_buffer.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -198,15 +199,16 @@ static void simple_request_body2(grpc_end2end_test_fixture f) { static void test_invoke_simple_request( grpc_end2end_test_config config, const char *name, void (*body)(grpc_end2end_test_fixture f)) { - char fullname[64]; + char *fullname; grpc_end2end_test_fixture f; - sprintf(fullname, "%s/%s", __FUNCTION__, name); + gpr_asprintf(&fullname, "%s/%s", __FUNCTION__, name); f = begin_test(config, fullname, NULL, NULL); body(f); end_test(&f); config.tear_down_data(&f); + gpr_free(fullname); } static void test_invoke_10_simple_requests(grpc_end2end_test_config config) { diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c index ec21e0d42f..1c83cc8059 100644 --- a/test/core/security/credentials_test.c +++ b/test/core/security/credentials_test.c @@ -498,10 +498,8 @@ static void validate_service_account_http_request( char *expected_body = NULL; GPR_ASSERT(body != NULL); GPR_ASSERT(body_size != 0); - expected_body = gpr_malloc(strlen(expected_service_account_http_body_prefix) + - strlen(test_signed_jwt) + 1); - sprintf(expected_body, "%s%s", expected_service_account_http_body_prefix, - test_signed_jwt); + gpr_asprintf(&expected_body, "%s%s", + expected_service_account_http_body_prefix, test_signed_jwt); GPR_ASSERT(strlen(expected_body) == body_size); GPR_ASSERT(!memcmp(expected_body, body, body_size)); gpr_free(expected_body); diff --git a/test/core/statistics/hash_table_test.c b/test/core/statistics/hash_table_test.c index ebfc2a2a9a..f8df2574a4 100644 --- a/test/core/statistics/hash_table_test.c +++ b/test/core/statistics/hash_table_test.c @@ -38,6 +38,7 @@ #include "src/core/statistics/hash_table.h" #include "src/core/support/murmur_hash.h" +#include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/time.h> @@ -187,15 +188,15 @@ static void test_insertion_and_deletion_with_high_collision_rate(void) { census_ht_option opt = {CENSUS_HT_POINTER, 13, &force_collision, &cmp_str_keys, NULL, NULL}; census_ht* ht = census_ht_create(&opt); - char key_str[1000][10]; + char key_str[1000][GPR_LTOA_MIN_BUFSIZE]; gpr_uint64 val = 0; int i = 0; for (i = 0; i < 1000; i++) { census_ht_key key; key.ptr = key_str[i]; - sprintf(key_str[i], "%d", i); + gpr_ltoa(i, key_str[i]); census_ht_insert(ht, key, (void*)(&val)); - printf("%d\n", i); + gpr_log(GPR_INFO, "%d\n", i); GPR_ASSERT(census_ht_get_size(ht) == (i + 1)); } for (i = 0; i < 1000; i++) { diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c index 1576a30c1b..d155dee9dc 100644 --- a/test/core/transport/chttp2/hpack_table_test.c +++ b/test/core/transport/chttp2/hpack_table_test.c @@ -36,6 +36,7 @@ #include <string.h> #include <stdio.h> +#include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include "test/core/util/test_config.h" @@ -131,8 +132,8 @@ static void test_static_lookup(void) { static void test_many_additions(void) { grpc_chttp2_hptbl tbl; int i; - char key[32]; - char value[32]; + char *key; + char *value; grpc_mdctx *mdctx; LOG_TEST(); @@ -141,14 +142,18 @@ static void test_many_additions(void) { grpc_chttp2_hptbl_init(&tbl, mdctx); for (i = 0; i < 1000000; i++) { - sprintf(key, "K:%d", i); - sprintf(value, "VALUE:%d", i); + gpr_asprintf(&key, "K:%d", i); + gpr_asprintf(&value, "VALUE:%d", i); grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, key, value)); assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value); + gpr_free(key); + gpr_free(value); if (i) { - sprintf(key, "K:%d", i - 1); - sprintf(value, "VALUE:%d", i - 1); + gpr_asprintf(&key, "K:%d", i - 1); + gpr_asprintf(&value, "VALUE:%d", i - 1); assert_index(&tbl, 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value); + gpr_free(key); + gpr_free(value); } } @@ -226,7 +231,7 @@ static void test_find(void) { /* overflow the string buffer, check find still works */ for (i = 0; i < 10000; i++) { - sprintf(buffer, "%d", i); + gpr_ltoa(i, buffer); grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "test", buffer)); } @@ -245,7 +250,7 @@ static void test_find(void) { for (i = 0; i < tbl.num_ents; i++) { int expect = 9999 - i; - sprintf(buffer, "%d", expect); + gpr_ltoa(expect, buffer); r = find_simple(&tbl, "test", buffer); GPR_ASSERT(r.index == i + 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY); diff --git a/test/core/transport/chttp2/stream_encoder_test.c b/test/core/transport/chttp2/stream_encoder_test.c index eb0f688f58..5e8ec0a1af 100644 --- a/test/core/transport/chttp2/stream_encoder_test.c +++ b/test/core/transport/chttp2/stream_encoder_test.c @@ -186,7 +186,7 @@ static void encode_int_to_str(int i, char *p) { static void test_decode_table_overflow(void) { int i; char key[3], value[3]; - char expect[128]; + char *expect; for (i = 0; i < 114; i++) { if (i > 0) { @@ -197,18 +197,21 @@ static void test_decode_table_overflow(void) { encode_int_to_str(i + 1, value); if (i + 61 >= 127) { - sprintf(expect, "000009 0104 deadbeef ff%02x 40 02%02x%02x 02%02x%02x", - i + 61 - 127, key[0], key[1], value[0], value[1]); + gpr_asprintf(&expect, + "000009 0104 deadbeef ff%02x 40 02%02x%02x 02%02x%02x", + i + 61 - 127, key[0], key[1], value[0], value[1]); } else if (i > 0) { - sprintf(expect, "000008 0104 deadbeef %02x 40 02%02x%02x 02%02x%02x", - 0x80 + 61 + i, key[0], key[1], value[0], value[1]); + gpr_asprintf(&expect, + "000008 0104 deadbeef %02x 40 02%02x%02x 02%02x%02x", + 0x80 + 61 + i, key[0], key[1], value[0], value[1]); } else { - sprintf(expect, "000007 0104 deadbeef 40 02%02x%02x 02%02x%02x", key[0], - key[1], value[0], value[1]); + gpr_asprintf(&expect, "000007 0104 deadbeef 40 02%02x%02x 02%02x%02x", + key[0], key[1], value[0], value[1]); } add_sopb_header(key, value); verify_sopb(0, 0, 0, expect); + gpr_free(expect); } /* if the above passes, then we must have just knocked this pair out of the diff --git a/test/core/transport/chttp2/timeout_encoding_test.c b/test/core/transport/chttp2/timeout_encoding_test.c index ffa0070e34..0ad90dbcef 100644 --- a/test/core/transport/chttp2/timeout_encoding_test.c +++ b/test/core/transport/chttp2/timeout_encoding_test.c @@ -36,6 +36,8 @@ #include <stdio.h> #include <string.h> +#include "src/core/support/string.h" +#include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/useful.h> #include "test/core/util/test_config.h" @@ -93,16 +95,23 @@ void decode_suite(char ext, gpr_timespec (*answer)(long x)) { 1234567, 12345678, 123456789, 98765432, 9876543, 987654, 98765, 9876, 987, 98, 9}; int i; - char input[32]; + char *input; for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) { - sprintf(input, "%ld%c", test_vals[i], ext); + gpr_asprintf(&input, "%ld%c", test_vals[i], ext); assert_decodes_as(input, answer(test_vals[i])); - sprintf(input, " %ld%c", test_vals[i], ext); + gpr_free(input); + + gpr_asprintf(&input, " %ld%c", test_vals[i], ext); assert_decodes_as(input, answer(test_vals[i])); - sprintf(input, "%ld %c", test_vals[i], ext); + gpr_free(input); + + gpr_asprintf(&input, "%ld %c", test_vals[i], ext); assert_decodes_as(input, answer(test_vals[i])); - sprintf(input, "%ld %c ", test_vals[i], ext); + gpr_free(input); + + gpr_asprintf(&input, "%ld %c ", test_vals[i], ext); assert_decodes_as(input, answer(test_vals[i])); + gpr_free(input); } } diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c index 804096d0e1..b23db894be 100644 --- a/test/core/transport/metadata_test.c +++ b/test/core/transport/metadata_test.c @@ -35,6 +35,7 @@ #include <stdio.h> +#include "src/core/support/string.h" #include "src/core/transport/chttp2/bin_encoder.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -99,7 +100,7 @@ static void test_create_metadata(void) { static void test_create_many_ephemeral_metadata(void) { grpc_mdctx *ctx; - char buffer[256]; + char buffer[GPR_LTOA_MIN_BUFSIZE]; long i; size_t mdtab_capacity_before; @@ -109,7 +110,7 @@ static void test_create_many_ephemeral_metadata(void) { mdtab_capacity_before = grpc_mdctx_get_mdtab_capacity_test_only(ctx); /* add, and immediately delete a bunch of different elements */ for (i = 0; i < MANY; i++) { - sprintf(buffer, "%ld", i); + gpr_ltoa(i, buffer); grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", buffer)); } /* capacity should not grow */ @@ -120,7 +121,7 @@ static void test_create_many_ephemeral_metadata(void) { static void test_create_many_persistant_metadata(void) { grpc_mdctx *ctx; - char buffer[256]; + char buffer[GPR_LTOA_MIN_BUFSIZE]; long i; grpc_mdelem **created = gpr_malloc(sizeof(grpc_mdelem *) * MANY); grpc_mdelem *md; @@ -130,12 +131,12 @@ static void test_create_many_persistant_metadata(void) { ctx = grpc_mdctx_create(); /* add phase */ for (i = 0; i < MANY; i++) { - sprintf(buffer, "%ld", i); + gpr_ltoa(i, buffer); created[i] = grpc_mdelem_from_strings(ctx, "a", buffer); } /* verify phase */ for (i = 0; i < MANY; i++) { - sprintf(buffer, "%ld", i); + gpr_ltoa(i, buffer); md = grpc_mdelem_from_strings(ctx, "a", buffer); GPR_ASSERT(md == created[i]); grpc_mdelem_unref(md); @@ -176,7 +177,7 @@ static void test_spin_creating_the_same_thing(void) { static void test_things_stick_around(void) { grpc_mdctx *ctx; int i, j; - char buffer[64]; + char *buffer; int nstrs = 10000; grpc_mdstr **strs = gpr_malloc(sizeof(grpc_mdstr *) * nstrs); int *shuf = gpr_malloc(sizeof(int) * nstrs); @@ -187,9 +188,10 @@ static void test_things_stick_around(void) { ctx = grpc_mdctx_create(); for (i = 0; i < nstrs; i++) { - sprintf(buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", i); + gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", i); strs[i] = grpc_mdstr_from_string(ctx, buffer); shuf[i] = i; + gpr_free(buffer); } for (i = 0; i < nstrs; i++) { @@ -208,10 +210,11 @@ static void test_things_stick_around(void) { for (i = 0; i < nstrs; i++) { grpc_mdstr_unref(strs[shuf[i]]); for (j = i + 1; j < nstrs; j++) { - sprintf(buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]); + gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]); test = grpc_mdstr_from_string(ctx, buffer); GPR_ASSERT(test == strs[shuf[j]]); grpc_mdstr_unref(test); + gpr_free(buffer); } } diff --git a/tools/clang-format/clang-format-all.sh b/tools/clang-format/clang-format-all.sh deleted file mode 100755 index 62228b0b01..0000000000 --- a/tools/clang-format/clang-format-all.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e -source $(dirname $0)/config.sh -cd $(dirname $0)/../.. -for dir in src test include -do - find $dir -name '*.c' -or -name '*.cc' -or -name '*.h' | xargs $CLANG_FORMAT -i -done - diff --git a/tools/clang-format/config.sh b/tools/clang-format/config.sh deleted file mode 100644 index 3adf2678e7..0000000000 --- a/tools/clang-format/config.sh +++ /dev/null @@ -1,11 +0,0 @@ -CLANG_FORMAT=clang-format-3.5 - -set -ex - -if not hash $CLANG_FORMAT 2>/dev/null; then - echo "$CLANG_FORMAT is needed but not installed" - echo "perhaps try:" - echo " sudo apt-get install $CLANG_FORMAT" - exit 1 -fi - diff --git a/tools/dockerfile/grpc_java/Dockerfile b/tools/dockerfile/grpc_java/Dockerfile index 78659dedeb..f234f514e6 100644 --- a/tools/dockerfile/grpc_java/Dockerfile +++ b/tools/dockerfile/grpc_java/Dockerfile @@ -1,9 +1,6 @@ # Dockerfile for the gRPC Java dev image FROM grpc/java_base -# Start the daemon that allows access to private git-on-borg repos -RUN /var/local/git/gcompute-tools/git-cookie-authdaemon - RUN cd /var/local/git/grpc-java/lib/okhttp && \ mvn -pl okhttp -am install RUN cd /var/local/git/grpc-java/lib/netty && \ @@ -13,4 +10,4 @@ RUN cd /var/local/git/grpc-java && \ mvn install # Specify the default command such that the interop server runs on its known testing port -CMD ["/var/local/git/grpc-java/run-test-server.sh", "--transport=HTTP2_NETTY_TLS", "--grpc_version=2", "--port=8030"] +CMD ["/var/local/git/grpc-java/run-test-server.sh", "--use_tls=true", "--port=8030"] diff --git a/tools/dockerfile/grpc_java_base/Dockerfile b/tools/dockerfile/grpc_java_base/Dockerfile index 44fa52c0e8..3271d1b2c2 100644 --- a/tools/dockerfile/grpc_java_base/Dockerfile +++ b/tools/dockerfile/grpc_java_base/Dockerfile @@ -20,14 +20,24 @@ ENV M2_HOME /var/local/apache-maven-3.2.1 ENV PATH $PATH:$JAVA_HOME/bin:$M2_HOME/bin ENV LD_LIBRARY_PATH /usr/local/lib -# Start the daemon that allows access to the protected git-on-borg repos -RUN /var/local/git/gcompute-tools/git-cookie-authdaemon +# Install a GitHub SSH service credential that gives access to the GitHub repo while it's private +# TODO: remove this once the repo is public +ADD .ssh .ssh +RUN chmod 600 .ssh/github.rsa +RUN mkdir -p $HOME/.ssh && echo 'Host github.com' > $HOME/.ssh/config +RUN echo " IdentityFile /.ssh/github.rsa" >> $HOME/.ssh/config +RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config -RUN git clone --recursive https://team.googlesource.com/one-platform-grpc-team/grpc-java /var/local/git/grpc-java +# Get the protobuf source from GitHub and install it +RUN git clone --recursive --branch v2.6.1 git@github.com:google/protobuf.git /var/local/git/protobuf +RUN cd /var/local/git/protobuf && \ + ./autogen.sh && \ + ./configure --prefix=/usr && \ + make -j12 && make check && make install && make clean RUN cd /var/local/git/grpc-java/lib/okhttp && \ mvn -pl okhttp -am validate RUN cd /var/local/git/grpc-java/lib/netty && \ mvn -pl codec-http2 -am validate RUN cd /var/local/git/grpc-java && \ - mvn validate
\ No newline at end of file + mvn validate diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index 145685305c..d97f829435 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -655,7 +655,7 @@ grpc_interop_gen_go_cmd() { grpc_interop_gen_java_cmd() { local cmd_prefix="sudo docker run grpc/java"; local test_script="/var/local/git/grpc-java/run-test-client.sh"; - local test_script+=" --transport=NETTY_TLS --grpc_version=2" + local test_script+=" --server_host_override=foo.test.google.com --use_test_ca=true --use_tls=true" local the_cmd="$cmd_prefix $test_script $@"; echo $the_cmd } @@ -683,7 +683,7 @@ grpc_interop_gen_php_cmd() { # flags= .... # generic flags to include the command # cmd=$($grpc_gen_test_cmd $flags) grpc_interop_gen_cxx_cmd() { - local cmd_prefix="sudo docker run grpc/cxx"; + local cmd_prefix="sudo docker run grpc/cxx"; local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl"; local the_cmd="$cmd_prefix $test_script $@"; echo $the_cmd diff --git a/tools/run_tests/build_node.sh b/tools/run_tests/build_node.sh index 600b1bde8c..4b092982b2 100755 --- a/tools/run_tests/build_node.sh +++ b/tools/run_tests/build_node.sh @@ -2,19 +2,18 @@ set -ex +CONFIG=${CONFIG:-opt} + # change to grpc repo root cd $(dirname $0)/../.. # tells npm install to look for files in that directory export GRPC_ROOT=`pwd` # tells npm install the subdirectory with library files -export GRPC_LIB_SUBDIR=libs/opt +export GRPC_LIB_SUBDIR=libs/$CONFIG # tells npm install not to use default locations export GRPC_NO_INSTALL=yes -# build the c libraries -make -j static_c - cd src/node npm install diff --git a/tools/run_tests/build_php.sh b/tools/run_tests/build_php.sh index 6841656bdb..0a8d0c7492 100755 --- a/tools/run_tests/build_php.sh +++ b/tools/run_tests/build_php.sh @@ -2,14 +2,13 @@ set -ex +CONFIG=${CONFIG:-opt} + # change to grpc repo root cd $(dirname $0)/../.. root=`pwd` -export GRPC_LIB_SUBDIR=libs/opt - -# make the libraries -make -j static_c +export GRPC_LIB_SUBDIR=libs/$CONFIG # build php cd src/php @@ -18,4 +17,3 @@ cd ext/grpc phpize ./configure --enable-grpc=$root make - diff --git a/tools/run_tests/run_node.sh b/tools/run_tests/run_node.sh new file mode 100755 index 0000000000..0056295949 --- /dev/null +++ b/tools/run_tests/run_node.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +# change to grpc repo root +cd $(dirname $0)/../.. + +root=`pwd` + +$root/src/node/node_modules/mocha/bin/mocha $root/src/node/test diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index da849f04cb..b7248e524b 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -46,8 +46,8 @@ class CLanguage(object): self.make_target = make_target with open('tools/run_tests/tests.json') as f: js = json.load(f) - self.binaries = [tgt['name'] - for tgt in js + self.binaries = [tgt['name'] + for tgt in js if tgt['language'] == test_lang] def test_binaries(self, config): @@ -59,6 +59,19 @@ class CLanguage(object): def build_steps(self): return [] +class NodeLanguage(object): + + def __init__(self): + self.allow_hashing = False + + def test_binaries(self, config): + return ['tools/run_tests/run_node.sh'] + + def make_targets(self): + return ['static_c'] + + def build_steps(self): + return [['tools/run_tests/build_node.sh']] class PhpLanguage(object): @@ -69,7 +82,7 @@ class PhpLanguage(object): return ['src/php/bin/run_tests.sh'] def make_targets(self): - return [] + return ['static_c'] def build_steps(self): return [['tools/run_tests/build_php.sh']] @@ -107,6 +120,7 @@ _DEFAULT = ['dbg', 'opt'] _LANGUAGES = { 'c++': CLanguage('cxx', 'c++'), 'c': CLanguage('c', 'c'), + 'node': NodeLanguage(), 'php': PhpLanguage(), 'python': PythonLanguage(), } @@ -190,8 +204,8 @@ class TestCache(object): def _build_and_run(check_cancelled, newline_on_success, cache): """Do one pass of building & running tests.""" - # build latest, sharing cpu between the various makes - if not jobset.run(build_steps): + # build latest sequentially + if not jobset.run(build_steps, maxjobs=1): return 1 # run all the tests |