From 5cd15f46426315d1455d270102bbdafd23e3fe35 Mon Sep 17 00:00:00 2001 From: Dominion Date: Tue, 30 May 2023 08:04:11 -0400 Subject: [PATCH 01/11] Check DreamDaemon output logs work in Live test --- tests/DMAPI/test_setup.dm | 1 + .../Live/Instance/WatchdogTest.cs | 35 ++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/DMAPI/test_setup.dm b/tests/DMAPI/test_setup.dm index 514921d668..18b35fbe9a 100644 --- a/tests/DMAPI/test_setup.dm +++ b/tests/DMAPI/test_setup.dm @@ -1,4 +1,5 @@ /world/New() + log << "Starting test..." text2file("SUCCESS", "test_success.txt") world.RunTest() diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index e8fc623199..976358f699 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -31,7 +31,6 @@ using Tgstation.Server.Host.Controllers; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.System; -using Tgstation.Server.Tests.Live; namespace Tgstation.Server.Tests.Live.Instance { @@ -61,7 +60,8 @@ namespace Tgstation.Server.Tests.Live.Instance { StartupTimeout = 15, HeartbeatSeconds = 0, - Port = TestLiveServer.DDPort + Port = TestLiveServer.DDPort, + LogOutput = false, }, cancellationToken), ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemonRequest { @@ -330,11 +330,12 @@ namespace Tgstation.Server.Tests.Live.Instance daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); Assert.AreEqual(WatchdogStatus.Offline, daemonStatus.Status.Value); - await CheckDMApiFail(daemonStatus.ActiveCompileJob, cancellationToken); + await CheckDMApiFail(daemonStatus.ActiveCompileJob, cancellationToken, false); daemonStatus = await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { - AdditionalParameters = string.Empty + AdditionalParameters = string.Empty, + LogOutput = true, }, cancellationToken); Assert.AreEqual(string.Empty, daemonStatus.AdditionalParameters); } @@ -985,7 +986,7 @@ namespace Tgstation.Server.Tests.Live.Instance while (timeout > 0); } - async Task CheckDMApiFail(CompileJobResponse compileJob, CancellationToken cancellationToken) + async Task CheckDMApiFail(CompileJobResponse compileJob, CancellationToken cancellationToken, bool checkLogs = true) { var gameDir = Path.Combine(instanceClient.Metadata.Path, "Game", compileJob.DirectoryName.Value.ToString(), Path.GetDirectoryName(compileJob.DmeName)); var failFile = Path.Combine(gameDir, "test_fail_reason.txt"); @@ -993,11 +994,29 @@ namespace Tgstation.Server.Tests.Live.Instance { var successFile = Path.Combine(gameDir, "test_success.txt"); Assert.IsTrue(File.Exists(successFile)); - return; + } + else + { + var text = await File.ReadAllTextAsync(failFile, cancellationToken); + Assert.Fail(text); } - var text = await File.ReadAllTextAsync(failFile, cancellationToken); - Assert.Fail(text); + if (!checkLogs) + return; + + var daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); + if (daemonStatus.Status != WatchdogStatus.Offline) + return; + + var outerLogsDir = Path.Combine(instanceClient.Metadata.Path, "Diagnostics", "DreamDaemonLogs"); + var logsDir = new DirectoryInfo(outerLogsDir).GetDirectories().OrderByDescending(x => x.CreationTime).FirstOrDefault(); + Assert.IsNotNull(logsDir); + + var logfile = logsDir.GetFiles().OrderByDescending(x => x.CreationTime).FirstOrDefault(); + Assert.IsNotNull(logfile); + + var logtext = await File.ReadAllTextAsync(logfile.FullName, cancellationToken); + Assert.IsFalse(String.IsNullOrWhiteSpace(logtext)); } } } From a01941c393ef16f3a757e81eb52325216ca77ab6 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 31 May 2023 18:38:01 -0400 Subject: [PATCH 02/11] Attempted fix for Linux output logging --- .../Components/Session/SessionControllerFactory.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs index 0662b381d2..92ce020d28 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs @@ -507,9 +507,7 @@ namespace Tgstation.Server.Host.Components.Session VisibilityWord(launchParameters.Visibility.Value), !byondLock.SupportsCli ? $" -logself -log {logFilePath}" - : !platformIdentifier.IsWindows // Just use stdout on if CLI is supported - ? " -logself" - : String.Empty, // Windows doesn't output anything to dd.exe if -logself is set? + : String.Empty, // DD doesn't output anything if -logself is set??? launchParameters.StartProfiler.Value ? " -profile" : String.Empty, From 2e59bb60dfef85dc37d4f7f88eae62dc3266b890 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 31 May 2023 19:14:57 -0400 Subject: [PATCH 03/11] Version bump to 5.12.4 --- build/Version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Version.props b/build/Version.props index 4aed1e7d1c..3b58bdbe15 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,7 +3,7 @@ - 5.12.3 + 5.12.4 4.6.0 9.10.2 10.4.1 From a1b50660650756296b2ad489771404e255c344ef Mon Sep 17 00:00:00 2001 From: Dominion Date: Thu, 1 Jun 2023 18:00:20 -0400 Subject: [PATCH 04/11] Workaround for GitHub's flakiness --- .../Core/ServerUpdater.cs | 147 ++++++++++-------- 1 file changed, 83 insertions(+), 64 deletions(-) diff --git a/src/Tgstation.Server.Host/Core/ServerUpdater.cs b/src/Tgstation.Server.Host/Core/ServerUpdater.cs index adcb902fd7..86623be571 100644 --- a/src/Tgstation.Server.Host/Core/ServerUpdater.cs +++ b/src/Tgstation.Server.Host/Core/ServerUpdater.cs @@ -87,78 +87,18 @@ namespace Tgstation.Server.Host.Core } /// - public async Task BeginUpdate(ISwarmService swarmService, Version newVersion, CancellationToken cancellationToken) + public async Task BeginUpdate(ISwarmService swarmService, Version version, CancellationToken cancellationToken) { if (swarmService == null) throw new ArgumentNullException(nameof(swarmService)); - if (newVersion == null) - throw new ArgumentNullException(nameof(newVersion)); + if (version == null) + throw new ArgumentNullException(nameof(version)); if (!swarmService.ExpectedNumberOfNodesConnected) return ServerUpdateResult.SwarmIntegrityCheckFailed; - logger.LogDebug("Looking for GitHub releases version {version}...", newVersion); - var gitHubClient = gitHubClientFactory.CreateClient(); - var releases = await gitHubClient - .Repository - .Release - .GetAll(updatesConfiguration.GitHubRepositoryId) - .WithToken(cancellationToken); - - logger.LogTrace("Received {releaseCount} total releases from GitHub", releases.Count); - - var filteredReleases = releases - .Where(x => x.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture)) - .ToList(); - - logger.LogTrace( - "Filtered to {releaseCount} releases matching the configured tag prefix of \"{tagPrefix}\"", - filteredReleases.Count, - updatesConfiguration.GitTagPrefix); - - foreach (var release in filteredReleases) - if (Version.TryParse( - release.TagName.Replace( - updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), - out var version)) - { - if (version == newVersion) - { - var asset = release.Assets.Where(x => x.Name.Equals(updatesConfiguration.UpdatePackageAssetName, StringComparison.Ordinal)).FirstOrDefault(); - if (asset == default) - continue; - - serverUpdateOperation = new ServerUpdateOperation - { - TargetVersion = version, - UpdateZipUrl = new Uri(asset.BrowserDownloadUrl), - SwarmService = swarmService, - }; - - try - { - if (!serverControl.TryStartUpdate(this, version)) - return ServerUpdateResult.UpdateInProgress; - } - finally - { - serverUpdateOperation = null; - } - - return ServerUpdateResult.Started; - } - } - else - logger.LogDebug("Unparsable release tag: {releaseTag}", release.TagName); - - if (updatesConfiguration.DumpReleasesOnNotFound) - logger.LogInformation( - "Found releases:{newline}\t{releases}", - Environment.NewLine, - String.Join($"{Environment.NewLine}\t", releases.Select(x => x.TagName).OrderBy(x => x))); - - return ServerUpdateResult.ReleaseMissing; + return await BeginUpdateImpl(swarmService, version, false, cancellationToken); } /// @@ -272,5 +212,84 @@ namespace Tgstation.Server.Host.Core return false; } + + /// + /// Start the process of downloading and applying an update to a new server version. Doesn't perform argument checking. + /// + /// The to use to coordinate the update. + /// The TGS to update to. + /// If this is a recursive call. + /// The for the operation. + /// A resulting in the . + async Task BeginUpdateImpl(ISwarmService swarmService, Version newVersion, bool recursed, CancellationToken cancellationToken) + { + logger.LogDebug("Looking for GitHub releases version {version}...", newVersion); + var gitHubClient = gitHubClientFactory.CreateClient(); + var releases = await gitHubClient + .Repository + .Release + .GetAll(updatesConfiguration.GitHubRepositoryId) + .WithToken(cancellationToken); + + logger.LogTrace("Received {releaseCount} total releases from GitHub", releases.Count); + + var filteredReleases = releases + .Where(x => x.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture)) + .ToList(); + + logger.LogTrace( + "Filtered to {releaseCount} releases matching the configured tag prefix of \"{tagPrefix}\"", + filteredReleases.Count, + updatesConfiguration.GitTagPrefix); + + foreach (var release in filteredReleases) + if (Version.TryParse( + release.TagName.Replace( + updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), + out var version)) + { + if (version == newVersion) + { + var asset = release.Assets.Where(x => x.Name.Equals(updatesConfiguration.UpdatePackageAssetName, StringComparison.Ordinal)).FirstOrDefault(); + if (asset == default) + continue; + + serverUpdateOperation = new ServerUpdateOperation + { + TargetVersion = version, + UpdateZipUrl = new Uri(asset.BrowserDownloadUrl), + SwarmService = swarmService, + }; + + try + { + if (!serverControl.TryStartUpdate(this, version)) + return ServerUpdateResult.UpdateInProgress; + } + finally + { + serverUpdateOperation = null; + } + + return ServerUpdateResult.Started; + } + } + else + logger.LogDebug("Unparsable release tag: {releaseTag}", release.TagName); + + if (updatesConfiguration.DumpReleasesOnNotFound) + logger.LogInformation( + "Found releases:{newline}\t{releases}", + Environment.NewLine, + String.Join($"{Environment.NewLine}\t", releases.Select(x => x.TagName).OrderBy(x => x))); + + if (!recursed) + { + logger.LogWarning("We didn't find the requested release, but GitHub has been known to just not give full results when querying all releases. We'll try one more time."); + return await BeginUpdateImpl(swarmService, newVersion, true, cancellationToken); + } + + return ServerUpdateResult.ReleaseMissing; + } } } From 5832b08c298a979803fb1e203425c9eeae2eb17e Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 2 Jun 2023 10:29:07 -0400 Subject: [PATCH 05/11] Fix config for Session:LowPriorityDeploymentProcesses being ignored --- src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index 4d1a37fe96..8470210425 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -814,8 +814,6 @@ namespace Tgstation.Server.Host.Components.Deployment using (var provider = new TemporaryDmbProvider(ioManager.ResolvePath(job.DirectoryName.ToString()), String.Concat(job.DmeName, DmbExtension), job)) await using (var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, cancellationToken)) { - controller.AdjustPriority(false); - var launchResult = await controller.LaunchResult; if (launchResult.StartupTime.HasValue) From a110dda407d333b2fce526dd4760e9b2747afe62 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 2 Jun 2023 10:36:37 -0400 Subject: [PATCH 06/11] Regression test for DreamDaemon priority issues --- .../Live/Instance/DeploymentTest.cs | 79 +++++++++++++++++-- .../Live/Instance/InstanceTest.cs | 6 +- .../Live/Instance/WatchdogTest.cs | 39 ++++++++- .../Live/LiveTestingServer.cs | 48 ++++++----- .../Live/TestLiveServer.cs | 6 +- 5 files changed, 145 insertions(+), 33 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs index b56d30efd1..81e5f5343c 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs @@ -20,13 +20,16 @@ namespace Tgstation.Server.Tests.Live.Instance readonly IDreamDaemonClient dreamDaemonClient; readonly IInstanceClient instanceClient; + readonly bool lowPriorityDeployments; + Task vpTest; - public DeploymentTest(IInstanceClient instanceClient, IJobsClient jobsClient) : base(jobsClient) + public DeploymentTest(IInstanceClient instanceClient, IJobsClient jobsClient, bool lowPriorityDeployments) : base(jobsClient) { this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient)); dreamMakerClient = instanceClient.DreamMaker; dreamDaemonClient = instanceClient.DreamDaemon; + this.lowPriorityDeployments = lowPriorityDeployments; } public async Task RunPreRepoClone(CancellationToken cancellationToken) @@ -34,7 +37,9 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.IsNull(vpTest); vpTest = TestVisibilityPermission(cancellationToken); var deployJob = await dreamMakerClient.Compile(cancellationToken); - deployJob = await WaitForJob(deployJob, 30, true, null, cancellationToken); + var deploymentJobWaitTask = WaitForJob(deployJob, 30, true, null, cancellationToken); + await CheckDreamDaemonPriority(deploymentJobWaitTask, cancellationToken); + deployJob = await deploymentJobWaitTask; Assert.IsTrue(deployJob.ErrorCode == ErrorCode.RepoCloning || deployJob.ErrorCode == ErrorCode.RepoMissing); var dmSettings = await dreamMakerClient.Read(cancellationToken); @@ -42,6 +47,65 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.AreEqual(null, dmSettings.ProjectName); } + async Task CheckDreamDaemonPriority(Task deploymentJobWaitTask, CancellationToken cancellationToken) + { + while (!deploymentJobWaitTask.IsCompleted) + { + var ddProcessName = new PlatformIdentifier().IsWindows && ByondTest.TestVersion >= new Version(515, 1598) + ? "dd" + : "DreamDaemon"; + + var allProcesses = System.Diagnostics.Process.GetProcessesByName(ddProcessName); + if (allProcesses.Length == 0) + continue; + + if (allProcesses.Length > 1) + Assert.Fail("Multiple DreamDaemon-like processes running!"); + + using var process = allProcesses[0]; + + int processId; + try + { + processId = process.Id; + } + catch + { + return; // vOv + } + + bool good = false; + while (!process.HasExited) + { + // we need to constantly reacquire the handle to invalidate caches + using var localProcess = System.Diagnostics.Process.GetProcessById(processId); + if (lowPriorityDeployments) + { + if (localProcess.PriorityClass == System.Diagnostics.ProcessPriorityClass.BelowNormal) + { + good = true; + break; + } + } + else + { + good = true; + if (localProcess.PriorityClass != System.Diagnostics.ProcessPriorityClass.Normal) + { + Assert.Fail("DreamDaemon's process priority changed when it shouldn't have!"); + } + + await Task.Delay(1, cancellationToken); + } + } + + if (!good) + Assert.Fail("Did not detect DreamDaemon lowering its process priority!"); + + break; + } + } + public async Task RunPostRepoClone(Task byondTask, CancellationToken cancellationToken) { Assert.IsNotNull(vpTest); @@ -80,6 +144,11 @@ namespace Tgstation.Server.Tests.Live.Instance } var deployJobTask = CompileAfterByondInstall(); + var deployJob = await deployJobTask; + var deploymentJobWaitTask = WaitForJob(deployJob, 40, true, ErrorCode.DreamMakerNeverValidated, cancellationToken); + + await CheckDreamDaemonPriority(deploymentJobWaitTask, cancellationToken); + await Task.WhenAll( ApiAssert.ThrowsException(() => dreamDaemonClient.Update(new DreamDaemonRequest { @@ -89,11 +158,7 @@ namespace Tgstation.Server.Tests.Live.Instance { ApiValidationPort = TestLiveServer.DDPort }, cancellationToken), ErrorCode.PortNotAvailable), - deployJobTask); - - var deployJob = await deployJobTask; - - await WaitForJob(deployJob, 40, true, ErrorCode.DreamMakerNeverValidated, cancellationToken); + deploymentJobWaitTask); const string FailProject = "tests/DMAPI/BuildFail/build_fail"; var updated = await dreamMakerClient.Update(new DreamMakerRequest diff --git a/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs index fd502926a9..0eecae4997 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs @@ -23,13 +23,13 @@ namespace Tgstation.Server.Tests.Live.Instance this.serverPort = serverPort; } - public async Task RunTests(CancellationToken cancellationToken) + public async Task RunTests(CancellationToken cancellationToken, bool highPrioDD, bool lowPrioDeployment) { var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, instanceClient.Metadata); var chatTest = new ChatTest(instanceClient.ChatBots, instanceManagerClient, instanceClient.Jobs, instanceClient.Metadata); var configTest = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata); var repoTest = new RepositoryTest(instanceClient.Repository, instanceClient.Jobs); - var dmTest = new DeploymentTest(instanceClient, instanceClient.Jobs); + var dmTest = new DeploymentTest(instanceClient, instanceClient.Jobs, lowPrioDeployment); var byondTask = byondTest.Run(cancellationToken, out var firstInstall); var chatTask = chatTest.RunPreWatchdog(cancellationToken); @@ -45,7 +45,7 @@ namespace Tgstation.Server.Tests.Live.Instance await dmTask; await byondTask; - await new WatchdogTest(instanceClient, instanceManager, serverPort).Run(cancellationToken); + await new WatchdogTest(instanceClient, instanceManager, serverPort, highPrioDD).Run(cancellationToken); } } } diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 976358f699..603a8744c6 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -39,15 +39,17 @@ namespace Tgstation.Server.Tests.Live.Instance readonly IInstanceClient instanceClient; readonly InstanceManager instanceManager; readonly ushort serverPort; + readonly bool highPrioDD; bool ranTimeoutTest = false; - public WatchdogTest(IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort) + public WatchdogTest(IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort, bool highPrioDD) : base(instanceClient.Jobs) { this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient)); this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); this.serverPort = serverPort; + this.highPrioDD = highPrioDD; } public async Task Run(CancellationToken cancellationToken) @@ -264,7 +266,9 @@ namespace Tgstation.Server.Tests.Live.Instance await WaitForJob(startJob, 40, false, null, cancellationToken); daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); + Assert.AreEqual(WatchdogStatus.Online, daemonStatus.Status.Value); + CheckDDPriority(cancellationToken); Assert.AreEqual(false, daemonStatus.SoftRestart); Assert.AreEqual(false, daemonStatus.SoftShutdown); Assert.AreEqual(string.Empty, daemonStatus.AdditionalParameters); @@ -321,7 +325,9 @@ namespace Tgstation.Server.Tests.Live.Instance await WaitForJob(startJob, 40, false, null, cancellationToken); daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); + Assert.AreEqual(WatchdogStatus.Online, daemonStatus.Status.Value); + CheckDDPriority(cancellationToken); Assert.AreEqual(false, daemonStatus.SoftRestart); Assert.AreEqual(false, daemonStatus.SoftShutdown); @@ -354,6 +360,8 @@ namespace Tgstation.Server.Tests.Live.Instance await WaitForJob(startJob, 40, false, null, cancellationToken); + CheckDDPriority(cancellationToken); + // lock on to DD and pause it so it can't heartbeat var ddProcs = System.Diagnostics.Process.GetProcessesByName("DreamDaemon").Where(x => !x.HasExited).ToList(); if (ddProcs.Count != 1) @@ -677,6 +685,28 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.AreEqual("Footer text", embedsResponse.Embed.Footer?.Text); } + void CheckDDPriority(CancellationToken cancellationToken) + { + var ddProcessName = new PlatformIdentifier().IsWindows && ByondTest.TestVersion >= new Version(515, 1598) + ? "dd" + : "DreamDaemon"; + + var allProcesses = System.Diagnostics.Process.GetProcessesByName(ddProcessName); + if (allProcesses.Length == 0) + Assert.Fail("Expected DreamDaemon to be running here"); + + if (allProcesses.Length > 1) + Assert.Fail("Multiple DreamDaemon-like processes running!"); + + using var process = allProcesses[0]; + + Assert.AreEqual( + highPrioDD + ? System.Diagnostics.ProcessPriorityClass.AboveNormal + : System.Diagnostics.ProcessPriorityClass.Normal, + process.PriorityClass); + } + async Task RunLongRunningTestThenUpdate(CancellationToken cancellationToken) { System.Console.WriteLine("TEST: WATCHDOG LONG RUNNING WITH UPDATE TEST"); @@ -698,6 +728,7 @@ namespace Tgstation.Server.Tests.Live.Instance daemonStatus = await DeployTestDme(DmeName, DreamDaemonSecurity.Safe, true, cancellationToken); Assert.AreEqual(WatchdogStatus.Online, daemonStatus.Status.Value); + CheckDDPriority(cancellationToken); Assert.AreEqual(initialCompileJob.Id, daemonStatus.ActiveCompileJob.Id); var newerCompileJob = daemonStatus.StagedCompileJob; @@ -738,7 +769,9 @@ namespace Tgstation.Server.Tests.Live.Instance daemonStatus = await DeployTestDme("LongRunning/long_running_test_copy", DreamDaemonSecurity.Safe, true, cancellationToken); + Assert.AreEqual(WatchdogStatus.Online, daemonStatus.Status.Value); + CheckDDPriority(cancellationToken); Assert.AreEqual(initialCompileJob.Id, daemonStatus.ActiveCompileJob.Id); var newerCompileJob = daemonStatus.StagedCompileJob; @@ -775,6 +808,8 @@ namespace Tgstation.Server.Tests.Live.Instance await WaitForJob(startJob, 70, false, null, cancellationToken); + CheckDDPriority(cancellationToken); + var byondInstallJobTask = instanceClient.Byond.SetActiveVersion( new ByondVersionRequest { @@ -829,7 +864,9 @@ namespace Tgstation.Server.Tests.Live.Instance await WaitForJob(startJob, 40, false, null, cancellationToken); var daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); + Assert.AreEqual(WatchdogStatus.Online, daemonStatus.Status.Value); + CheckDDPriority(cancellationToken); Assert.AreEqual(TestLiveServer.DDPort, daemonStatus.CurrentPort); // Try killing the DD process to ensure it gets set to the restoring state diff --git a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs index c6fcd0ea82..62c4ceaa3e 100644 --- a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs +++ b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs @@ -32,6 +32,9 @@ namespace Tgstation.Server.Tests.Live public bool DumpOpenApiSpecpath { get; } + public bool HighPriorityDreamDaemon { get; } + public bool LowPriorityDeployments { get; } + public bool RestartRequested => RealServer.RestartRequested; readonly List args; @@ -72,34 +75,41 @@ namespace Tgstation.Server.Tests.Live var gitHubAccessToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"); var dumpOpenAPISpecPathEnvVar = Environment.GetEnvironmentVariable("TGS_TEST_DUMP_API_SPEC"); - if (string.IsNullOrEmpty(DatabaseType)) + if (String.IsNullOrEmpty(DatabaseType)) Assert.Inconclusive("No database type configured in env var TGS_TEST_DATABASE_TYPE!"); - if (string.IsNullOrEmpty(connectionString)) + if (String.IsNullOrEmpty(connectionString)) Assert.Inconclusive("No connection string configured in env var TGS_TEST_CONNECTION_STRING!"); - if (string.IsNullOrEmpty(gitHubAccessToken)) + if (String.IsNullOrEmpty(gitHubAccessToken)) Console.WriteLine("WARNING: No GitHub access token configured, test may fail due to rate limits!"); - DumpOpenApiSpecpath = !string.IsNullOrEmpty(dumpOpenAPISpecPathEnvVar); + DumpOpenApiSpecpath = !String.IsNullOrEmpty(dumpOpenAPISpecPathEnvVar); + + // neither of these should really matter but it's better that we test them + // high prio DD might help with some topic flakiness actually + HighPriorityDreamDaemon = true; + LowPriorityDeployments = true; args = new List() { - string.Format(CultureInfo.InvariantCulture, "Database:DropDatabase={0}", true), // Replaced after first Run - string.Format(CultureInfo.InvariantCulture, "General:ConfigVersion={0}", GeneralConfiguration.CurrentConfigVersion), - string.Format(CultureInfo.InvariantCulture, "General:ApiPort={0}", port), - string.Format(CultureInfo.InvariantCulture, "Database:DatabaseType={0}", DatabaseType), - string.Format(CultureInfo.InvariantCulture, "Database:ConnectionString={0}", connectionString), - string.Format(CultureInfo.InvariantCulture, "General:SetupWizardMode={0}", SetupWizardMode.Never), - string.Format(CultureInfo.InvariantCulture, "General:MinimumPasswordLength={0}", 10), - string.Format(CultureInfo.InvariantCulture, "General:InstanceLimit={0}", 11), - string.Format(CultureInfo.InvariantCulture, "General:UserLimit={0}", 150), - string.Format(CultureInfo.InvariantCulture, "General:UserGroupLimit={0}", 47), - string.Format(CultureInfo.InvariantCulture, "General:HostApiDocumentation={0}", DumpOpenApiSpecpath), - string.Format(CultureInfo.InvariantCulture, "FileLogging:Directory={0}", Path.Combine(Directory, "Logs")), - string.Format(CultureInfo.InvariantCulture, "FileLogging:LogLevel={0}", "Trace"), - string.Format(CultureInfo.InvariantCulture, "General:ValidInstancePaths:0={0}", Directory), - "General:ByondTopicTimeout=3000" + String.Format(CultureInfo.InvariantCulture, "Database:DropDatabase={0}", true), // Replaced after first Run + String.Format(CultureInfo.InvariantCulture, "General:ConfigVersion={0}", GeneralConfiguration.CurrentConfigVersion), + String.Format(CultureInfo.InvariantCulture, "General:ApiPort={0}", port), + String.Format(CultureInfo.InvariantCulture, "Database:DatabaseType={0}", DatabaseType), + String.Format(CultureInfo.InvariantCulture, "Database:ConnectionString={0}", connectionString), + String.Format(CultureInfo.InvariantCulture, "General:SetupWizardMode={0}", SetupWizardMode.Never), + String.Format(CultureInfo.InvariantCulture, "General:MinimumPasswordLength={0}", 10), + String.Format(CultureInfo.InvariantCulture, "General:InstanceLimit={0}", 11), + String.Format(CultureInfo.InvariantCulture, "General:UserLimit={0}", 150), + String.Format(CultureInfo.InvariantCulture, "General:UserGroupLimit={0}", 47), + String.Format(CultureInfo.InvariantCulture, "General:HostApiDocumentation={0}", DumpOpenApiSpecpath), + String.Format(CultureInfo.InvariantCulture, "FileLogging:Directory={0}", Path.Combine(Directory, "Logs")), + String.Format(CultureInfo.InvariantCulture, "FileLogging:LogLevel={0}", "Trace"), + String.Format(CultureInfo.InvariantCulture, "General:ValidInstancePaths:0={0}", Directory), + "General:ByondTopicTimeout=3000", + $"Session:HighPriorityLiveDreamDaemon={HighPriorityDreamDaemon}", + $"Session:LowPriorityDeploymentProcesses={LowPriorityDeployments}", }; if (dumpOnMissingUpdate) diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index 1ade1e1c5a..10569c914c 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -864,7 +864,7 @@ namespace Tgstation.Server.Tests.Live Assert.IsTrue(Directory.Exists(instanceClient.Metadata.Path)); - var instanceTests = FailFast(new InstanceTest(instanceClient, adminClient.Instances, GetInstanceManager(), (ushort)server.Url.Port).RunTests(cancellationToken)); + var instanceTests = FailFast(new InstanceTest(instanceClient, adminClient.Instances, GetInstanceManager(), (ushort)server.Url.Port).RunTests(cancellationToken, server.HighPriorityDreamDaemon, server.LowPriorityDeployments)); await Task.WhenAll(rootTest, adminTest, instancesTest, instanceTests, usersTest); @@ -1020,7 +1020,7 @@ namespace Tgstation.Server.Tests.Live Assert.AreEqual(WatchdogStatus.Online, dd.Status.Value); var compileJob = await instanceClient.DreamMaker.Compile(cancellationToken); - var wdt = new WatchdogTest(instanceClient, GetInstanceManager(), (ushort)server.Url.Port); + var wdt = new WatchdogTest(instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon); await wdt.WaitForJob(compileJob, 30, false, null, cancellationToken); dd = await instanceClient.DreamDaemon.Read(cancellationToken); @@ -1067,7 +1067,7 @@ namespace Tgstation.Server.Tests.Live Assert.AreEqual(WatchdogStatus.Online, currentDD.Status); Assert.AreEqual(expectedStaged, currentDD.StagedCompileJob.Job.Id.Value); - var wdt = new WatchdogTest(instanceClient, GetInstanceManager(), (ushort)server.Url.Port); + var wdt = new WatchdogTest(instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon); currentDD = await wdt.TellWorldToReboot(cancellationToken); Assert.AreEqual(expectedStaged, currentDD.ActiveCompileJob.Job.Id.Value); Assert.IsNull(currentDD.StagedCompileJob); From 5e3949e33f7eaebbb8a007c5e1340ac81f3544ac Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 2 Jun 2023 10:56:36 -0400 Subject: [PATCH 07/11] Fix Linux actions builds --- tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs index 62c4ceaa3e..d4fd75b758 100644 --- a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs +++ b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs @@ -88,8 +88,10 @@ namespace Tgstation.Server.Tests.Live // neither of these should really matter but it's better that we test them // high prio DD might help with some topic flakiness actually - HighPriorityDreamDaemon = true; - LowPriorityDeployments = true; + // github doesn't allow nicing though + bool runningInGitHubActions = !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_RUN_ID")); + HighPriorityDreamDaemon = !runningInGitHubActions; + LowPriorityDeployments = !runningInGitHubActions; args = new List() { From d94ba26151c3c49800f8221d06fb540e81250eed Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 2 Jun 2023 11:46:55 -0400 Subject: [PATCH 08/11] Use Assert.AreEqual instead of if --- .../Live/Instance/DeploymentTest.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs index 81e5f5343c..19c5984e69 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs @@ -49,6 +49,8 @@ namespace Tgstation.Server.Tests.Live.Instance async Task CheckDreamDaemonPriority(Task deploymentJobWaitTask, CancellationToken cancellationToken) { + // this doesn't check dm's priority, but it really should + while (!deploymentJobWaitTask.IsCompleted) { var ddProcessName = new PlatformIdentifier().IsWindows && ByondTest.TestVersion >= new Version(515, 1598) @@ -90,11 +92,7 @@ namespace Tgstation.Server.Tests.Live.Instance else { good = true; - if (localProcess.PriorityClass != System.Diagnostics.ProcessPriorityClass.Normal) - { - Assert.Fail("DreamDaemon's process priority changed when it shouldn't have!"); - } - + Assert.AreEqual(System.Diagnostics.ProcessPriorityClass.Normal, localProcess.PriorityClass, "DreamDaemon's process priority changed when it shouldn't have!"); await Task.Delay(1, cancellationToken); } } From 50c657ea32bfe76655599dd90f45f4af408ad050 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 2 Jun 2023 12:39:38 -0400 Subject: [PATCH 09/11] Attempt to normalize the process priority class on Windows Live tests Also assume Windows allows nicing. --- .../Tgstation.Server.Tests/Live/LiveTestingServer.cs | 10 ++++++---- tests/Tgstation.Server.Tests/Live/TestLiveServer.cs | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs index d4fd75b758..6a66269efb 100644 --- a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs +++ b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs @@ -88,10 +88,12 @@ namespace Tgstation.Server.Tests.Live // neither of these should really matter but it's better that we test them // high prio DD might help with some topic flakiness actually - // github doesn't allow nicing though - bool runningInGitHubActions = !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_RUN_ID")); - HighPriorityDreamDaemon = !runningInGitHubActions; - LowPriorityDeployments = !runningInGitHubActions; + // github doesn't allow nicing on linux though + var runningInGitHubActions = !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_RUN_ID")); + var windows = new Host.System.PlatformIdentifier().IsWindows; + var nicingAllowed = windows || !runningInGitHubActions; + HighPriorityDreamDaemon = nicingAllowed; + LowPriorityDeployments = nicingAllowed; args = new List() { diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index 10569c914c..678e361897 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -730,6 +730,17 @@ namespace Tgstation.Server.Tests.Live [TestMethod] public async Task TestStandardTgsOperation() { + using(var currentProcess = System.Diagnostics.Process.GetCurrentProcess()) + { + var currentPriorityClass = currentProcess.PriorityClass; + if (currentPriorityClass != ProcessPriorityClass.Normal) + { + // attempt to adjust it + Console.WriteLine($"TEST PROCESS PRIORITY: Attempting to normalize process priority from {currentPriorityClass}..."); + currentProcess.PriorityClass = ProcessPriorityClass.Normal; + } + } + const int MaximumTestMinutes = 30; using var hardCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaximumTestMinutes)); var hardCancellationToken = hardCancellationTokenSource.Token; From 34225f8c50f5cde5713330b014f4f250826a92e0 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 2 Jun 2023 16:45:24 -0400 Subject: [PATCH 10/11] Reduce log spam --- .../Components/Session/SessionController.cs | 10 ++++++++-- .../Live/DummyChatProvider.cs | 19 +------------------ .../Live/Instance/WatchdogTest.cs | 3 +++ 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index e3da5a1ea7..164fb6e218 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -29,6 +29,11 @@ namespace Tgstation.Server.Host.Components.Session /// sealed class SessionController : Chunker, ISessionController, IBridgeHandler, IChannelSink { + /// + /// If calls to should be trace logged. + /// + internal static bool LogTopicRequests { get; set; } = true; + /// public DMApiParameters DMApiParameters => ReattachInformation; @@ -365,7 +370,7 @@ namespace Tgstation.Server.Host.Components.Session void LogCombinedResponse() { - if (combinedResponse != null) + if (LogTopicRequests && combinedResponse != null) Logger.LogTrace("Topic response: {topicString}", combinedResponse.ByondTopicResponse.StringData ?? "(NO STRING DATA)"); } @@ -768,7 +773,8 @@ namespace Tgstation.Server.Host.Components.Session parameters.AccessIdentifier = ReattachInformation.AccessIdentifier; var fullCommandString = GenerateQueryString(parameters, out var json); - Logger.LogTrace("Topic request: {json}", json); + if (LogTopicRequests) + Logger.LogTrace("Topic request: {json}", json); var fullCommandByteCount = Encoding.UTF8.GetByteCount(fullCommandString); var topicPriority = parameters.IsPriority; if (fullCommandByteCount <= DMApiConstants.MaximumTopicRequestLength) diff --git a/tests/Tgstation.Server.Tests/Live/DummyChatProvider.cs b/tests/Tgstation.Server.Tests/Live/DummyChatProvider.cs index 6eb24a0c0f..972e7acc75 100644 --- a/tests/Tgstation.Server.Tests/Live/DummyChatProvider.cs +++ b/tests/Tgstation.Server.Tests/Live/DummyChatProvider.cs @@ -86,8 +86,6 @@ namespace Tgstation.Server.Tests.Live this.commands = commands ?? throw new ArgumentNullException(nameof(commands)); this.random = random ?? throw new ArgumentNullException(nameof(random)); - logger.LogTrace("Base channel ID {baseChannelId}", channelIdAllocator); - knownChannels = new Dictionary(); randomMessageCts = new CancellationTokenSource(); randomMessageTask = RandomMessageLoop(this.randomMessageCts.Token); @@ -95,7 +93,6 @@ namespace Tgstation.Server.Tests.Live public override async ValueTask DisposeAsync() { - Logger.LogTrace("DisposeAsync Child"); this.randomMessageCts.Cancel(); this.randomMessageCts.Dispose(); await this.randomMessageTask; @@ -107,8 +104,6 @@ namespace Tgstation.Server.Tests.Live if (message == null) throw new ArgumentNullException(nameof(message)); - Logger.LogTrace("SendMessage"); - Assert.IsTrue(knownChannels.ContainsKey(channelId)); cancellationToken.ThrowIfCancellationRequested(); @@ -131,8 +126,6 @@ namespace Tgstation.Server.Tests.Live if (gitHubRepo == null) throw new ArgumentNullException(nameof(gitHubRepo)); - Logger.LogTrace("SendUpdateMessage"); - Assert.IsTrue(knownChannels.ContainsKey(channelId)); cancellationToken.ThrowIfCancellationRequested(); @@ -155,7 +148,6 @@ namespace Tgstation.Server.Tests.Live protected override Task Connect(CancellationToken cancellationToken) { - Logger.LogTrace("Connect"); cancellationToken.ThrowIfCancellationRequested(); // 30% chance to fail AFTER initial connection @@ -169,7 +161,6 @@ namespace Tgstation.Server.Tests.Live protected override Task DisconnectImpl(CancellationToken cancellationToken) { - Logger.LogTrace("DisconnectImpl"); cancellationToken.ThrowIfCancellationRequested(); connected = false; @@ -181,7 +172,6 @@ namespace Tgstation.Server.Tests.Live protected override Task>> MapChannelsImpl(IEnumerable channels, CancellationToken cancellationToken) { channels = channels.ToList(); - Logger.LogTrace("MapChannels: [{channels}]", String.Join(", ", channels.Select(channel => channel.IrcChannel ?? channel.DiscordChannelId?.ToString() ?? throw new InvalidOperationException("BAD CHANNEL")))); cancellationToken.ThrowIfCancellationRequested(); @@ -202,7 +192,6 @@ namespace Tgstation.Server.Tests.Live private ChannelRepresentation CreateChannel(ChatChannel channel) { - ulong channelId; if (channel.DiscordChannelId.HasValue) channelId = channel.DiscordChannelId.Value; @@ -235,7 +224,6 @@ namespace Tgstation.Server.Tests.Live async Task RandomMessageLoop(CancellationToken cancellationToken) { - Logger.LogTrace("RandomMessageLoop"); try { for (var i = 0UL; !cancellationToken.IsCancellationRequested; ++i) @@ -337,11 +325,7 @@ namespace Tgstation.Server.Tests.Live content = $"{content} {commands.ElementAt(random.Next(0, commands.Count)).Name}"; // 40% chance to attempt a custom chat command in long_running_test else - // equal chance for each - if (random.Next(0, 100) > 50) - content = $"{content} embeds_test"; - else - content = $"{content} response_overload_test"; + content = $"{content} embeds_test"; // NEVER send the response_overload_test, it causes so much havoc in CI and we test it manually EnqueueMessage(new Message { @@ -353,7 +337,6 @@ namespace Tgstation.Server.Tests.Live } catch (OperationCanceledException) { - Logger.LogTrace("RandomMessageLoop cancelled"); } } } diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 603a8744c6..12aa55e81a 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -26,6 +26,7 @@ using Tgstation.Server.Client.Components; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Components.Chat; using Tgstation.Server.Host.Components.Interop; +using Tgstation.Server.Host.Components.Session; using Tgstation.Server.Host.Components.Watchdog; using Tgstation.Server.Host.Controllers; using Tgstation.Server.Host.Extensions; @@ -103,9 +104,11 @@ namespace Tgstation.Server.Tests.Live.Instance var deleteJobTask = TestDeleteByondInstallErrorCasesAndQueing(cancellationToken); + SessionController.LogTopicRequests = false; await WhiteBoxChatCommandTest(cancellationToken); await SendChatOverloadCommand(cancellationToken); await ValidateTopicLimits(cancellationToken); + SessionController.LogTopicRequests = true; // This one fucks with the access_identifer, run it in isolation await WhiteBoxValidateBridgeRequestLimitAndTestChunking(cancellationToken); From e335f2b002f82d34f2ca8db82c2c2c60b4a000d4 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 2 Jun 2023 18:16:43 -0400 Subject: [PATCH 11/11] More restrictive priority checks --- tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs | 2 ++ tests/Tgstation.Server.Tests/Live/TestLiveServer.cs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs index 19c5984e69..e4171c29d5 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs @@ -88,6 +88,8 @@ namespace Tgstation.Server.Tests.Live.Instance good = true; break; } + else + Assert.AreEqual(System.Diagnostics.ProcessPriorityClass.Normal, localProcess.PriorityClass); } else { diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index 678e361897..5b10b5b6d6 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -741,6 +741,11 @@ namespace Tgstation.Server.Tests.Live } } + using (var currentProcess = System.Diagnostics.Process.GetCurrentProcess()) + { + Assert.AreEqual(ProcessPriorityClass.Normal, currentProcess.PriorityClass); + } + const int MaximumTestMinutes = 30; using var hardCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaximumTestMinutes)); var hardCancellationToken = hardCancellationTokenSource.Token;