public async Task <ServerSourceHolder> StartServerAsync(EndpointInfoSourceCallback sourceCallback       = null, IDumpService dumpService = null,
                                                                OperationTrackerService operationTrackerService = null)
        {
            DiagnosticPortHelper.Generate(DiagnosticPortConnectionMode.Listen, out _, out string transportName);
            _outputHelper.WriteLine("Starting server endpoint info source at '" + transportName + "'.");

            List <IEndpointInfoSourceCallbacks> callbacks = new();

            if (null != sourceCallback)
            {
                callbacks.Add(sourceCallback);
                if (null != dumpService)
                {
                    callbacks.Add(new OperationTrackerServiceEndpointInfoSourceCallback(operationTrackerService));
                }
            }

            IOptions <DiagnosticPortOptions> portOptions = Extensions.Options.Options.Create(
                new DiagnosticPortOptions()
            {
                ConnectionMode = DiagnosticPortConnectionMode.Listen,
                EndpointName   = transportName
            });

            ServerEndpointInfoSource source = new(portOptions, callbacks, operationTrackerService);

            await source.StartAsync(CancellationToken.None);

            return(new ServerSourceHolder(source, transportName));
        }
        public async Task CollectionRule_ConfigurationChangeTest(DiagnosticPortConnectionMode mode)
        {
            const string firstRuleName  = "FirstRule";
            const string secondRuleName = "SecondRule";

            DiagnosticPortHelper.Generate(
                mode,
                out DiagnosticPortConnectionMode appConnectionMode,
                out string diagnosticPortPath);

            await using MonitorCollectRunner toolRunner = new(_outputHelper);
            toolRunner.ConnectionMode        = mode;
            toolRunner.DiagnosticPortPath    = diagnosticPortPath;
            toolRunner.DisableAuthentication = true;

            // Create a rule with some settings
            RootOptions originalOptions = new();

            originalOptions.CreateCollectionRule(firstRuleName)
            .SetStartupTrigger();

            await toolRunner.WriteUserSettingsAsync(originalOptions);

            await toolRunner.StartAsync();

            AppRunner appRunner = new(_outputHelper, Assembly.GetExecutingAssembly());

            appRunner.ConnectionMode     = appConnectionMode;
            appRunner.DiagnosticPortPath = diagnosticPortPath;
            appRunner.ScenarioName       = TestAppScenarios.AsyncWait.Name;

            Task originalActionsCompletedTask = toolRunner.WaitForCollectionRuleActionsCompletedAsync(firstRuleName);

            await appRunner.ExecuteAsync(async() =>
            {
                // Validate that the first rule is observed and its actions are run.
                await originalActionsCompletedTask;

                // Set up new observers for the first and second rule.
                originalActionsCompletedTask = toolRunner.WaitForCollectionRuleActionsCompletedAsync(firstRuleName);
                Task newActionsCompletedTask = toolRunner.WaitForCollectionRuleActionsCompletedAsync(secondRuleName);

                // Change collection rule configuration to only contain the second rule.
                RootOptions newOptions = new();
                newOptions.CreateCollectionRule(secondRuleName)
                .SetStartupTrigger();

                await toolRunner.WriteUserSettingsAsync(newOptions);

                // Validate that only the second rule is observed.
                await newActionsCompletedTask;
                Assert.False(originalActionsCompletedTask.IsCompleted);

                await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue);
            });

            Assert.Equal(0, appRunner.ExitCode);
        }
        public async Task CollectionRule_StoppedOnExitTest(DiagnosticPortConnectionMode mode)
        {
            DiagnosticPortHelper.Generate(
                mode,
                out DiagnosticPortConnectionMode appConnectionMode,
                out string diagnosticPortPath);

            await using MonitorCollectRunner toolRunner = new(_outputHelper);
            toolRunner.ConnectionMode        = mode;
            toolRunner.DiagnosticPortPath    = diagnosticPortPath;
            toolRunner.DisableAuthentication = true;

            // Create a rule with some settings
            RootOptions originalOptions = new();

            originalOptions.CreateCollectionRule(DefaultRuleName)
            .SetEventCounterTrigger(options =>
            {
                options.ProviderName          = "System.Runtime";
                options.CounterName           = "cpu-usage";
                options.GreaterThan           = 1000; // Intentionally unobtainable
                options.SlidingWindowDuration = TimeSpan.FromSeconds(1);
            });

            await toolRunner.WriteUserSettingsAsync(originalOptions);

            await toolRunner.StartAsync();

            AppRunner appRunner = new(_outputHelper, Assembly.GetExecutingAssembly());

            appRunner.ConnectionMode     = appConnectionMode;
            appRunner.DiagnosticPortPath = diagnosticPortPath;
            appRunner.ScenarioName       = TestAppScenarios.AsyncWait.Name;

            Task ruleStartedTask  = toolRunner.WaitForCollectionRuleStartedAsync(DefaultRuleName);
            Task rulesStoppedTask = toolRunner.WaitForCollectionRulesStoppedAsync();

            await appRunner.ExecuteAsync(async() =>
            {
                await ruleStartedTask;

                await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue);
            });

            Assert.Equal(0, appRunner.ExitCode);

            // All of the rules for the process should have stopped. Note that dotnet-monitor has
            // not yet exited at this point in time; this is verification that the rules have stopped
            // for the target process before dotnet-monitor shuts down.
            await rulesStoppedTask;
        }
        public static async Task SingleTarget(
            ITestOutputHelper outputHelper,
            IHttpClientFactory httpClientFactory,
            DiagnosticPortConnectionMode mode,
            string scenarioName,
            Func <AppRunner, ApiClient, Task> appValidate,
            Func <ApiClient, int, Task> postAppValidate = null,
            Action <AppRunner> configureApp             = null,
            Action <MonitorCollectRunner> configureTool = null,
            bool disableHttpEgress = false)
        {
            DiagnosticPortHelper.Generate(
                mode,
                out DiagnosticPortConnectionMode appConnectionMode,
                out string diagnosticPortPath);

            await using MonitorCollectRunner toolRunner = new(outputHelper);
            toolRunner.ConnectionMode        = mode;
            toolRunner.DiagnosticPortPath    = diagnosticPortPath;
            toolRunner.DisableAuthentication = true;
            toolRunner.DisableHttpEgress     = disableHttpEgress;

            configureTool?.Invoke(toolRunner);

            await toolRunner.StartAsync();

            using HttpClient httpClient = await toolRunner.CreateHttpClientDefaultAddressAsync(httpClientFactory);

            ApiClient apiClient = new(outputHelper, httpClient);

            AppRunner appRunner = new(outputHelper, Assembly.GetExecutingAssembly());

            appRunner.ConnectionMode     = appConnectionMode;
            appRunner.DiagnosticPortPath = diagnosticPortPath;
            appRunner.ScenarioName       = scenarioName;

            configureApp?.Invoke(appRunner);

            await appRunner.ExecuteAsync(async() =>
            {
                await appValidate(appRunner, apiClient);
            });

            Assert.Equal(0, appRunner.ExitCode);

            if (null != postAppValidate)
            {
                await postAppValidate(apiClient, await appRunner.ProcessIdTask);
            }
        }
