public async Task MultipleCallsEventualSuccess(bool async, bool serverStreaming) { var callDuration = TimeSpan.FromTicks(300); var failures = 4; // Fifth call will succeed var name = "name"; // Copied from request to response var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(failures, callDuration, scheduler); var retrySettings = new RetrySettings( retryBackoff: DoublingBackoff, timeoutBackoff: ConstantBackoff, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: null, delayJitter: RetrySettings.NoJitter); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = name }; var result = await Call(async, serverStreaming, scheduler, server, request, callSettings); Assert.Equal(name, result.Name); }); var firstCall = time0; var secondCall = firstCall + callDuration + TimeSpan.FromTicks(1000); // Delay for 1000 ticks var thirdCall = secondCall + callDuration + TimeSpan.FromTicks(2000); // Delay for 2000 ticks var fourthCall = thirdCall + callDuration + TimeSpan.FromTicks(4000); // Delay for 4000 ticks var fifthCall = fourthCall + callDuration + TimeSpan.FromTicks(5000); // Delay for 5000 ticks, as that's the max server.AssertCallTimes(firstCall, secondCall, thirdCall, fourthCall, fifthCall); // Time of last action was when the call returned Assert.Equal(fifthCall + callDuration, scheduler.Clock.GetCurrentDateTimeUtc()); }
public async Task RetryFilter_EventualSuccess(bool async, StatusCode failureCode, StatusCode[] filterCodes) { var callDuration = 100; var failures = 1; var scheduler = new FakeScheduler(); var server = new Server(failures, callDuration, scheduler, failureCode); // We're not really interested in the timing in this test. var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, timeoutBackoff: ConstantBackoff, delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: RetrySettings.FilterForStatusCodes(filterCodes)); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var retryingCallable = server.Callable.WithRetry(scheduler.Clock, scheduler); await Call(async, retryingCallable, new SimpleRequest { Name = "irrelevant" }, callSettings); }); Assert.True(server.CallTimes.Count() > 1); }
public void test() { LanguageServiceSettings settings = new LanguageServiceSettings(); if (TimeOut > 0) { int hour = TimeOut / 3600; int min = TimeOut / 60; if (TimeOut >= 60) { TimeOut = TimeOut % 60; } TimeSpan ts0 = new TimeSpan(hour, min, TimeOut); settings.AnalyzeSentimentSettings = CallSettings.FromCallTiming(CallTiming.FromTimeout(ts0)); } string json = "{\"type\": \"service_account\",\"project_id\": \"" + Config["project_id"] + "\",\"private_key_id\": \"" + Config["private_key_id"] + "\",\"private_key\": \"" + Config["private_key"] + "\",\"client_email\": \"" + Config["client_email"] + "\",\"client_id\": \"" + Config["client_id"] + "\",\"auth_uri\": \"" + Config["auth_uri"] + "\",\"token_uri\": \"" + Config["token_uri"] + "\",\"auth_provider_x509_cert_url\": \"" + Config["auth_provider_x509_cert_url"] + "\",\"client_x509_cert_url\": \"" + Config["client_x509_cert_url"] + "\"}"; var credential = GoogleCredential.FromJson(json).CreateScoped(LanguageServiceClient.DefaultScopes); var channel = new Grpc.Core.Channel( LanguageServiceClient.DefaultEndpoint.ToString(), credential.ToChannelCredentials()); LanguageServiceClient test = LanguageServiceClient.Create(channel, settings); var Sentimentresponse = test.AnalyzeSentiment(new Document() { Content = "hello", Type = Document.Types.Type.PlainText }); }
/// <summary> /// Updates the call settings with configuration parameters. /// </summary> /// <param name="callSettings">The call settings.</param> /// <param name="config">The configuration.</param> /// <param name="serviceContext">The service context.</param> /// <returns></returns> private CallSettings UpdateCallSettingsWithConfigParameters(CallSettings callSettings, GoogleAdsConfig config, GoogleAdsServiceContext serviceContext) { callSettings = callSettings.WithHeader(GoogleAdsConfig.DEVELOPER_TOKEN_KEYNAME, config.DeveloperToken) .WithResponseMetadataHandler(delegate(Metadata metadata) { GoogleAdsResponseMetadata responseMetadata = new GoogleAdsResponseMetadata(metadata); serviceContext.OnResponseMetadataReceived(responseMetadata); }); if (!string.IsNullOrEmpty(config.LoginCustomerId)) { callSettings = callSettings.WithHeader(GoogleAdsConfig.LOGIN_CUSTOMER_ID_KEYNAME, config.LoginCustomerId); } if (!string.IsNullOrEmpty(config.LibraryIdentifierOverride)) { callSettings = callSettings.WithHeader(GoogleAdsConfig.LIBRARY_IDENTIFIER_KEYNAME, config.LibraryIdentifierOverride); } callSettings = callSettings.WithCallTiming(CallTiming.FromTimeout( TimeSpan.FromMilliseconds(config.Timeout))); return(callSettings); }
public async Task FirstCallSucceeds(bool async, bool serverStreaming) { var name = "name"; // Copied from request to response var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(0, TimeSpan.FromTicks(300), scheduler); var retrySettings = new RetrySettings( retryBackoff: DoublingBackoff, timeoutBackoff: ConstantBackoff, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: null, delayJitter: RetrySettings.NoJitter); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = name }; var result = await Call(async, serverStreaming, scheduler, server, request, callSettings); Assert.Equal(name, result.Name); }); server.AssertCallTimes(time0); // Time of last action was when the call returned Assert.Equal(300, scheduler.Clock.GetCurrentDateTimeUtc().Ticks); }
public async Task RetryFilter_EventualFailure(bool async, bool serverStreaming, StatusCode failureCode, StatusCode[] filterCodes) { var callDuration = TimeSpan.FromTicks(100); var failures = 1; var scheduler = new FakeScheduler(); var server = new Server(failures, callDuration, scheduler, failureCode); // We're not really interested in the timing in this test. var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, timeoutBackoff: ConstantBackoff, delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: RetrySettings.FilterForStatusCodes(filterCodes)); var task = scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); await Assert.ThrowsAsync <RpcException>(() => task); Assert.Equal(1, server.CallTimes.Count()); }
public async Task ExponentialTimeouts(bool async, bool serverStreaming) { var callDuration = TimeSpan.FromTicks(300); var failures = 2; var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(failures, callDuration, scheduler); var callable = server.Callable; var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, // 1500 ticks always timeoutBackoff: DoublingBackoff, // 1000, then 2000, then 4000 totalExpiration: Expiration.FromTimeout(TimeSpan.FromTicks(4500)), retryFilter: null, delayJitter: RetrySettings.NoJitter); await scheduler.RunAsync(async() => { // Expiration truncates the third timeout. We expect: // Call 1: t=0, deadline=1000, completes at 300 // Call 2: t=1800, deadline=3800 (2000+1800), completes at 2100 // Call 3, t=3600, deadline=4500 (would be 7600, but overall deadline truncates), completes at 3900 (with success) var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); server.AssertCallTimes(time0, time0 + TimeSpan.FromTicks(1800), time0 + TimeSpan.FromTicks(3600)); server.AssertDeadlines(time0 + TimeSpan.FromTicks(1000), time0 + TimeSpan.FromTicks(3800), time0 + TimeSpan.FromTicks(4500)); Assert.Equal(3900L, scheduler.Clock.GetCurrentDateTimeUtc().Ticks); }
public async Task RetryCancellation(bool serverStreaming, [CombinatorialValues(1500, 3500)] int delayMs) { // Note: Cannot test cancellation during wait for response header, due to FakeScheduler shortcomings. var async = true; var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(10, TimeSpan.FromSeconds(1), scheduler); var retrySettings = new RetrySettings( retryBackoff: new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), 1.0), timeoutBackoff: new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), 1.0), delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(10)), retryFilter: RetrySettings.DefaultFilter); var delay = TimeSpan.FromMilliseconds(delayMs); Task task = scheduler.RunAsync(async() => { var cts = new CancellationTokenSource(); var unused = Task.Run(async() => { await scheduler.Delay(delay); cts.Cancel(); }); var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)).WithCancellationToken(cts.Token); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); await Assert.ThrowsAsync <TaskCanceledException>(() => task); Assert.Equal(time0 + delay, scheduler.Clock.GetCurrentDateTimeUtc()); }
private async Task process() { InitializeQueue(); var response = await _sub.PullAsync(_subscriptionName, false, 1, CallSettings.FromCallTiming( CallTiming.FromExpiration( Expiration.FromTimeout( TimeSpan.FromSeconds(90))))); if (response.ReceivedMessages == null || response.ReceivedMessages.Count == 0) { return; } var message = response.ReceivedMessages[0]; var jsonBytes = message.Message.Data.ToByteArray(); var payload = Encoding.UTF8.GetString(jsonBytes); _appLogger.LogMessage($"Message Type: {typeof(T).FullName} dequeued with MessageId: {message.Message.MessageId}"); _messageHandler(payload); await _sub.AcknowledgeAsync(_subscriptionName, new string[] { message.AckId }); }
public async Task RetryFilter_EventualSuccess(bool async, bool serverStreaming) { StatusCode failureCode = StatusCode.NotFound; StatusCode[] filterCodes = new[] { StatusCode.NotFound, StatusCode.DeadlineExceeded }; var callDuration = TimeSpan.FromTicks(100); var failures = 1; var scheduler = new FakeScheduler(); var server = new Server(failures, callDuration, scheduler, failureCode); // We're not really interested in the timing in this test. var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, timeoutBackoff: ConstantBackoff, delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: RetrySettings.FilterForStatusCodes(filterCodes)); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); Assert.True(server.CallTimes.Count() > 1); }
/// <summary> /// Creates a service context that binds the service, callsettings and the client. /// </summary> /// <param name="config">The configuration.</param> /// <returns>The service context.</returns> private GoogleAdsServiceContext CreateServiceContext(GoogleAdsConfig config) { GoogleAdsServiceContext serviceContext = new GoogleAdsServiceContext(); CallSettings callSettings = CallSettings.FromCallTiming( CallTiming.FromRetry(new RetrySettings( retryBackoff : backoffSettings, timeoutBackoff : backoffSettings, totalExpiration : Expiration.FromTimeout(TimeSpan.FromMilliseconds( config.Timeout)), retryFilter : retryFilter ))) .WithHeader(GoogleAdsConfig.DEVELOPER_TOKEN_KEYNAME, config.DeveloperToken) .WithResponseMetadataHandler(delegate(Metadata metadata) { GoogleAdsResponseMetadata responseMetadata = new GoogleAdsResponseMetadata(metadata); serviceContext.OnResponseMetadataReceived(responseMetadata); }); if (!string.IsNullOrEmpty(config.LoginCustomerId)) { callSettings.WithHeader("login-customer-id", config.LoginCustomerId); } serviceContext.CallSettings = callSettings; return(serviceContext); }
public async Task CallSettingsDeadlineIsObserved(bool async, bool serverStreaming) { var callDuration = TimeSpan.FromTicks(300); var failures = 4; // Fifth call would succeed, but we won't get that far. var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(failures, callDuration, scheduler); var callable = server.Callable; var retrySettings = new RetrySettings( retryBackoff: DoublingBackoff, timeoutBackoff: ConstantBackoff, totalExpiration: Expiration.FromTimeout(TimeSpan.FromTicks(2500)), retryFilter: null, delayJitter: RetrySettings.NoJitter); var task = scheduler.RunAsync(async() => { // Expiration makes it fail while waiting to make third call var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); await Assert.ThrowsAsync <RpcException>(() => task); var firstCall = time0; var secondCall = firstCall + callDuration + TimeSpan.FromTicks(1000); server.AssertCallTimes(firstCall, secondCall); // We fail immediately when we work out that we would time out before we make the third // call - so this is before the actual total timeout. Assert.Equal((secondCall + callDuration).Ticks, scheduler.Clock.GetCurrentDateTimeUtc().Ticks); }
public void FromCallTiming() { Assert.Null(CallSettings.FromCallTiming(null)); var timing = CallTiming.FromExpiration(Expiration.None); var settings = CallSettings.FromCallTiming(timing); Assert.Same(timing, settings.Timing); }
/// <summary> /// Retry Call Setting for Create Table /// </summary> /// <param name="tryCount"></param> /// <returns></returns> private BigtableTableAdminSettings CreateRetryCallSettings() { var clientSettings = BigtableTableAdminSettings.GetDefault(); var longTimeout = CallTiming.FromTimeout(TimeSpan.FromMinutes(2)); clientSettings.CreateTableSettings = clientSettings.CreateTableSettings.WithCallTiming(longTimeout); return(clientSettings); }
public void SucceedWithExpiration() { var apiCall = ApiServerStreamingCall.Create <int, int>( (request, callOptions) => null, CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(TimeSpan.FromSeconds(100)))), new FakeClock()); Assert.Null(apiCall.Call(0, null)); }
public void WithCallTiming_NullSettings() { CallSettings noSettings = null; Assert.Null(noSettings.WithCallTiming(null)); CallTiming timing = CallTiming.FromExpiration(Expiration.None); var result = noSettings.WithCallTiming(timing); Assert.Same(timing, result.Timing); }
public void WithEarlierDeadline_DeadlineIsLaterThanExistingDeadline() { // Use a cancellation token to emphasize that it's not just the timing. var token = new CancellationTokenSource().Token; CallSettings settings = CallSettings.FromCancellationToken(token) .WithCallTiming(CallTiming.FromExpiration(Expiration.FromDeadline(new DateTime(10L, DateTimeKind.Utc)))); DateTime?deadline = new DateTime(20L, DateTimeKind.Utc); Assert.Same(settings, settings.WithEarlierDeadline(deadline, new FakeClock())); }
public void SucceedWithExpiration() { var apiCall = ApiBidirectionalStreamingCall.Create <int, int>( callOptions => null, CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(TimeSpan.FromSeconds(100)))), new BidirectionalStreamingSettings(100), new FakeClock()); Assert.Null(apiCall.Call(null)); }
public void ToCallOptions_CallTimingExpirationTimeout() { var clock = new FakeClock(); var timeout = TimeSpan.FromSeconds(1); CallSettings callSettings = CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(timeout))); var options = callSettings.ToCallOptions(clock); // Value should be exact, as we control time precisely. Assert.Equal(options.Deadline.Value, clock.GetCurrentDateTimeUtc() + timeout); }
public void WithCallTiming_NullTiming() { CallTiming timing = CallTiming.FromExpiration(Expiration.None); CancellationToken token = new CancellationTokenSource().Token; var original = new CallSettings(token, null, timing, null, null, null); var result = original.WithCallTiming(null); Assert.Null(result.Timing); Assert.Equal(token, result.CancellationToken); }
public void WithExpiration_SettingsWithExpiration() { var token = new CancellationTokenSource().Token; var originalTiming = CallTiming.FromTimeout(TimeSpan.FromSeconds(5)); CallSettings settings = CallSettings.FromCancellationToken(token).WithCallTiming(originalTiming); Expiration expiration = Expiration.FromTimeout(TimeSpan.FromSeconds(1)); var result = settings.WithExpiration(expiration); Assert.Same(expiration, result.Timing.Expiration); Assert.Equal(token, result.CancellationToken); }
public void ToCallOptions_CallTimingExpirationDeadline() { var deadline = new DateTime(2015, 6, 19, 5, 2, 3, DateTimeKind.Utc); var mockClock = new Mock <IClock>(); CallSettings callSettings = CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromDeadline(deadline))); var options = callSettings.ToCallOptions(mockClock.Object); // Value should be exact, as we control time precisely. Assert.Equal(options.Deadline.Value, deadline); mockClock.Verify(c => c.GetCurrentDateTimeUtc(), Times.Never); }
public void WithEarlierDeadline_DeadlineIsLaterThanExistingTimeout() { // Use a cancellation token to emphasize that it's not just the timing. var token = new CancellationTokenSource().Token; CallSettings settings = CallSettings.FromCancellationToken(token) .WithCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(TimeSpan.FromTicks(100)))); var clock = new FakeClock(); DateTime?deadline = clock.GetCurrentDateTimeUtc() + TimeSpan.FromTicks(200); Assert.Same(settings, settings.WithEarlierDeadline(deadline, clock)); }
public void WithEarlierDeadline_DeadlineIsLaterThanExistingRetryTotalExpiration() { var backoffSettings = new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), 2.0); // Use a cancellation token to emphasize that it's not just the timing. var token = new CancellationTokenSource().Token; var timing = CallTiming.FromRetry(new RetrySettings(backoffSettings, backoffSettings, Expiration.FromDeadline(new DateTime(100L, DateTimeKind.Utc)))); CallSettings settings = CallSettings.FromCancellationToken(token) .WithCallTiming(timing); DateTime?deadline = new DateTime(200L, DateTimeKind.Utc); Assert.Same(settings, settings.WithEarlierDeadline(deadline, new FakeClock())); }
public void WithCancellationToken() { CancellationToken token1 = new CancellationTokenSource().Token; CallTiming timing = CallTiming.FromExpiration(Expiration.None); var original = new CallSettings(token1, null, timing, null, null, null); CancellationToken token2 = new CancellationTokenSource().Token; var result = original.WithCancellationToken(token2); Assert.Same(timing, result.Timing); Assert.Equal(token2, result.CancellationToken); }
public void FailWithRetry() { var apiCall = ApiServerStreamingCall.Create <int, int>( (request, callOptions) => null, CallSettings.FromCallTiming(CallTiming.FromRetry(new RetrySettings( new BackoffSettings(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(100), 2.0), new BackoffSettings(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(100), 2.0), Expiration.FromTimeout(TimeSpan.FromSeconds(100))))), new FakeClock()); Assert.Throws <InvalidOperationException>(() => apiCall.Call(0, null)); }
public StackDriverLogger(string projectId) { _projectId = projectId; _retryAWhile = CallSettings.FromCallTiming(CallTiming.FromRetry(new RetrySettings( new BackoffSettings(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(12), 2.0), new BackoffSettings(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(120)), Google.Api.Gax.Expiration.FromTimeout(TimeSpan.FromSeconds(180)), (Grpc.Core.RpcException e) => new[] { Grpc.Core.StatusCode.Internal, Grpc.Core.StatusCode.DeadlineExceeded } .Contains(e.Status.StatusCode) ))); }
public void WithCallTiming_NonNull() { CallTiming timing1 = CallTiming.FromExpiration(Expiration.None); CallTiming timing2 = CallTiming.FromDeadline(DateTime.UtcNow); CancellationToken token = new CancellationTokenSource().Token; var original = new CallSettings(token, null, timing1, null, null, null); var result = original.WithCallTiming(timing2); Assert.Same(timing2, result.Timing); Assert.Equal(token, result.CancellationToken); }
/// <inheritdoc /> public async Task <SpannerClient> CreateClientAsync(ServiceEndpoint endpoint, ITokenAccess credential, IDictionary additionalOptions) { ChannelCredentials channelCredentials; var allowImmediateTimeout = false; if (additionalOptions.Contains(nameof(SpannerSettings.AllowImmediateTimeouts))) { allowImmediateTimeout = Convert.ToBoolean(additionalOptions[nameof(SpannerSettings.AllowImmediateTimeouts)]); } if (credential == null) { channelCredentials = await CreateDefaultChannelCredentialsAsync().ConfigureAwait(false); } else { channelCredentials = credential.ToChannelCredentials(); } var channel = new Channel( endpoint.Host, endpoint.Port, channelCredentials); Logger.LogPerformanceCounterFn("SpannerClient.RawCreateCount", x => x + 1); //Pull the timeout from spanner options. //The option must be set before OpenAsync is called. var idempotentCallSettings = CallSettings.FromCallTiming( CallTiming.FromRetry( new RetrySettings( SpannerSettings.GetDefaultRetryBackoff(), SpannerSettings.GetDefaultTimeoutBackoff(), SpannerSettings.ConvertTimeoutToExpiration(SpannerOptions.Instance.Timeout, allowImmediateTimeout), SpannerSettings.IdempotentRetryFilter ))); return(SpannerClient.Create( channel, new SpannerSettings { CreateSessionSettings = idempotentCallSettings, GetSessionSettings = idempotentCallSettings, DeleteSessionSettings = idempotentCallSettings, ExecuteSqlSettings = idempotentCallSettings, ReadSettings = idempotentCallSettings, BeginTransactionSettings = idempotentCallSettings, CommitSettings = idempotentCallSettings, RollbackSettings = idempotentCallSettings, AllowImmediateTimeouts = allowImmediateTimeout })); }
public void MergedWith_BothNonNull() { CallTiming timing1 = CallTiming.FromExpiration(Expiration.None); CallTiming timing2 = CallTiming.FromDeadline(DateTime.UtcNow); CancellationToken token = new CancellationTokenSource().Token; var settings1 = new CallSettings(token, null, timing1, null, null, null); var settings2 = new CallSettings(null, null, timing2, null, null, null); var merged = settings1.MergedWith(settings2); Assert.Equal(token, merged.CancellationToken); Assert.Same(timing2, merged.Timing); }