public async Task EgressCancelTest() { await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, appValidate : async(appRunner, apiClient) => { int processId = await appRunner.ProcessIdTask; OperationResponse response = await apiClient.EgressTraceAsync(processId, durationSeconds: -1, FileProviderName); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); OperationStatusResponse operationResult = await apiClient.GetOperationStatus(response.OperationUri); Assert.Equal(HttpStatusCode.OK, operationResult.StatusCode); Assert.True(operationResult.OperationStatus.Status == OperationState.Running); HttpStatusCode deleteStatus = await apiClient.CancelEgressOperation(response.OperationUri); Assert.Equal(HttpStatusCode.OK, deleteStatus); operationResult = await apiClient.GetOperationStatus(response.OperationUri); Assert.Equal(HttpStatusCode.OK, operationResult.StatusCode); Assert.Equal(OperationState.Cancelled, operationResult.OperationStatus.Status); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureTool : (toolRunner) => { toolRunner.WriteKeyPerValueConfiguration(new RootOptions().AddFileSystemEgress(FileProviderName, _tempDirectory.FullName)); }); }
public async Task DisableHttpEgressTest() { await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, appValidate : async(appRunner, appClient) => { int processId = await appRunner.ProcessIdTask; ProcessInfo processInfo = await appClient.GetProcessAsync(processId); Assert.NotNull(processInfo); // Dump Error Check ValidationProblemDetailsException validationProblemDetailsExceptionDumps = await Assert.ThrowsAsync <ValidationProblemDetailsException>( () => appClient.CaptureDumpAsync(processId, DumpType.Mini)); Assert.Equal(HttpStatusCode.BadRequest, validationProblemDetailsExceptionDumps.StatusCode); Assert.Equal(StatusCodes.Status400BadRequest, validationProblemDetailsExceptionDumps.Details.Status); Assert.Equal(DisabledHTTPEgressErrorMessage, validationProblemDetailsExceptionDumps.Message); // Logs Error Check ValidationProblemDetailsException validationProblemDetailsExceptionLogs = await Assert.ThrowsAsync <ValidationProblemDetailsException>( () => appClient.CaptureLogsAsync(processId, CommonTestTimeouts.LogsDuration, LogLevel.None, LogFormat.NewlineDelimitedJson)); Assert.Equal(HttpStatusCode.BadRequest, validationProblemDetailsExceptionLogs.StatusCode); Assert.Equal(StatusCodes.Status400BadRequest, validationProblemDetailsExceptionLogs.Details.Status); Assert.Equal(DisabledHTTPEgressErrorMessage, validationProblemDetailsExceptionLogs.Message); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, disableHttpEgress : true); }
public async Task ConcurrencyLimitTest() { await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, appValidate : async(appRunner, apiClient) => { int processId = await appRunner.ProcessIdTask; OperationResponse response1 = await EgressTraceWithDelay(apiClient, processId); OperationResponse response2 = await EgressTraceWithDelay(apiClient, processId); OperationResponse response3 = await EgressTraceWithDelay(apiClient, processId); ValidationProblemDetailsException ex = await Assert.ThrowsAsync <ValidationProblemDetailsException>(() => EgressTraceWithDelay(apiClient, processId)); Assert.Equal(HttpStatusCode.TooManyRequests, ex.StatusCode); Assert.Equal((int)HttpStatusCode.TooManyRequests, ex.Details.Status.GetValueOrDefault()); await CancelEgressOperation(apiClient, response1); await CancelEgressOperation(apiClient, response2); OperationResponse response4 = await EgressTraceWithDelay(apiClient, processId, delay: false); await CancelEgressOperation(apiClient, response3); await CancelEgressOperation(apiClient, response4); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureTool : (toolRunner) => { toolRunner.WriteKeyPerValueConfiguration(new RootOptions().AddFileSystemEgress(FileProviderName, _tempDirectory.FullName)); }); }
public async Task CollectionRule_StartupTriggerTest(DiagnosticPortConnectionMode mode) { using TemporaryDirectory tempDirectory = new(_outputHelper); string ExpectedFilePath = Path.Combine(tempDirectory.FullName, "file.txt"); string ExpectedFileContent = Guid.NewGuid().ToString("N"); Task ruleCompletedTask = null; await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.AsyncWait.Name, appValidate : async(runner, client) => { await ruleCompletedTask; Assert.True(File.Exists(ExpectedFilePath)); Assert.Equal(ExpectedFileContent, File.ReadAllText(ExpectedFilePath)); await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureTool : runner => { runner.ConfigurationFromEnvironment.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddExecuteActionAppAction("TextFileOutput", ExpectedFilePath, ExpectedFileContent); ruleCompletedTask = runner.WaitForCollectionRuleCompleteAsync(DefaultRuleName); }); }
public Task InfoEndpointValidationTest(DiagnosticPortConnectionMode mode) { return(ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.AsyncWait.Name, appValidate: async(runner, client) => { // GET /info DotnetMonitorInfo info = await client.GetInfoAsync(); Assert.NotNull(info.Version); // Not sure of how to get Dotnet Monitor version from within tests... Assert.True(Version.TryParse(info.RuntimeVersion, out Version runtimeVersion), "Unable to parse version from RuntimeVersion property."); Version currentAspNetVersion = TargetFrameworkMoniker.Net60.GetAspNetCoreFrameworkVersion(); Assert.Equal(currentAspNetVersion.Major, runtimeVersion.Major); Assert.Equal(currentAspNetVersion.Minor, runtimeVersion.Minor); Assert.Equal(currentAspNetVersion.Revision, runtimeVersion.Revision); Assert.Equal(mode, info.DiagnosticPortMode); if (mode == DiagnosticPortConnectionMode.Connect) { Assert.Null(info.DiagnosticPortName); } else if (mode == DiagnosticPortConnectionMode.Listen) { Assert.Equal(runner.DiagnosticPortPath, info.DiagnosticPortName); } await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); })); }
public async Task EgressListTest() { await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, appValidate : async(appRunner, apiClient) => { int processId = await appRunner.ProcessIdTask; OperationResponse response1 = await EgressTraceWithDelay(apiClient, processId); OperationResponse response2 = await EgressTraceWithDelay(apiClient, processId, delay: false); await CancelEgressOperation(apiClient, response2); List <OperationSummary> result = await apiClient.GetOperations(); Assert.Equal(2, result.Count); OperationStatusResponse status1 = await apiClient.GetOperationStatus(response1.OperationUri); OperationSummary summary1 = result.First(os => os.OperationId == status1.OperationStatus.OperationId); ValidateOperation(status1.OperationStatus, summary1); OperationStatusResponse status2 = await apiClient.GetOperationStatus(response2.OperationUri); OperationSummary summary2 = result.First(os => os.OperationId == status2.OperationStatus.OperationId); ValidateOperation(status2.OperationStatus, summary2); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureTool : (toolRunner) => { toolRunner.WriteKeyPerValueConfiguration(new RootOptions().AddFileSystemEgress(FileProviderName, _tempDirectory.FullName)); }); }
public Task TestCustomMetrics() { return(ScenarioRunner.SingleTarget(_outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, async(appRunner, apiClient) => { var counterNames = new[] { "cpu-usage", "working-set" }; using ResponseStreamHolder holder = await apiClient.CaptureMetricsAsync(await appRunner.ProcessIdTask, durationSeconds: 10, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, Providers = new[] { new EventMetricsProvider { ProviderName = EventPipe.MonitoringSourceConfiguration.SystemRuntimeEventSourceName, CounterNames = counterNames, } } }); var metrics = GetAllMetrics(holder); await ValidateMetrics(new [] { EventPipe.MonitoringSourceConfiguration.SystemRuntimeEventSourceName }, counterNames, metrics, strict: true); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); })); }
public Task TestDefaultMetrics() { return(ScenarioRunner.SingleTarget(_outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, async(appRunner, apiClient) => { using ResponseStreamHolder holder = await apiClient.CaptureMetricsAsync(await appRunner.ProcessIdTask, durationSeconds: 10); var metrics = GetAllMetrics(holder); await ValidateMetrics(new [] { EventPipe.MonitoringSourceConfiguration.SystemRuntimeEventSourceName }, new [] { "cpu-usage", "working-set", "gc-heap-size", "threadpool-thread-count", "threadpool-queue-length" }, metrics, strict: false); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); })); }
public async Task CollectionRule_CommandLineFilterNoMatchTest(DiagnosticPortConnectionMode mode) { Task filteredTask = null; await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.AsyncWait.Name, appValidate : async(runner, client) => { await filteredTask; await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureTool : runner => { // Note that the process name filter is specified as "SpinWait" whereas the // actual command line of the target process will contain "AsyncWait". runner.ConfigurationFromEnvironment.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddProcessNameFilter(TestAppScenarios.SpinWait.Name); filteredTask = runner.WaitForCollectionRuleUnmatchedFiltersAsync(DefaultRuleName); }); }
public Task LogsDefaultLevelNoneNotSupportedViaBodyTest(DiagnosticPortConnectionMode mode, LogFormat logFormat) { return(ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.Logger.Name, appValidate: async(runner, client) => { ValidationProblemDetailsException exception = await Assert.ThrowsAsync <ValidationProblemDetailsException>( async() => { using ResponseStreamHolder _ = await client.CaptureLogsAsync( await runner.ProcessIdTask, CommonTestTimeouts.LogsDuration, new LogsConfiguration() { LogLevel = LogLevel.None }, logFormat); }); Assert.Equal(HttpStatusCode.BadRequest, exception.StatusCode); Assert.Equal(StatusCodes.Status400BadRequest, exception.Details.Status); // Allow test app to gracefully exit by continuing the scenario. await runner.SendCommandAsync(TestAppScenarios.Logger.Commands.StartLogging); })); }
public Task DumpTest(DiagnosticPortConnectionMode mode, DumpType type) { #if !NET6_0_OR_GREATER // Capturing non-full dumps via diagnostic command works inconsistently // on Alpine for .NET 5 and lower (the dump command will return successfully, but) // the dump file will not exist). Only test other dump types on .NET 6+ if (DistroInformation.IsAlpineLinux && type != DumpType.Full) { _outputHelper.WriteLine("Skipped on Alpine for .NET 5 and lower."); return(Task.CompletedTask); } #endif return(ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.AsyncWait.Name, appValidate: async(runner, client) => { int processId = await runner.ProcessIdTask; using ResponseStreamHolder holder = await client.CaptureDumpAsync(processId, type); Assert.NotNull(holder); // The dump operation may still be in progress but the process should still be discoverable. // If this check fails, then the dump operation is causing dotnet-monitor to not be able // to observe the process any more. ProcessInfo processInfo = await client.GetProcessAsync(processId); Assert.NotNull(processInfo); await DumpTestUtilities.ValidateDump(runner.Environment.ContainsKey(DumpTestUtilities.EnableElfDumpOnMacOS), holder.Stream); await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureApp: runner => { // MachO not supported on .NET 5, only ELF: https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/xplat-minidump-generation.md#os-x if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && DotNetHost.RuntimeVersion.Major == 5) { runner.Environment.Add(DumpTestUtilities.EnableElfDumpOnMacOS, "1"); } }, configureTool: runner => { string dumpTempFolder = Path.Combine(runner.TempPath, "Dumps"); // The dump temp folder should not exist in order to test that capturing dumps into the folder // will work since dotnet-monitor should ensure the folder is created before issuing the dump command. Assert.False(Directory.Exists(dumpTempFolder), "The dump temp folder should not exist."); runner.ConfigurationFromEnvironment.SetDumpTempFolder(dumpTempFolder); })); }
public async Task EgressNotExistTest() { await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, appValidate : async(appRunner, apiClient) => { int processId = await appRunner.ProcessIdTask; ValidationProblemDetailsException validationException = await Assert.ThrowsAsync <ValidationProblemDetailsException>( () => apiClient.EgressTraceAsync(processId, durationSeconds: 5, FileProviderName)); Assert.Equal(HttpStatusCode.BadRequest, validationException.StatusCode); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }); }
public async Task SharedConcurrencyLimitTest() { await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, appValidate : async(appRunner, apiClient) => { int processId = await appRunner.ProcessIdTask; OperationResponse response1 = await EgressTraceWithDelay(apiClient, processId); OperationResponse response3 = await EgressTraceWithDelay(apiClient, processId); using HttpResponseMessage traceDirect1 = await TraceWithDelay(apiClient, processId); Assert.Equal(HttpStatusCode.OK, traceDirect1.StatusCode); ValidationProblemDetailsException ex = await Assert.ThrowsAsync <ValidationProblemDetailsException>( () => EgressTraceWithDelay(apiClient, processId, delay: false)); Assert.Equal(HttpStatusCode.TooManyRequests, ex.StatusCode); using HttpResponseMessage traceDirect = await TraceWithDelay(apiClient, processId, delay: false); Assert.Equal(HttpStatusCode.TooManyRequests, traceDirect.StatusCode); //Validate that the failure from a direct call (handled by middleware) //matches the failure produces by egress operations (handled by the Mvc ActionResult stack) using HttpResponseMessage egressDirect = await EgressDirect(apiClient, processId); Assert.Equal(HttpStatusCode.TooManyRequests, egressDirect.StatusCode); Assert.Equal(await egressDirect.Content.ReadAsStringAsync(), await traceDirect.Content.ReadAsStringAsync()); await CancelEgressOperation(apiClient, response1); OperationResponse response4 = await EgressTraceWithDelay(apiClient, processId, delay: false); await CancelEgressOperation(apiClient, response3); await CancelEgressOperation(apiClient, response4); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureTool : (toolRunner) => { toolRunner.WriteKeyPerValueConfiguration(new RootOptions().AddFileSystemEgress(FileProviderName, _tempDirectory.FullName)); }); }
public async Task CollectionRule_ActionLimitTest(DiagnosticPortConnectionMode mode) { using TemporaryDirectory tempDirectory = new(_outputHelper); string ExpectedFilePath = Path.Combine(tempDirectory.FullName, "file.txt"); string ExpectedFileContent = Guid.NewGuid().ToString("N"); Task ruleCompletedTask = null; await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.SpinWait.Name, appValidate : async(runner, client) => { await runner.SendCommandAsync(TestAppScenarios.SpinWait.Commands.StartSpin); await ruleCompletedTask; await runner.SendCommandAsync(TestAppScenarios.SpinWait.Commands.StopSpin); Assert.True(File.Exists(ExpectedFilePath)); Assert.Equal(ExpectedFileContent, File.ReadAllText(ExpectedFilePath)); }, configureTool : runner => { runner.ConfigurationFromEnvironment.CreateCollectionRule(DefaultRuleName) .SetEventCounterTrigger(options => { // cpu usage greater that 5% for 2 seconds options.ProviderName = "System.Runtime"; options.CounterName = "cpu-usage"; options.GreaterThan = 5; options.SlidingWindowDuration = TimeSpan.FromSeconds(2); }) .AddExecuteActionAppAction("TextFileOutput", ExpectedFilePath, ExpectedFileContent) .SetActionLimits(count: 1); ruleCompletedTask = runner.WaitForCollectionRuleCompleteAsync(DefaultRuleName); }); }
public Task SingleProcessIdentificationTest(DiagnosticPortConnectionMode mode) { string expectedEnvVarValue = Guid.NewGuid().ToString("D"); return(ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.AsyncWait.Name, appValidate: async(runner, client) => { int processId = await runner.ProcessIdTask; // GET /processes and filter to just the single process IEnumerable <ProcessIdentifier> identifiers = await client.GetProcessesWithRetryAsync( _outputHelper, new[] { processId }); Assert.NotNull(identifiers); Assert.Single(identifiers); await VerifyProcessAsync(client, identifiers, processId, expectedEnvVarValue); await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, postAppValidate: async(client, processId) => { // GET /processes and filter to just the single process IEnumerable <ProcessIdentifier> identifiers = await client.GetProcessesWithRetryAsync( _outputHelper, new[] { processId }); // Verify app is no longer reported Assert.NotNull(identifiers); Assert.Empty(identifiers); }, configureApp: runner => { runner.Environment[ExpectedEnvVarName] = expectedEnvVarValue; })); }
private Task ValidateLogsAsync( DiagnosticPortConnectionMode mode, LogsConfiguration configuration, Func <ChannelReader <LogEntry>, Task> callback, LogFormat logFormat) { return(ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.Logger.Name, appValidate: async(runner, client) => await ValidateResponseStream( runner, client.CaptureLogsAsync( await runner.ProcessIdTask, CommonTestTimeouts.LogsDuration, configuration, logFormat), callback, logFormat))); }
public async Task CollectionRule_ProcessNameFilterNoMatchTest(DiagnosticPortConnectionMode mode) { Task filteredTask = null; await ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.AsyncWait.Name, appValidate : async(runner, client) => { await filteredTask; await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureTool : runner => { runner.ConfigurationFromEnvironment.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddProcessNameFilter("UmatchedName"); filteredTask = runner.WaitForCollectionRuleUnmatchedFiltersAsync(DefaultRuleName); }); }