Exemple #5
0
        public async Task MultiProcessIdentificationTest(DiagnosticPortConnectionMode mode)
        {
            DiagnosticPortHelper.Generate(
                mode,
                out DiagnosticPortConnectionMode appConnectionMode,
                out string diagnosticPortPath);

            await using MonitorCollectRunner toolRunner = new(_outputHelper);
            toolRunner.ConnectionMode        = mode;
            toolRunner.DiagnosticPortPath    = diagnosticPortPath;
            toolRunner.DisableAuthentication = true;
            await toolRunner.StartAsync();

            using HttpClient httpClient = await toolRunner.CreateHttpClientDefaultAddressAsync(_httpClientFactory);

            ApiClient apiClient = new(_outputHelper, httpClient);

            const int appCount = 3;

            AppRunner[] appRunners = new AppRunner[appCount];

            for (int i = 0; i < appCount; i++)
            {
                AppRunner runner = new(_outputHelper, Assembly.GetExecutingAssembly(), appId : i + 1);
                runner.ConnectionMode     = appConnectionMode;
                runner.DiagnosticPortPath = diagnosticPortPath;
                runner.ScenarioName       = TestAppScenarios.AsyncWait.Name;
                runner.Environment[ExpectedEnvVarName] = Guid.NewGuid().ToString("D");
                appRunners[i] = runner;
            }

            IList <ProcessIdentifier> identifiers;
            await appRunners.ExecuteAsync(async() =>
            {
                // Scope to only the processes that were launched by the test
                IList <int> unmatchedPids = new List <int>();
                foreach (AppRunner runner in appRunners)
                {
                    unmatchedPids.Add(await runner.ProcessIdTask);
                }

                // Query for process identifiers
                identifiers = (await apiClient.GetProcessesWithRetryAsync(
                                   _outputHelper,
                                   unmatchedPids.ToArray())).ToList();
                Assert.NotNull(identifiers);

                _outputHelper.WriteLine("Start enumerating discovered processes.");
                foreach (ProcessIdentifier identifier in identifiers.ToList())
                {
                    _outputHelper.WriteLine($"- PID:  {identifier.Pid}");
                    _outputHelper.WriteLine($"  UID:  {identifier.Uid}");
                    _outputHelper.WriteLine($"  Name: {identifier.Name}");

                    unmatchedPids.Remove(identifier.Pid);
                }
                _outputHelper.WriteLine("End enumerating discovered processes");

                Assert.Empty(unmatchedPids);
                Assert.Equal(appRunners.Length, identifiers.Count);

                foreach (ProcessIdentifier processIdentifier in identifiers)
                {
                    int pid     = processIdentifier.Pid;
                    Guid uid    = processIdentifier.Uid;
                    string name = processIdentifier.Name;
#if NET5_0_OR_GREATER
                    // CHECK 1: Get response for processes using PID, UID, and Name and check for consistency

                    List <ProcessInfo> processInfoQueriesCheck1 = new List <ProcessInfo>();

                    processInfoQueriesCheck1.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid));
                    // Only check with uid if it is non-empty; this can happen in connect mode if the ProcessInfo command fails
                    // to respond within the short period of time that is used to get the additional process information.
                    if (uid == Guid.Empty)
                    {
                        _outputHelper.WriteLine("Skipped uid-only check because it is empty GUID.");
                    }
                    else
                    {
                        processInfoQueriesCheck1.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, uid: uid));
                    }

                    VerifyProcessInfoEquality(processInfoQueriesCheck1);
