static async Task ReportTestInfoAsync() { var testInfoResults = new string[] { $"Network Run Profile={Settings.Current.NetworkRunProfile}", $"Network Network Id={Settings.Current.NetworkId}", $"Network Frequencies={string.Join(",", Settings.Current.Frequencies.Select(f => $"[offline:{f.OfflineFrequency},Online:{f.OnlineFrequency},Runs:{f.RunsCount}]"))}" }; var testResultReportingClient = new TestResultReportingClient() { BaseUrl = Settings.Current.TestResultCoordinatorEndpoint.AbsoluteUri }; foreach (string testInfo in testInfoResults) { await ModuleUtil.ReportTestResultAsync( testResultReportingClient, Log, new TestInfoResult( Settings.Current.TrackingId, Settings.Current.ModuleId, testInfo, DateTime.UtcNow)); } }
static async Task Main(string[] args) { Logger.LogInformation($"Starting Deployment Tester with the following settings: \r\n{Settings.Current}"); try { (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(TimeSpan.FromSeconds(5), Logger); var testResultReportingClient = new TestResultReportingClient { BaseUrl = Settings.Current.TestResultCoordinatorUrl.AbsoluteUri }; if (Settings.Current.TestMode == DeploymentTesterMode.Receiver) { await ReportDeploymentEnvironmentVariablesAsync(testResultReportingClient); } else { await Task.Delay(Settings.Current.TestStartDelay); await UpdateDeploymentEnvironmentVariablesAsync(testResultReportingClient, cts); } await cts.Token.WhenCanceled(); completed.Set(); handler.ForEach(h => GC.KeepAlive(h)); Logger.LogInformation("DeploymentTester Main() finished."); } catch (Exception ex) { Logger.LogError(ex, "Unexpected exception found."); } }
static async Task UpdateDeploymentEnvironmentVariablesAsync(TestResultReportingClient apiClient, CancellationTokenSource cts) { RegistryManager registryManager = null; try { registryManager = RegistryManager.CreateFromConnectionString(Settings.Current.IoTHubConnectionString.OrDefault()); JObject deploymentJson = await GetEdgeAgentDeploymentManifestJsonAsync(registryManager, Settings.Current.DeviceId); DateTime testStartAt = DateTime.UtcNow; long count = 1; var envVars = new Dictionary <string, string>(); while (!cts.IsCancellationRequested && DateTime.UtcNow - testStartAt < Settings.Current.TestDuration) { KeyValuePair <string, string> newEnvVar = AddEnvironmentValue(deploymentJson, Settings.Current.TargetModuleId.OrDefault(), count); ConfigurationContent configContent = JsonConvert.DeserializeObject <ConfigurationContent>(deploymentJson.ToString()); await registryManager.ApplyConfigurationContentOnDeviceAsync(Settings.Current.DeviceId, configContent); envVars.Add(newEnvVar.Key, newEnvVar.Value); var testResult = new DeploymentTestResult(Settings.Current.TrackingId, Settings.Current.ModuleId + ".send", envVars, DateTime.UtcNow); await ModuleUtil.ReportTestResultAsync(apiClient, Logger, testResult); Logger.LogInformation($"Successfully report to TRC for new deployment: tracking id={Settings.Current.TrackingId}, new environment variable={newEnvVar.Key}:{newEnvVar.Value}, EnvVars Count={envVars.Count}."); await Task.Delay(Settings.Current.DeploymentUpdatePeriod, cts.Token); count++; } } finally { registryManager?.Dispose(); } }
public TwinAllOperationsResultHandler(Uri reportUrl, TwinEventStorage storage, string moduleId) { this.testResultReportingClient = new TestResultReportingClient { BaseUrl = reportUrl.AbsoluteUri }; this.moduleId = moduleId; this.storage = storage; }
public TwinEdgeOperationsResultHandler(Uri reportUrl, string moduleId, Option <string> trackingId) { this.testResultReportingClient = new TestResultReportingClient { BaseUrl = reportUrl.AbsoluteUri }; this.moduleId = moduleId; this.trackingId = trackingId.Expect(() => new ArgumentNullException(nameof(trackingId))); }
static async Task <DateTime> RestartEdgeHubAsync( ServiceClient iotHubServiceClient, CancellationToken cancellationToken) { DateTime startAttemptTime = DateTime.UtcNow; CloudToDeviceMethod c2dMethod = new CloudToDeviceMethod("RestartModule"); string payloadSchema = "{{ \"SchemaVersion\": \"1.0\", \"Id\": \"{0}\" }}"; string payload = string.Format(payloadSchema, "edgeHub"); Logger.LogInformation("RestartModule Method Payload: {0}", payload); c2dMethod.SetPayloadJson(payload); while (true) { try { // TODO: Introduce the offline scenario to use docker command. CloudToDeviceMethodResult response = await iotHubServiceClient.InvokeDeviceMethodAsync(Settings.Current.DeviceId, "$edgeAgent", c2dMethod); if ((HttpStatusCode)response.Status != HttpStatusCode.OK) { Logger.LogError($"Calling EdgeHub restart failed with status code {response.Status} : {response.GetPayloadAsJson()}."); } else { Logger.LogInformation($"Calling EdgeHub restart succeeded with status code {response.Status}."); } return(DateTime.UtcNow); } catch (Exception e) { Logger.LogError($"Exception caught for payload {payload}: {e}"); if (Settings.Current.RestartPeriod < DateTime.UtcNow - startAttemptTime) { string errorMessage = $"Failed to restart EdgeHub from {startAttemptTime} to {DateTime.UtcNow}:\n\n{e}\n\nPayload: {payload}"; TestResultBase errorResult = new ErrorTestResult( Settings.Current.TrackingId, GetSource(), errorMessage, DateTime.UtcNow); var reportClient = new TestResultReportingClient { BaseUrl = Settings.Current.ReportingEndpointUrl.AbsoluteUri }; await ModuleUtil.ReportTestResultAsync( reportClient, Logger, errorResult, cancellationToken); throw; } } } }
public NetworkStatusReporter(Uri testResultCoordinatorEndpoint, string moduleId, string trackingId) { this.testResultReportingClient = new TestResultReportingClient() { BaseUrl = testResultCoordinatorEndpoint.AbsoluteUri }; this.moduleId = moduleId; this.trackingId = trackingId; }
internal TestResultReporterClient(Uri baseUri, ILogger logger) : base(logger) { Preconditions.CheckNotNull(baseUri, nameof(baseUri)); this.baseUri = baseUri; this.testResultReportingClient = new TestResultReportingClient { BaseUrl = baseUri.AbsoluteUri }; this.logger = Preconditions.CheckNotNull(logger, nameof(logger)); }
TestResultReportingClient GetReportClient() { if (this.reportClient == null) { this.reportClient = new TestResultReportingClient { BaseUrl = Settings.Current.ReportingEndpointUrl.AbsoluteUri }; } return(this.reportClient); }
public static async Task <int> MainAsync() { Logger.LogInformation($"Starting CloudToDeviceMessageTester with the following settings:\r\n{Settings.Current}"); DateTime testStartAt = DateTime.UtcNow; (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(TimeSpan.FromSeconds(5), Logger); ICloudToDeviceMessageTester cloudToDeviceMessageTester = null; TestResultReportingClient reportClient = new TestResultReportingClient() { BaseUrl = Settings.Current.ReportingEndpointUrl.AbsoluteUri }; try { if (Settings.Current.TestMode == CloudToDeviceMessageTesterMode.Receiver) { cloudToDeviceMessageTester = new CloudToDeviceMessageReceiver( Logger, Settings.Current.SharedSettings, Settings.Current.ReceiverSettings, reportClient); } else { cloudToDeviceMessageTester = new CloudToDeviceMessageSender( Logger, Settings.Current.SharedSettings, Settings.Current.SenderSettings, reportClient); } await cloudToDeviceMessageTester.StartAsync(cts.Token); } catch (Exception ex) { Logger.LogError(ex, $"Error occurred during CloudToDeviceMessageTester while in {Settings.Current.TestMode} mode."); } finally { // Implicit CloseAsync() cloudToDeviceMessageTester?.Dispose(); } await cts.Token.WhenCanceled(); completed.Set(); handler.ForEach(h => GC.KeepAlive(h)); Logger.LogInformation($"{nameof(Program.MainAsync)} finished."); return(0); }
internal CloudToDeviceMessageSender( ILogger logger, C2DTestSharedSettings sharedMetadata, C2DTestSenderSettings senderMetadata, TestResultReportingClient testResultReportingClient) { this.logger = Preconditions.CheckNotNull(logger, nameof(logger)); this.iotHubConnectionString = Preconditions.CheckNonWhiteSpace(sharedMetadata.IotHubConnectionString, nameof(sharedMetadata.IotHubConnectionString)); this.deviceId = Preconditions.CheckNonWhiteSpace(sharedMetadata.DeviceId, nameof(sharedMetadata.DeviceId)); this.moduleId = Preconditions.CheckNonWhiteSpace(sharedMetadata.ModuleId, nameof(sharedMetadata.ModuleId)); this.trackingId = Preconditions.CheckNonWhiteSpace(senderMetadata.TrackingId, nameof(senderMetadata.TrackingId)); this.messageDelay = senderMetadata.MessageDelay; this.testStartDelay = senderMetadata.TestStartDelay; this.testDuration = senderMetadata.TestDuration; this.testResultReportingClient = Preconditions.CheckNotNull(testResultReportingClient, nameof(testResultReportingClient)); }
public async Task PriorityQueueModuleToHubMessages() { // TODO: Add Windows and ARM32. Windows won't be able to work for this test until we add NetworkController Windows implementation if (OsPlatform.IsWindows() || !OsPlatform.Is64Bit()) { Assert.Ignore("Priority Queue module to module messages test has been disabled for Windows and Arm32 until we can fix it."); } CancellationToken token = this.TestToken; string trcImage = Context.Current.TestResultCoordinatorImage.Expect(() => new ArgumentException("testResultCoordinatorImage parameter is required for Priority Queues test")); string loadGenImage = Context.Current.LoadGenImage.Expect(() => new ArgumentException("loadGenImage parameter is required for Priority Queues test")); string relayerImage = Context.Current.RelayerImage.Expect(() => new ArgumentException("relayerImage parameter is required for Priority Queues test")); string networkControllerImage = Context.Current.NetworkControllerImage.Expect(() => new ArgumentException("networkControllerImage parameter is required for Priority Queues test")); string trackingId = Guid.NewGuid().ToString(); TestInfo testInfo = this.InitTestInfo(5, 1000, true); var testResultReportingClient = new TestResultReportingClient { BaseUrl = "http://localhost:5001" }; Action <EdgeConfigBuilder> addInitialConfig = this.BuildAddInitialConfig(trackingId, "hubtest", trcImage, loadGenImage, testInfo, true); Action <EdgeConfigBuilder> addNetworkControllerConfig = this.BuildAddNetworkControllerConfig(trackingId, networkControllerImage); EdgeDeployment deployment = await this.runtime.DeployConfigurationAsync(addInitialConfig + addNetworkControllerConfig, token); bool networkOn = true; await this.ToggleConnectivity(!networkOn, NetworkControllerModuleName, token); await Task.Delay(TimeSpan.Parse(LoadGenTestDuration) + TimeSpan.Parse(LoadGenTestStartDelay) + TimeSpan.FromSeconds(10)); await this.ToggleConnectivity(networkOn, NetworkControllerModuleName, token); PriorityQueueTestStatus loadGenTestStatus = await this.PollUntilFinishedAsync(LoadGenModuleName, token); ConcurrentQueue <MessageTestResult> messages = new ConcurrentQueue <MessageTestResult>(); await this.ReceiveEventsFromIotHub(deployment.StartTime, messages, loadGenTestStatus, token); while (messages.TryDequeue(out MessageTestResult messageTestResult)) { await testResultReportingClient.ReportResultAsync(messageTestResult.ToTestOperationResultDto()); } await this.ValidateResultsAsync(); }
protected async Task ReportResult(long messageIdCounter) { await Settings.Current.TestResultCoordinatorUrl.ForEachAsync( async trcUrl => { var testResultCoordinatorUrl = new Uri(trcUrl, UriKind.Absolute); var testResultReportingClient = new TestResultReportingClient { BaseUrl = testResultCoordinatorUrl.AbsoluteUri }; var testResult = new MessageTestResult(Settings.Current.ModuleId + ".send", DateTime.UtcNow) { TrackingId = this.TrackingId, BatchId = this.BatchId.ToString(), SequenceNumber = messageIdCounter.ToString() }; await ModuleUtil.ReportTestResultAsync(testResultReportingClient, this.Logger, testResult); }); }
internal CloudToDeviceMessageReceiver( ILogger logger, C2DTestSharedSettings sharedMetadata, C2DTestReceiverSettings receiverMetadata, TestResultReportingClient testResultReportingClient) { this.logger = Preconditions.CheckNotNull(logger, nameof(logger)); this.iotHubConnectionString = Preconditions.CheckNonWhiteSpace(sharedMetadata.IotHubConnectionString, nameof(sharedMetadata.IotHubConnectionString)); this.deviceId = Preconditions.CheckNonWhiteSpace(sharedMetadata.DeviceId, nameof(sharedMetadata.DeviceId)); this.moduleId = Preconditions.CheckNonWhiteSpace(sharedMetadata.ModuleId, nameof(sharedMetadata.ModuleId)); this.transportType = receiverMetadata.TransportType; this.gatewayHostName = Preconditions.CheckNonWhiteSpace(receiverMetadata.GatewayHostName, nameof(receiverMetadata.GatewayHostName)); this.workloadUri = Preconditions.CheckNonWhiteSpace(receiverMetadata.WorkloadUri, nameof(receiverMetadata.WorkloadUri)); this.apiVersion = Preconditions.CheckNonWhiteSpace(receiverMetadata.ApiVersion, nameof(receiverMetadata.ApiVersion)); this.moduleGenerationId = Preconditions.CheckNonWhiteSpace(receiverMetadata.ModuleGenerationId, nameof(receiverMetadata.ModuleGenerationId)); this.iotHubHostName = Preconditions.CheckNonWhiteSpace(receiverMetadata.IotHubHostName, nameof(receiverMetadata.IotHubHostName)); this.testResultReportingClient = Preconditions.CheckNotNull(testResultReportingClient, nameof(testResultReportingClient)); }
static async Task ReportDeploymentEnvironmentVariablesAsync(TestResultReportingClient trcClient) { // Report all environment variable with predefined prefix to Test Result Coordinator var envVars = new Dictionary <string, string>(); foreach (DictionaryEntry envVariable in Environment.GetEnvironmentVariables()) { if (envVariable.Key.ToString().StartsWith(Settings.EnvironmentVariablePrefix, StringComparison.OrdinalIgnoreCase)) { envVars.Add(envVariable.Key.ToString(), envVariable.Value.ToString()); } } var testResult = new DeploymentTestResult(Settings.Current.TrackingId, Settings.Current.ModuleId + ".receive", envVars, DateTime.UtcNow); await ModuleUtil.ReportTestResultAsync(trcClient, Logger, testResult); Logger.LogInformation($"Successfully report to TRC for new deployment: tracking id={Settings.Current.TrackingId}, environment variable count={testResult.EnvironmentVariables.Count}."); }
public async Task PriorityQueueModuleToHubMessages() { CancellationToken token = this.TestToken; string trcImage = Context.Current.TestResultCoordinatorImage.Expect(() => new ArgumentException("testResultCoordinatorImage parameter is required for Priority Queues test")); string loadGenImage = Context.Current.LoadGenImage.Expect(() => new ArgumentException("loadGenImage parameter is required for Priority Queues test")); string relayerImage = Context.Current.RelayerImage.Expect(() => new ArgumentException("relayerImage parameter is required for Priority Queues test")); string networkControllerImage = Context.Current.NetworkControllerImage.Expect(() => new ArgumentException("networkControllerImage parameter is required for Priority Queues test")); string trackingId = Guid.NewGuid().ToString(); TestInfo testInfo = this.InitTestInfo(5, 1000, true, "00:00:40"); var testResultReportingClient = new TestResultReportingClient { BaseUrl = "http://localhost:5001" }; Action <EdgeConfigBuilder> addLoadGenConfig = this.BuildAddLoadGenConfig(trackingId, loadGenImage, testInfo, true); Action <EdgeConfigBuilder> addTrcConfig = TestResultCoordinatorUtil.BuildAddTestResultCoordinatorConfig(trackingId, trcImage, LoadGenModuleName, "hubtest"); Action <EdgeConfigBuilder> addNetworkControllerConfig = TestResultCoordinatorUtil.BuildAddNetworkControllerConfig(trackingId, networkControllerImage); EdgeDeployment deployment = await this.runtime.DeployConfigurationAsync(addLoadGenConfig + addTrcConfig + addNetworkControllerConfig, token, Context.Current.NestedEdge); bool networkOn = true; await this.ToggleConnectivity(!networkOn, NetworkControllerModuleName, token); await Task.Delay(TimeSpan.Parse(LoadGenTestDuration) + TimeSpan.Parse(testInfo.LoadGenStartDelay) + TimeSpan.FromSeconds(10)); await this.ToggleConnectivity(networkOn, NetworkControllerModuleName, token); PriorityQueueTestStatus loadGenTestStatus = await this.PollUntilFinishedAsync(LoadGenModuleName, token); ConcurrentQueue <MessageTestResult> messages = new ConcurrentQueue <MessageTestResult>(); await this.ReceiveEventsFromIotHub(deployment.StartTime, messages, loadGenTestStatus, trackingId, token); while (messages.TryDequeue(out MessageTestResult messageTestResult)) { await testResultReportingClient.ReportResultAsync(messageTestResult.ToTestOperationResultDto()); } await TestResultCoordinatorUtil.ValidateResultsAsync(); }
static async Task Main() { Logger.LogInformation($"Starting load gen with the following settings:\r\n{Settings.Current}"); ModuleClient moduleClient = null; try { (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(TimeSpan.FromSeconds(5), Logger); Guid batchId = Guid.NewGuid(); Logger.LogInformation($"Batch Id={batchId}"); moduleClient = await ModuleUtil.CreateModuleClientAsync( Settings.Current.TransportType, ModuleUtil.DefaultTimeoutErrorDetectionStrategy, ModuleUtil.DefaultTransientRetryStrategy, Logger); Logger.LogInformation($"Load gen delay start for {Settings.Current.TestStartDelay}."); await Task.Delay(Settings.Current.TestStartDelay); DateTime testStartAt = DateTime.UtcNow; long messageIdCounter = 1; while (!cts.IsCancellationRequested && (Settings.Current.TestDuration == TimeSpan.Zero || DateTime.UtcNow - testStartAt < Settings.Current.TestDuration)) { try { await SendEventAsync(moduleClient, batchId, Settings.Current.TrackingId, messageIdCounter); // Report sending message successfully to Test Result Coordinator await Settings.Current.TestResultCoordinatorUrl.ForEachAsync( async trcUrl => { var testResultCoordinatorUrl = new Uri(trcUrl, UriKind.Absolute); var testResultReportingClient = new TestResultReportingClient { BaseUrl = testResultCoordinatorUrl.AbsoluteUri }; var testResult = new MessageTestResult(Settings.Current.ModuleId + ".send", DateTime.UtcNow) { TrackingId = Settings.Current.TrackingId, BatchId = batchId.ToString(), SequenceNumber = messageIdCounter.ToString() }; await ModuleUtil.ReportTestResultAsync(testResultReportingClient, Logger, testResult); }); if (messageIdCounter % 1000 == 0) { Logger.LogInformation($"Sent {messageIdCounter} messages."); } await Task.Delay(Settings.Current.MessageFrequency); messageIdCounter++; } catch (Exception ex) { Logger.LogError(ex, $"[SendEventAsync] Sequence number {messageIdCounter}, BatchId: {batchId.ToString()};"); } } Logger.LogInformation("Finish sending messages."); await cts.Token.WhenCanceled(); completed.Set(); handler.ForEach(h => GC.KeepAlive(h)); } catch (Exception ex) { Logger.LogError(ex, "Error occurred during load gen."); } finally { Logger.LogInformation("Closing connection to Edge Hub."); moduleClient?.CloseAsync(); moduleClient?.Dispose(); } Logger.LogInformation("Load Gen complete. Exiting."); }
static async Task <MessageResponse> ProcessAndSendMessageAsync(Message message, object userContext) { Uri testResultCoordinatorUrl = Settings.Current.TestResultCoordinatorUrl; try { if (!(userContext is ModuleClient moduleClient)) { throw new InvalidOperationException("UserContext doesn't contain expected value"); } // Must make a new message instead of reusing the old message because of the way the SDK sends messages string trackingId = string.Empty; string batchId = string.Empty; string sequenceNumber = string.Empty; var messageProperties = new List <KeyValuePair <string, string> >(); foreach (KeyValuePair <string, string> prop in message.Properties) { switch (prop.Key) { case TestConstants.Message.TrackingIdPropertyName: trackingId = prop.Value ?? string.Empty; break; case TestConstants.Message.BatchIdPropertyName: batchId = prop.Value ?? string.Empty; break; case TestConstants.Message.SequenceNumberPropertyName: sequenceNumber = prop.Value ?? string.Empty; break; } messageProperties.Add(new KeyValuePair <string, string>(prop.Key, prop.Value)); } if (string.IsNullOrWhiteSpace(trackingId) || string.IsNullOrWhiteSpace(batchId) || string.IsNullOrWhiteSpace(sequenceNumber)) { Logger.LogWarning($"Received message missing info: trackingid={trackingId}, batchId={batchId}, sequenceNumber={sequenceNumber}"); return(MessageResponse.Completed); } // Report receiving message successfully to Test Result Coordinator var testResultReportingClient = new TestResultReportingClient { BaseUrl = testResultCoordinatorUrl.AbsoluteUri }; var testResultReceived = new MessageTestResult(Settings.Current.ModuleId + ".receive", DateTime.UtcNow) { TrackingId = trackingId, BatchId = batchId, SequenceNumber = sequenceNumber }; await ModuleUtil.ReportTestResultAsync(testResultReportingClient, Logger, testResultReceived); Logger.LogInformation($"Successfully received message: trackingid={trackingId}, batchId={batchId}, sequenceNumber={sequenceNumber}"); byte[] messageBytes = message.GetBytes(); var messageCopy = new Message(messageBytes); messageProperties.ForEach(kvp => messageCopy.Properties.Add(kvp)); await moduleClient.SendEventAsync(Settings.Current.OutputName, messageCopy); // Report sending message successfully to Test Result Coordinator var testResultSent = new MessageTestResult(Settings.Current.ModuleId + ".send", DateTime.UtcNow) { TrackingId = trackingId, BatchId = batchId, SequenceNumber = sequenceNumber }; await ModuleUtil.ReportTestResultAsync(testResultReportingClient, Logger, testResultSent); Logger.LogInformation($"Successfully sent message: trackingid={trackingId}, batchId={batchId}, sequenceNumber={sequenceNumber}"); } catch (Exception ex) { Logger.LogError(ex, $"Error in {nameof(ProcessAndSendMessageAsync)} method"); } return(MessageResponse.Completed); }
public static async Task <int> MainAsync() { Logger.LogInformation($"Starting DirectMethodSender with the following settings:\r\n{Settings.Current}"); (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(TimeSpan.FromSeconds(5), Logger); DirectMethodSenderBase directMethodClient = null; ModuleClient reportClient = null; Option <Uri> analyzerUrl = Settings.Current.AnalyzerUrl; Option <Uri> testReportCoordinatorUrl = Settings.Current.TestResultCoordinatorUrl; try { Guid batchId = Guid.NewGuid(); Logger.LogInformation($"Batch Id={batchId}"); directMethodClient = await CreateClientAsync(Settings.Current.InvocationSource); reportClient = await ModuleUtil.CreateModuleClientAsync( Settings.Current.TransportType, ModuleUtil.DefaultTimeoutErrorDetectionStrategy, ModuleUtil.DefaultTransientRetryStrategy, Logger); Logger.LogInformation($"Load gen delay start for {Settings.Current.TestStartDelay}."); await Task.Delay(Settings.Current.TestStartDelay, cts.Token); DateTime testStartAt = DateTime.UtcNow; while (!cts.Token.IsCancellationRequested && IsTestTimeUp(testStartAt)) { (HttpStatusCode result, long dmCounter) = await directMethodClient.InvokeDirectMethodAsync(cts); // TODO: Create an abstract class to handle the reporting client generation if (testReportCoordinatorUrl.HasValue) { await testReportCoordinatorUrl.ForEachAsync( async (Uri uri) => { var testResult = new DirectMethodTestResult(Settings.Current.ModuleId + ".send", DateTime.UtcNow) { TrackingId = Settings.Current.TrackingId.Expect(() => new ArgumentException("TrackingId is empty")), BatchId = batchId.ToString(), SequenceNumber = dmCounter.ToString(), Result = result.ToString() }; var testResultReportingClient = new TestResultReportingClient { BaseUrl = uri.AbsoluteUri }; await ModuleUtil.ReportTestResultAsync(testResultReportingClient, Logger, testResult); }); } else { await analyzerUrl.ForEachAsync( async (Uri uri) => { var testResult = new LegacyDirectMethodTestResult(Settings.Current.TargetModuleId, DateTime.UtcNow) { Result = result.ToString() }; var testResultReportingClient = new TestResultReportingClient { BaseUrl = uri.AbsoluteUri }; await ModuleUtil.ReportTestResultAsync(testResultReportingClient, Logger, testResult); }, async() => { await reportClient.SendEventAsync("AnyOutput", new Message(Encoding.UTF8.GetBytes("Direct Method call succeeded."))); }); } await Task.Delay(Settings.Current.DirectMethodDelay, cts.Token); } await cts.Token.WhenCanceled(); } catch (Exception e) { Logger.LogError(e, "Error occurred during direct method sender test setup"); } finally { // Implicit CloseAsync() directMethodClient?.Dispose(); reportClient?.Dispose(); } completed.Set(); handler.ForEach(h => GC.KeepAlive(h)); Logger.LogInformation("DirectMethodSender Main() finished."); return(0); }