From 0c62fe8a3ac85f0a6d6f7247dd6fe0f2cbd1e55f Mon Sep 17 00:00:00 2001 From: Stanley Cheung Date: Mon, 5 Oct 2020 15:15:23 -0700 Subject: [PATCH] Fix non-determinism in code generator --- javascript/net/grpc/web/grpc_generator.cc | 20 ++--- .../net/grpc/web/grpcwebclientbase_test.js | 81 ++++++++++++------- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/javascript/net/grpc/web/grpc_generator.cc b/javascript/net/grpc/web/grpc_generator.cc index 276a00e..9811f3a 100644 --- a/javascript/net/grpc/web/grpc_generator.cc +++ b/javascript/net/grpc/web/grpc_generator.cc @@ -529,15 +529,17 @@ string GetBasename(string filename) { return basename; } -// Finds all message types used in all services in the file. -std::set GetAllMessages(const FileDescriptor* file) { - std::set messages; +// Finds all message types used in all services in the file. Return results as a +// map of full names to descriptors to get sorted results and deterministic +// build outputs. +std::map GetAllMessages(const FileDescriptor* file) { + std::map messages; for (int s = 0; s < file->service_count(); ++s) { const ServiceDescriptor* service = file->service(s); for (int m = 0; m < service->method_count(); ++m) { const MethodDescriptor *method = service->method(m); - messages.insert(method->input_type()); - messages.insert(method->output_type()); + messages[method->input_type()->full_name()] = method->input_type(); + messages[method->output_type()->full_name()] = method->output_type(); } } @@ -545,10 +547,10 @@ std::set GetAllMessages(const FileDescriptor* file) { } void PrintClosureDependencies(Printer* printer, const FileDescriptor* file) { - for (const Descriptor* message : GetAllMessages(file)) { + for (const auto &entry : GetAllMessages(file)) { printer->Print( "goog.require('proto.$full_name$');\n", - "full_name", message->full_name()); + "full_name", entry.second->full_name()); } printer->Print("\n\n\n"); } @@ -604,8 +606,8 @@ void PrintES6Imports(Printer* printer, const FileDescriptor* file) { printer->Print("import * as grpcWeb from 'grpc-web';\n\n"); std::set imports; - for (const Descriptor* message : GetAllMessages(file)) { - const string& name = message->file()->name(); + for (const auto &entry : GetAllMessages(file)) { + const string& name = entry.second->file()->name(); string dep_filename = GetRootPath(file->name(), name) + StripProto(name); if (imports.find(dep_filename) != imports.end()) { continue; diff --git a/javascript/net/grpc/web/grpcwebclientbase_test.js b/javascript/net/grpc/web/grpcwebclientbase_test.js index 77c8b14..dd96f50 100644 --- a/javascript/net/grpc/web/grpcwebclientbase_test.js +++ b/javascript/net/grpc/web/grpcwebclientbase_test.js @@ -19,27 +19,27 @@ goog.module('grpc.web.GrpcWebClientBaseTest'); goog.setTestOnly('grpc.web.GrpcWebClientBaseTest'); const ClientReadableStream = goog.require('grpc.web.ClientReadableStream'); -var EventType = goog.require('goog.net.EventType'); -var GrpcWebClientBase = goog.require('grpc.web.GrpcWebClientBase'); -var Map = goog.require('goog.structs.Map'); -var googCrypt = goog.require('goog.crypt.base64'); -var googEvents = goog.require('goog.events'); -var testSuite = goog.require('goog.testing.testSuite'); +const EventType = goog.require('goog.net.EventType'); +const GrpcWebClientBase = goog.require('grpc.web.GrpcWebClientBase'); +const Map = goog.require('goog.structs.Map'); +const googCrypt = goog.require('goog.crypt.base64'); +const googEvents = goog.require('goog.events'); +const testSuite = goog.require('goog.testing.testSuite'); const {StreamInterceptor} = goog.require('grpc.web.Interceptor'); goog.require('goog.testing.jsunit'); -var REQUEST_BYTES = [1, 2, 3]; -var FAKE_METHOD = 'fake-method'; -var PROTO_FIELD_VALUE = 'meow'; -var EXPECTED_HEADERS; -var EXPECTED_HEADER_VALUES; -var EXPECTED_UNARY_HEADERS = +const REQUEST_BYTES = [1, 2, 3]; +const FAKE_METHOD = 'fake-method'; +const PROTO_FIELD_VALUE = 'meow'; +const DEFAULT_UNARY_HEADERS = ['Content-Type', 'Accept', 'X-User-Agent', 'X-Grpc-Web']; -var EXPECTED_UNARY_HEADER_VALUES = [ +const DEFAULT_UNARY_HEADER_VALUES = [ 'application/grpc-web-text', 'application/grpc-web-text', 'grpc-web-javascript/0.1', '1' ]; -var dataCallback; +let dataCallback; +let expectedHeaders; +let expectedHeaderValues; testSuite({ @@ -53,8 +53,8 @@ testSuite({ }, tearDown: function() { - EXPECTED_HEADERS = null; - EXPECTED_HEADER_VALUES = null; + expectedHeaders = null; + expectedHeaderValues = null; }, testRpcResponse: function() { @@ -69,7 +69,7 @@ testSuite({ expectUnaryHeaders(); client.rpcCall( - FAKE_METHOD, {}, {}, { + FAKE_METHOD, /** requestMessage */ {}, /** metadata */ {}, { requestSerializeFn: function(request) { return REQUEST_BYTES; }, @@ -85,6 +85,28 @@ testSuite({ dataCallback(); }, + testDeadline: function() { + const client = new GrpcWebClientBase(); + client.newXhr_ = function() { + return new MockXhr({ + deadline: true, + response: googCrypt.encodeByteArray(new Uint8Array(0)), + }); + }; + + expectUnaryHeaders(); + const deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + client.rpcCall( + FAKE_METHOD, /** requestMessage */ {}, {'deadline': deadline}, { + requestSerializeFn: (request) => REQUEST_BYTES, + responseDeserializeFn: (bytes) => {}, + + }, + (error, response) => assertNull(error)); + dataCallback(); + }, + testStreamInterceptor: function() { var interceptor = new StreamResponseInterceptor(); var client = new GrpcWebClientBase({'streamInterceptors': [interceptor]}); @@ -180,8 +202,8 @@ testSuite({ /** Sets expected headers as the unary response headers */ function expectUnaryHeaders() { - EXPECTED_HEADERS = EXPECTED_UNARY_HEADERS; - EXPECTED_HEADER_VALUES = EXPECTED_UNARY_HEADER_VALUES; + expectedHeaders = [...DEFAULT_UNARY_HEADERS]; + expectedHeaderValues = [...DEFAULT_UNARY_HEADER_VALUES]; } @@ -198,18 +220,23 @@ class MockXhr { /** * @param {string} url - * @param {string=} opt_method - * @param {string=} opt_content - * @param {string=} opt_headers + * @param {string=} method + * @param {string=} content + * @param {string=} headers */ - send(url, opt_method, opt_content, opt_headers) { + send(url, method, content, headers) { assertEquals(FAKE_METHOD, url); - assertEquals('POST', opt_method); + assertEquals('POST', method); assertElementsEquals( googCrypt.encodeByteArray(new Uint8Array([0, 0, 0, 0, 3, 1, 2, 3])), - opt_content); - assertElementsEquals(EXPECTED_HEADERS, this.headers.getKeys()); - assertElementsEquals(EXPECTED_HEADER_VALUES, this.headers.getValues()); + content); + if (!this.mockValues.deadline) { + assertElementsEquals(expectedHeaders, this.headers.getKeys()); + assertElementsEquals(expectedHeaderValues, this.headers.getValues()); + } else { + expectedHeaders.push('grpc-timeout'); + assertElementsEquals(expectedHeaders, this.headers.getKeys()); + } } /**