#endif
                    // CHECK 2: Get response for requests using PID | PID and UID | PID, UID, and Name and check for consistency

                    List <ProcessInfo> processInfoQueriesCheck2 = new List <ProcessInfo>();

                    processInfoQueriesCheck2.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid));
                    processInfoQueriesCheck2.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid, uid: uid));
                    processInfoQueriesCheck2.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid, uid: uid, name: name));

                    VerifyProcessInfoEquality(processInfoQueriesCheck2);

                    // CHECK 3: Get response for processes using PID and an unassociated (randomly generated) UID and ensure the proper exception is thrown

                    await VerifyInvalidRequestException(apiClient, pid, Guid.NewGuid(), null);
                }

                // CHECK 4: Get response for processes using invalid PID, UID, or Name and ensure the proper exception is thrown

                await VerifyInvalidRequestException(apiClient, -1, null, null);
                await VerifyInvalidRequestException(apiClient, null, Guid.NewGuid(), null);
                await VerifyInvalidRequestException(apiClient, null, null, "");

                // Verify each app instance is reported and shut them down.
                foreach (AppRunner runner in appRunners)
                {
                    Assert.True(runner.Environment.TryGetValue(ExpectedEnvVarName, out string expectedEnvVarValue));

                    await VerifyProcessAsync(apiClient, identifiers, await runner.ProcessIdTask, expectedEnvVarValue);

                    await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue);
                }
            });

            for (int i = 0; i < appCount; i++)
            {
                Assert.True(0 == appRunners[i].ExitCode, $"App {i} exit code is non-zero.");
            }

            // Query for process identifiers
            identifiers = (await apiClient.GetProcessesAsync()).ToList();
            Assert.NotNull(identifiers);

            // Verify none of the apps are reported
            List <int> runnerProcessIds = new(appCount);

            for (int i = 0; i < appCount; i++)
            {
                runnerProcessIds.Add(await appRunners[i].ProcessIdTask);
            }

            foreach (ProcessIdentifier identifier in identifiers)
            {
                Assert.DoesNotContain(identifier.Pid, runnerProcessIds);
            }
        }