From c1f439321a458ddbfba5ef1bd445aea2ae324d77 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Fri, 7 Jul 2023 16:43:43 -0400 Subject: [PATCH] Test oldest compatible BYOND version in Live test --- tests/DMAPI/LongRunning/Test.dm | 14 +- .../CachingFileDownloader.cs | 5 +- .../Live/Instance/ByondTest.cs | 48 ++++-- .../Live/Instance/ConfigurationTest.cs | 2 +- .../Live/Instance/DeploymentTest.cs | 34 ++-- .../Live/Instance/InstanceTest.cs | 157 +++++++++++++++++- .../Live/Instance/TestBridgeHandler.cs | 6 +- .../Live/Instance/WatchdogTest.cs | 137 ++++++++------- .../Live/InstanceManagerTest.cs | 6 +- .../Live/LiveTestingServer.cs | 65 ++++---- .../Live/TestLiveServer.cs | 140 +++++++++++----- 11 files changed, 431 insertions(+), 183 deletions(-) diff --git a/tests/DMAPI/LongRunning/Test.dm b/tests/DMAPI/LongRunning/Test.dm index d40b87550e..281cbd3998 100644 --- a/tests/DMAPI/LongRunning/Test.dm +++ b/tests/DMAPI/LongRunning/Test.dm @@ -51,7 +51,7 @@ startup_complete = TRUE if(run_bridge_test) - CheckBridgeLimits() + CheckBridgeLimits(run_bridge_test) /world/Topic(T, Addr, Master, Keys) if(findtext(T, "tgs_integration_test_tactics3") == 0) @@ -76,9 +76,9 @@ var/run_bridge_test var/tactics2 = data["tgs_integration_test_tactics2"] if(tactics2) if(startup_complete) - CheckBridgeLimits() + CheckBridgeLimits(tactics2) else - run_bridge_test = TRUE + run_bridge_test = tactics2 return "ack2" // Topic limit tests @@ -259,17 +259,17 @@ var/suppress_bridge_spam = FALSE var/payload = jointext(builder, "") return payload -/proc/CheckBridgeLimits() +/proc/CheckBridgeLimits(id) set waitfor = FALSE - CheckBridgeLimitsImpl() + CheckBridgeLimitsImpl(id) -/proc/CheckBridgeLimitsImpl() +/proc/CheckBridgeLimitsImpl(id) sleep(30) // Evil custom bridge command hacking here var/datum/tgs_api/v5/api = TGS_READ_GLOBAL(tgs) var/old_ai = api.access_identifier - api.access_identifier = "tgs_integration_test" + api.access_identifier = id lastTgsError = null diff --git a/tests/Tgstation.Server.Tests/CachingFileDownloader.cs b/tests/Tgstation.Server.Tests/CachingFileDownloader.cs index 618e114b31..3d5eb95024 100644 --- a/tests/Tgstation.Server.Tests/CachingFileDownloader.cs +++ b/tests/Tgstation.Server.Tests/CachingFileDownloader.cs @@ -41,7 +41,10 @@ namespace Tgstation.Server.Tests }); var logger = loggerFactory.CreateLogger("CachingFileDownloader"); - await InitializeByondVersion(logger, ByondTest.TestVersion, new PlatformIdentifier().IsWindows, cancellationToken); + var cfd = new CachingFileDownloader(loggerFactory.CreateLogger()); + var edgeVersion = await ByondTest.GetEdgeVersion(cfd, cancellationToken); + + await InitializeByondVersion(logger, edgeVersion, new PlatformIdentifier().IsWindows, cancellationToken); // predownload the target github release update asset var gitHubToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"); diff --git a/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs index a190994e72..6a84ef8876 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -23,13 +25,15 @@ namespace Tgstation.Server.Tests.Live.Instance { sealed class ByondTest : JobsRequiredTest { - public static readonly Version TestVersion = new(515, 1605); - readonly IByondClient byondClient; readonly IFileDownloader fileDownloader; readonly Api.Models.Instance metadata; + static Version edgeVersion; + + Version testVersion; + public ByondTest(IByondClient byondClient, IJobsClient jobsClient, IFileDownloader fileDownloader, Api.Models.Instance metadata) : base(jobsClient) { @@ -44,8 +48,22 @@ namespace Tgstation.Server.Tests.Live.Instance return RunContinued(firstInstall, cancellationToken); } + public static async Task GetEdgeVersion(IFileDownloader fileDownloader, CancellationToken cancellationToken) + { + if (edgeVersion != null) + return edgeVersion; + + await using var provider = fileDownloader.DownloadFile(new Uri("https://www.byond.com/download/version.txt"), null); + var stream = await provider.GetResult(cancellationToken); + using var reader = new StreamReader(stream, Encoding.UTF8, false, -1, true); + var text = await reader.ReadToEndAsync(); + var splits = text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + return edgeVersion = Version.Parse(splits.Last()); + } + async Task RunPartOne(CancellationToken cancellationToken) { + testVersion = await GetEdgeVersion(fileDownloader, cancellationToken); await TestNoVersion(cancellationToken); await TestInstallStable(cancellationToken); } @@ -62,7 +80,7 @@ namespace Tgstation.Server.Tests.Live.Instance { var deleteThisOneBecauseItWasntPartOfTheOriginalTest = await byondClient.DeleteVersion(new ByondVersionDeleteRequest { - Version = new(TestVersion.Major, TestVersion.Minor, 2) + Version = new(testVersion.Major, testVersion.Minor, 2) }, cancellationToken); await WaitForJob(deleteThisOneBecauseItWasntPartOfTheOriginalTest, 30, false, null, cancellationToken); @@ -76,14 +94,14 @@ namespace Tgstation.Server.Tests.Live.Instance var uninstallResponseTask = byondClient.DeleteVersion( new ByondVersionDeleteRequest { - Version = TestVersion + Version = testVersion }, cancellationToken); var badBecauseActiveResponseTask = ApiAssert.ThrowsException(() => byondClient.DeleteVersion( new ByondVersionDeleteRequest { - Version = new(TestVersion.Major, TestVersion.Minor, 1) + Version = new(testVersion.Major, testVersion.Minor, 1) }, cancellationToken), ErrorCode.ByondCannotDeleteActiveVersion); @@ -98,13 +116,13 @@ namespace Tgstation.Server.Tests.Live.Instance await nonExistentUninstallResponseTask; await uninstallTask; - var byondDir = Path.Combine(metadata.Path, "Byond", TestVersion.ToString()); + var byondDir = Path.Combine(metadata.Path, "Byond", testVersion.ToString()); Assert.IsFalse(Directory.Exists(byondDir)); var newVersions = await byondClient.InstalledVersions(null, cancellationToken); Assert.IsNotNull(newVersions); Assert.AreEqual(1, newVersions.Count); - Assert.AreEqual(new Version(TestVersion.Major, TestVersion.Minor, 1), newVersions[0].Version); + Assert.AreEqual(new Version(testVersion.Major, testVersion.Minor, 1), newVersions[0].Version); } async Task TestInstallFakeVersion(CancellationToken cancellationToken) @@ -122,7 +140,7 @@ namespace Tgstation.Server.Tests.Live.Instance { var newModel = new ByondVersionRequest { - Version = TestVersion + Version = testVersion }; var test = await byondClient.SetActiveVersion(newModel, null, cancellationToken); Assert.IsNotNull(test.InstallJob); @@ -179,12 +197,12 @@ namespace Tgstation.Server.Tests.Live.Instance using var windowsByondInstaller = byondInstaller as WindowsByondInstaller; // get the bytes for stable - using var stableBytesMs = await byondInstaller.DownloadVersion(TestVersion, cancellationToken); + using var stableBytesMs = await byondInstaller.DownloadVersion(testVersion, cancellationToken); var test = await byondClient.SetActiveVersion( new ByondVersionRequest { - Version = TestVersion, + Version = testVersion, UploadCustomZip = true }, stableBytesMs, @@ -198,7 +216,7 @@ namespace Tgstation.Server.Tests.Live.Instance var test2 = await byondClient.SetActiveVersion( new ByondVersionRequest { - Version = TestVersion, + Version = testVersion, UploadCustomZip = true }, stableBytesMs, @@ -208,22 +226,22 @@ namespace Tgstation.Server.Tests.Live.Instance await WaitForJob(test2.InstallJob, 30, false, null, cancellationToken); var newSettings = await byondClient.ActiveVersion(cancellationToken); - Assert.AreEqual(new Version(TestVersion.Major, TestVersion.Minor, 2), newSettings.Version); + Assert.AreEqual(new Version(testVersion.Major, testVersion.Minor, 2), newSettings.Version); // test a few switches var installResponse = await byondClient.SetActiveVersion(new ByondVersionRequest { - Version = TestVersion + Version = testVersion }, null, cancellationToken); Assert.IsNull(installResponse.InstallJob); await ApiAssert.ThrowsException(() => byondClient.SetActiveVersion(new ByondVersionRequest { - Version = new Version(TestVersion.Major, TestVersion.Minor, 3) + Version = new Version(testVersion.Major, testVersion.Minor, 3) }, null, cancellationToken), ErrorCode.ByondNonExistentCustomVersion); installResponse = await byondClient.SetActiveVersion(new ByondVersionRequest { - Version = new Version(TestVersion.Major, TestVersion.Minor, 1) + Version = new Version(testVersion.Major, testVersion.Minor, 1) }, null, cancellationToken); Assert.IsNull(installResponse.InstallJob); } diff --git a/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs index 8a7c7013c1..c921b2c9cd 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs @@ -97,7 +97,7 @@ namespace Tgstation.Server.Tests.Live.Instance await configurationClient.CreateDirectory(staticDir, cancellationToken); } - Task SetupDMApiTests(CancellationToken cancellationToken) + public Task SetupDMApiTests(CancellationToken cancellationToken) { // just use an I/O manager here var ioManager = new DefaultIOManager(); diff --git a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs index b770494875..45b6d5dfc8 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs @@ -20,15 +20,24 @@ namespace Tgstation.Server.Tests.Live.Instance readonly IDreamDaemonClient dreamDaemonClient; readonly IInstanceClient instanceClient; + readonly ushort dmPort; + readonly ushort ddPort; readonly bool lowPriorityDeployments; Task vpTest; - public DeploymentTest(IInstanceClient instanceClient, IJobsClient jobsClient, bool lowPriorityDeployments) : base(jobsClient) + public DeploymentTest( + IInstanceClient instanceClient, + IJobsClient jobsClient, + ushort dmPort, + ushort ddPort, + bool lowPriorityDeployments) : base(jobsClient) { this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient)); dreamMakerClient = instanceClient.DreamMaker; dreamDaemonClient = instanceClient.DreamDaemon; + this.dmPort = dmPort; + this.ddPort = ddPort; this.lowPriorityDeployments = lowPriorityDeployments; } @@ -50,14 +59,9 @@ 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) - ? "dd" - : "DreamDaemon"; - - var allProcesses = TestLiveServer.GetAllDDProcesses(); + var allProcesses = TestLiveServer.GetDDProcessesOnPort(dmPort); if (allProcesses.Count == 0) continue; @@ -115,27 +119,27 @@ namespace Tgstation.Server.Tests.Live.Instance var updatedDM = await dreamMakerClient.Update(new DreamMakerRequest { ProjectName = "tests/DMAPI/ApiFree/api_free", - ApiValidationPort = TestLiveServer.DMPort + ApiValidationPort = dmPort }, cancellationToken); - Assert.AreEqual(TestLiveServer.DMPort, updatedDM.ApiValidationPort); + Assert.AreEqual(dmPort, updatedDM.ApiValidationPort); Assert.AreEqual("tests/DMAPI/ApiFree/api_free", updatedDM.ProjectName); } else { var updatedDM = await dreamMakerClient.Update(new DreamMakerRequest { - ApiValidationPort = TestLiveServer.DMPort + ApiValidationPort = dmPort }, cancellationToken); - Assert.AreEqual(TestLiveServer.DMPort, updatedDM.ApiValidationPort); + Assert.AreEqual(dmPort, updatedDM.ApiValidationPort); } var updatedDD = await dreamDaemonClient.Update(new DreamDaemonRequest { StartupTimeout = 15, - Port = TestLiveServer.DDPort + Port = ddPort }, cancellationToken); Assert.AreEqual(15U, updatedDD.StartupTimeout); - Assert.AreEqual(TestLiveServer.DDPort, updatedDD.Port); + Assert.AreEqual(ddPort, updatedDD.Port); async Task CompileAfterByondInstall() { @@ -152,11 +156,11 @@ namespace Tgstation.Server.Tests.Live.Instance await Task.WhenAll( ApiAssert.ThrowsException(() => dreamDaemonClient.Update(new DreamDaemonRequest { - Port = TestLiveServer.DMPort + Port = dmPort, }, cancellationToken), ErrorCode.PortNotAvailable), ApiAssert.ThrowsException(() => dreamMakerClient.Update(new DreamMakerRequest { - ApiValidationPort = TestLiveServer.DDPort + ApiValidationPort = ddPort }, cancellationToken), ErrorCode.PortNotAvailable), deploymentJobWaitTask); diff --git a/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs index b30533f082..dc877c57e9 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs @@ -1,38 +1,55 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +using Moq; + +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Components.Byond; +using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.System; namespace Tgstation.Server.Tests.Live.Instance { sealed class InstanceTest { - readonly IInstanceClient instanceClient; readonly IInstanceManagerClient instanceManagerClient; readonly IFileDownloader fileDownloader; readonly InstanceManager instanceManager; readonly ushort serverPort; - public InstanceTest(IInstanceClient instanceClient, IInstanceManagerClient instanceManagerClient, IFileDownloader fileDownloader, InstanceManager instanceManager, ushort serverPort) + public InstanceTest(IInstanceManagerClient instanceManagerClient, IFileDownloader fileDownloader, InstanceManager instanceManager, ushort serverPort) { - this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient)); this.instanceManagerClient = instanceManagerClient ?? throw new ArgumentNullException(nameof(instanceManagerClient)); this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader)); this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); this.serverPort = serverPort; } - public async Task RunTests(bool highPrioDD, bool lowPrioDeployment, CancellationToken cancellationToken) + public async Task RunTests( + IInstanceClient instanceClient, + ushort dmPort, + ushort ddPort, + bool highPrioDD, + bool lowPrioDeployment, + CancellationToken cancellationToken) { var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, fileDownloader, 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, lowPrioDeployment); + var dmTest = new DeploymentTest(instanceClient, instanceClient.Jobs, dmPort, ddPort, lowPrioDeployment); var byondTask = byondTest.Run(cancellationToken, out var firstInstall); var chatTask = chatTest.RunPreWatchdog(cancellationToken); @@ -48,7 +65,135 @@ namespace Tgstation.Server.Tests.Live.Instance await dmTask; await byondTask; - await new WatchdogTest(instanceClient, instanceManager, serverPort, highPrioDD).Run(cancellationToken); + await new WatchdogTest( + await ByondTest.GetEdgeVersion(fileDownloader, cancellationToken), instanceClient, instanceManager, serverPort, highPrioDD, ddPort).Run(cancellationToken); + } + + public async Task RunCompatTests( + IInstanceClient instanceClient, + ushort dmPort, + ushort ddPort, + bool highPrioDD, + CancellationToken cancellationToken) + { + var compatVersion = new Version(510, 1346); + const string Origin = "https://github.com/Cyberboss/common_core"; + var cloneRequest = instanceClient.Repository.Clone(new RepositoryCreateRequest + { + Origin = new Uri(Origin), + }, cancellationToken); + + + var dmUpdateRequest = instanceClient.DreamMaker.Update(new DreamMakerRequest + { + ApiValidationPort = dmPort, + }, cancellationToken); + + // need at least one chat bot to satisfy DMAPI test, + // use discord as it allows multi-botting on on token unlike IRC + var connectionString = Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_TOKEN"); + if (String.IsNullOrWhiteSpace(connectionString)) + // needs to just be valid + connectionString = new DiscordConnectionStringBuilder + { + BasedMeme = true, + BotToken = "some_token", + DeploymentBranding = true, + DMOutputDisplay = DiscordDMOutputDisplayType.Always, + }.ToString(); + else + // standardize + connectionString = new DiscordConnectionStringBuilder(connectionString).ToString(); + + var channelIdStr = Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_CHANNEL"); + if (String.IsNullOrWhiteSpace(channelIdStr)) + channelIdStr = "487268744419344384"; + + var chatRequest = instanceClient.ChatBots.Create(new ChatBotCreateRequest + { + ChannelLimit = 10, + Channels = new List + { + new ChatChannel + { + ChannelData = channelIdStr, + Tag = "some_tag", + IsAdminChannel = true, + IsSystemChannel = true, + IsUpdatesChannel = true, + IsWatchdogChannel = true, + }, + }, + ConnectionString = connectionString, + Enabled = true, + Name = "compat_test_bot", + Provider = ChatProvider.Discord, + ReconnectionInterval = 1, + }, cancellationToken); + + var jrt = new JobsRequiredTest(instanceClient.Jobs); + + IByondInstaller byondInstaller = new PlatformIdentifier().IsWindows + ? new WindowsByondInstaller( + Mock.Of(), + Mock.Of(), + fileDownloader, + Options.Create(new GeneralConfiguration()), + Mock.Of>()) + : new PosixByondInstaller( + Mock.Of(), + Mock.Of(), + fileDownloader, + Mock.Of>()); + + using var windowsByondInstaller = byondInstaller as WindowsByondInstaller; + + // get the bytes for stable + ByondInstallResponse installJob2; + using (var stableBytesMs = await byondInstaller.DownloadVersion(compatVersion, cancellationToken)) + { + installJob2 = await instanceClient.Byond.SetActiveVersion(new ByondVersionRequest + { + UploadCustomZip = true, + Version = compatVersion, + }, stableBytesMs, cancellationToken); + } + + await chatRequest; + + var jobs = await instanceClient.Jobs.List(null, cancellationToken); + var theJobWeWant = jobs.First(x => x.Description.Contains("Reconnect chat bot")); + + await Task.WhenAll( + jrt.WaitForJob(installJob2.InstallJob, 30, false, null, cancellationToken), + jrt.WaitForJob(cloneRequest.Result.ActiveJob, 60, false, null, cancellationToken), + jrt.WaitForJob(theJobWeWant, 30, false, null, cancellationToken), + dmUpdateRequest, + cloneRequest); + + var configSetupTask = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata).SetupDMApiTests(cancellationToken); + + if (TestingUtils.RunningInGitHubActions + || String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN")) + || Environment.MachineName.Equals("CYBERSTATIONXVI", StringComparison.OrdinalIgnoreCase)) + await instanceClient.Repository.Update(new RepositoryUpdateRequest + { + CreateGitHubDeployments = true, + PostTestMergeComment = true, + PushTestMergeCommits = true, + AccessUser = "Cyberboss", + AccessToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"), + }, cancellationToken); + + await configSetupTask; + + await new WatchdogTest(compatVersion, instanceClient, instanceManager, serverPort, highPrioDD, ddPort).Run(cancellationToken); + + await instanceManagerClient.Update(new InstanceUpdateRequest + { + Id = instanceClient.Metadata.Id, + Online = false, + }, cancellationToken); } } } diff --git a/tests/Tgstation.Server.Tests/Live/Instance/TestBridgeHandler.cs b/tests/Tgstation.Server.Tests/Live/Instance/TestBridgeHandler.cs index dacd8bdd98..bb23bbdc63 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/TestBridgeHandler.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/TestBridgeHandler.cs @@ -24,21 +24,23 @@ namespace Tgstation.Server.Tests.Live.Instance public DMApiParameters DMApiParameters => new DMApiParametersImpl { - AccessIdentifier = "tgs_integration_test" + AccessIdentifier = accessIdentifier }; long lastBridgeRequestSize = 0; readonly TaskCompletionSource bridgeTestsTcs; readonly ushort serverPort; + readonly string accessIdentifier; bool chunksProcessed = false; - public TestBridgeHandler(TaskCompletionSource tcs, ILogger logger, ushort serverPort) + public TestBridgeHandler(TaskCompletionSource tcs, ILogger logger, string accessIdentifier, ushort serverPort) : base(logger) { bridgeTestsTcs = tcs; this.serverPort = serverPort; + this.accessIdentifier = accessIdentifier; } public async Task ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 829be29024..3ee9ffa856 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -17,7 +17,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api; using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Api.Models.Response; @@ -38,25 +37,66 @@ namespace Tgstation.Server.Tests.Live.Instance { sealed class WatchdogTest : JobsRequiredTest { + static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder => + { + builder.AddConsole(); + builder.SetMinimumLevel(LogLevel.Trace); + }); + + public static readonly TopicClient StaticTopicClient = new(new SocketParameters + { + SendTimeout = TimeSpan.FromSeconds(30), + ReceiveTimeout = TimeSpan.FromSeconds(30), + ConnectTimeout = TimeSpan.FromSeconds(30), + DisconnectTimeout = TimeSpan.FromSeconds(30) + }, loggerFactory.CreateLogger($"WatchdogTest.TopicClient.Static")); + readonly IInstanceClient instanceClient; readonly InstanceManager instanceManager; readonly ushort serverPort; + readonly ushort ddPort; readonly bool highPrioDD; + readonly TopicClient topicClient; + readonly Version testVersion; bool ranTimeoutTest = false; - public WatchdogTest(IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort, bool highPrioDD) + public WatchdogTest(Version testVersion, IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort, bool highPrioDD, ushort ddPort) : 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; + this.ddPort = ddPort; + this.testVersion = testVersion ?? throw new ArgumentNullException(nameof(testVersion)); + + this.topicClient = new(new SocketParameters + { + SendTimeout = TimeSpan.FromSeconds(30), + ReceiveTimeout = TimeSpan.FromSeconds(30), + ConnectTimeout = TimeSpan.FromSeconds(30), + DisconnectTimeout = TimeSpan.FromSeconds(30) + }, loggerFactory.CreateLogger($"WatchdogTest.TopicClient.{instanceClient.Metadata.Name}")); } public async Task Run(CancellationToken cancellationToken) { - System.Console.WriteLine("TEST: START WATCHDOG TESTS"); + System.Console.WriteLine($"TEST: START WATCHDOG TESTS {instanceClient.Metadata.Name}"); + + async Task CheckByondVersions() + { + var listTask = instanceClient.Byond.InstalledVersions(null, cancellationToken); + + var list = await listTask; + + Assert.AreEqual(1, list.Count); + var byondVersion = list[0]; + + Assert.AreEqual(1, byondVersion.Version.Build); + Assert.AreEqual(testVersion.Major, byondVersion.Version.Major); + Assert.AreEqual(testVersion.Minor, byondVersion.Version.Minor); + } await Task.WhenAll( // Increase startup timeout, disable heartbeats, enable map threads because we've tested without for years @@ -64,10 +104,11 @@ namespace Tgstation.Server.Tests.Live.Instance { StartupTimeout = 15, HealthCheckSeconds = 0, - Port = TestLiveServer.DDPort, + Port = ddPort, MapThreads = 2, LogOutput = false, }, cancellationToken), + CheckByondVersions(), ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemonRequest { SoftShutdown = true, @@ -100,7 +141,7 @@ namespace Tgstation.Server.Tests.Live.Instance // for the restart staging tests await DeployTestDme("LongRunning/long_running_test", DreamDaemonSecurity.Trusted, true, cancellationToken); - System.Console.WriteLine("TEST: END WATCHDOG TESTS"); + System.Console.WriteLine($"TEST: END WATCHDOG TESTS {instanceClient.Metadata.Name}"); } async Task InteropTestsForLongRunningDme(CancellationToken cancellationToken) @@ -152,10 +193,10 @@ namespace Tgstation.Server.Tests.Live.Instance await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); - var topicRequestResult = await TopicClient.SendTopic( + var topicRequestResult = await topicClient.SendTopic( IPAddress.Loopback, $"shadow_wizard_money_gang=1", - TestLiveServer.DDPort, + ddPort, cancellationToken); Assert.IsNotNull(topicRequestResult); @@ -184,10 +225,10 @@ namespace Tgstation.Server.Tests.Live.Instance async Task TestDeleteByondInstallErrorCasesAndQueing(CancellationToken cancellationToken) { - var testCustomVersion = new Version(ByondTest.TestVersion.Major, ByondTest.TestVersion.Minor, 1); + var testCustomVersion = new Version(testVersion.Major, testVersion.Minor, 1); var currentByond = await instanceClient.Byond.ActiveVersion(cancellationToken); Assert.IsNotNull(currentByond); - Assert.AreEqual(ByondTest.TestVersion.Semver(), currentByond.Version); + Assert.AreEqual(testVersion.Semver(), currentByond.Version); // Change the active version and check we get delayed while deleting the old one because the watchdog is using it var setActiveResponse = await instanceClient.Byond.SetActiveVersion( @@ -204,7 +245,7 @@ namespace Tgstation.Server.Tests.Live.Instance var deleteJob = await instanceClient.Byond.DeleteVersion( new ByondVersionDeleteRequest { - Version = ByondTest.TestVersion, + Version = testVersion, }, cancellationToken); @@ -219,7 +260,7 @@ namespace Tgstation.Server.Tests.Live.Instance setActiveResponse = await instanceClient.Byond.SetActiveVersion( new ByondVersionRequest { - Version = ByondTest.TestVersion, + Version = testVersion, }, null, cancellationToken); @@ -245,7 +286,7 @@ namespace Tgstation.Server.Tests.Live.Instance deleteJob = await instanceClient.Byond.DeleteVersion( new ByondVersionDeleteRequest { - Version = ByondTest.TestVersion, + Version = testVersion, }, cancellationToken); @@ -255,13 +296,13 @@ namespace Tgstation.Server.Tests.Live.Instance return deleteJob; } - static async Task SendChatOverloadCommand(CancellationToken cancellationToken) + async Task SendChatOverloadCommand(CancellationToken cancellationToken) { // for the code coverage really... - var topicRequestResult = await TopicClient.SendTopic( + var topicRequestResult = await topicClient.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics5=1", - TestLiveServer.DDPort, + ddPort, cancellationToken); Assert.IsNotNull(topicRequestResult); @@ -389,7 +430,7 @@ namespace Tgstation.Server.Tests.Live.Instance { blockSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true); blockSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false); - blockSocket.Bind(new IPEndPoint(IPAddress.Any, TestLiveServer.DDPort)); + blockSocket.Bind(new IPEndPoint(IPAddress.Any, ddPort)); // Don't use StartDD here startJob = await instanceClient.DreamDaemon.Start(cancellationToken); @@ -453,7 +494,7 @@ namespace Tgstation.Server.Tests.Live.Instance CheckDDPriority(); // lock on to DD and pause it so it can't health check - var ddProcs = TestLiveServer.GetAllDDProcesses().Where(x => !x.HasExited).ToList(); + var ddProcs = TestLiveServer.GetDDProcessesOnPort(ddPort).Where(x => !x.HasExited).ToList(); if (ddProcs.Count != 1) Assert.Fail($"Incorrect number of DD processes: {ddProcs.Count}"); @@ -474,10 +515,10 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.IsFalse(ddProc.HasExited); // check DD agrees - var topicRequestResult = await TopicClient.SendTopic( + var topicRequestResult = await topicClient.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics8=1", - TestLiveServer.DDPort, + ddPort, cancellationToken); Assert.IsNotNull(topicRequestResult); @@ -542,7 +583,7 @@ namespace Tgstation.Server.Tests.Live.Instance { try { - SocketExtensions.BindTest(TestLiveServer.DDPort, false); + SocketExtensions.BindTest(ddPort, false); break; } catch @@ -583,12 +624,13 @@ namespace Tgstation.Server.Tests.Live.Instance builder.SetMinimumLevel(LogLevel.Trace); })) { - var bridgeProcessor = new TestBridgeHandler(bridgeTestsTcs, loggerFactory.CreateLogger(), serverPort); + var accessIdentifier = $"tgs_integration_test_for_instance_{instanceClient.Metadata.Name}"; + var bridgeProcessor = new TestBridgeHandler(bridgeTestsTcs, loggerFactory.CreateLogger(), accessIdentifier, serverPort); using var bridgeRegistration = instanceManager.RegisterHandler(bridgeProcessor); System.Console.WriteLine("TEST: Sending Bridge tests topic..."); - var bridgeTestTopicResult = await TopicClient.SendTopic(IPAddress.Loopback, "tgs_integration_test_tactics2=1", TestLiveServer.DDPort, cancellationToken); + var bridgeTestTopicResult = await topicClient.SendTopic(IPAddress.Loopback, $"tgs_integration_test_tactics2={accessIdentifier}", ddPort, cancellationToken); Assert.AreEqual("ack2", bridgeTestTopicResult.StringData); await bridgeTestsTcs.Task.WaitAsync(cancellationToken); @@ -600,7 +642,7 @@ namespace Tgstation.Server.Tests.Live.Instance await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); } - static async Task ValidateTopicLimits(CancellationToken cancellationToken) + async Task ValidateTopicLimits(CancellationToken cancellationToken) { // Time for topic tests // Request @@ -620,7 +662,7 @@ namespace Tgstation.Server.Tests.Live.Instance var baseSize = (int)(DMApiConstants.MaximumTopicRequestLength - 1); - var topicString = $"tgs_integration_test_tactics3={TopicClient.SanitizeString(json)}"; + var topicString = $"tgs_integration_test_tactics3={topicClient.SanitizeString(json)}"; var wrappingSize = topicString.Length; while (!cancellationToken.IsCancellationRequested) @@ -638,10 +680,10 @@ namespace Tgstation.Server.Tests.Live.Instance try { System.Console.WriteLine($"Topic send limit test S:{currentSize}..."); - topicRequestResult = await TopicClient.SendTopic( + topicRequestResult = await topicClient.SendTopic( IPAddress.Loopback, - $"tgs_integration_test_tactics3={TopicClient.SanitizeString(JsonConvert.SerializeObject(topic, DMApiConstants.SerializerSettings))}", - TestLiveServer.DDPort, + $"tgs_integration_test_tactics3={topicClient.SanitizeString(JsonConvert.SerializeObject(topic, DMApiConstants.SerializerSettings))}", + ddPort, cancellationToken); } catch (ArgumentOutOfRangeException) @@ -679,10 +721,10 @@ namespace Tgstation.Server.Tests.Live.Instance { var currentSize = baseSize + (int)Math.Pow(2, nextPow); System.Console.WriteLine($"Topic recieve limit test S:{currentSize}..."); - var topicRequestResult = await TopicClient.SendTopic( + var topicRequestResult = await topicClient.SendTopic( IPAddress.Loopback, - $"tgs_integration_test_tactics4={TopicClient.SanitizeString(currentSize.ToString())}", - TestLiveServer.DDPort, + $"tgs_integration_test_tactics4={topicClient.SanitizeString(currentSize.ToString())}", + ddPort, cancellationToken); if (topicRequestResult.ResponseType != TopicResponseType.StringResponse @@ -799,11 +841,7 @@ namespace Tgstation.Server.Tests.Live.Instance void CheckDDPriority() { - var ddProcessName = new PlatformIdentifier().IsWindows && ByondTest.TestVersion >= new Version(515, 1598) - ? "dd" - : "DreamDaemon"; - - var allProcesses = TestLiveServer.GetAllDDProcesses().Where(x => !x.HasExited).ToList(); + var allProcesses = TestLiveServer.GetDDProcessesOnPort(ddPort).Where(x => !x.HasExited).ToList(); if (allProcesses.Count == 0) Assert.Fail("Expected DreamDaemon to be running here"); @@ -827,6 +865,7 @@ namespace Tgstation.Server.Tests.Live.Instance var daemonStatus = await DeployTestDme(DmeName, DreamDaemonSecurity.Trusted, true, cancellationToken); var initialCompileJob = daemonStatus.ActiveCompileJob; + Assert.IsNotNull(initialCompileJob); Assert.AreEqual(WatchdogStatus.Offline, daemonStatus.Status.Value); Assert.IsNotNull(daemonStatus.ActiveCompileJob); Assert.IsNull(daemonStatus.StagedCompileJob); @@ -908,7 +947,7 @@ namespace Tgstation.Server.Tests.Live.Instance async Task RunLongRunningTestThenUpdateWithByondVersionSwitch(CancellationToken cancellationToken) { System.Console.WriteLine("TEST: WATCHDOG BYOND VERSION UPDATE TEST"); - var versionToInstall = ByondTest.TestVersion; + var versionToInstall = testVersion; versionToInstall = versionToInstall.Semver(); var currentByondVersion = await instanceClient.Byond.ActiveVersion(cancellationToken); @@ -979,7 +1018,7 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.AreEqual(WatchdogStatus.Online, daemonStatus.Status.Value); CheckDDPriority(); - Assert.AreEqual(TestLiveServer.DDPort, daemonStatus.CurrentPort); + Assert.AreEqual(ddPort, daemonStatus.CurrentPort); // Try killing the DD process to ensure it gets set to the restoring state do @@ -1007,9 +1046,9 @@ namespace Tgstation.Server.Tests.Live.Instance await CheckDMApiFail(daemonStatus.ActiveCompileJob, cancellationToken); } - static bool KillDD(bool require) + bool KillDD(bool require) { - var ddProcs = TestLiveServer.GetAllDDProcesses().Where(x => !x.HasExited).ToList(); + var ddProcs = TestLiveServer.GetDDProcessesOnPort(ddPort).Where(x => !x.HasExited).ToList(); if (require && ddProcs.Count == 0 || ddProcs.Count > 1) Assert.Fail($"Incorrect number of DD processes: {ddProcs.Count}"); @@ -1020,29 +1059,15 @@ namespace Tgstation.Server.Tests.Live.Instance return ddProc != null; } - static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder => - { - builder.AddConsole(); - builder.SetMinimumLevel(LogLevel.Trace); - }); - - public static readonly TopicClient TopicClient = new(new SocketParameters - { - SendTimeout = TimeSpan.FromSeconds(30), - ReceiveTimeout = TimeSpan.FromSeconds(30), - ConnectTimeout = TimeSpan.FromSeconds(30), - DisconnectTimeout = TimeSpan.FromSeconds(30) - }, loggerFactory.CreateLogger("WatchdogTest.TopicClient")); - - public Task TellWorldToReboot(CancellationToken cancellationToken) => TellWorldToReboot2(instanceClient, cancellationToken); - public static async Task TellWorldToReboot2(IInstanceClient instanceClient, CancellationToken cancellationToken) + public Task TellWorldToReboot(CancellationToken cancellationToken) => TellWorldToReboot2(instanceClient, ddPort, cancellationToken); + public static async Task TellWorldToReboot2(IInstanceClient instanceClient, ushort ddPort, CancellationToken cancellationToken) { var daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); Assert.IsNotNull(daemonStatus.StagedCompileJob); var initialCompileJob = daemonStatus.ActiveCompileJob; System.Console.WriteLine("TEST: Sending world reboot topic..."); - var result = await TopicClient.SendTopic(IPAddress.Loopback, "tgs_integration_test_special_tactics=1", TestLiveServer.DDPort, cancellationToken); + var result = await StaticTopicClient.SendTopic(IPAddress.Loopback, "tgs_integration_test_special_tactics=1", ddPort, cancellationToken); Assert.AreEqual("ack", result.StringData); using var tempCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); diff --git a/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs b/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs index b25ebe0efc..ed3abf33af 100644 --- a/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs +++ b/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs @@ -38,9 +38,9 @@ namespace Tgstation.Server.Tests.Live this.testRootPath = testRootPath ?? throw new ArgumentNullException(nameof(testRootPath)); } - public async Task CreateTestInstance(CancellationToken cancellationToken) + public async Task CreateTestInstance(string name, CancellationToken cancellationToken) { - var instance = await CreateTestInstanceStub("LiveTestsInstance", cancellationToken); + var instance = await CreateTestInstanceStub(name, cancellationToken); return await instanceManagerClient.Update(new InstanceUpdateRequest { Id = instance.Id, @@ -52,7 +52,7 @@ namespace Tgstation.Server.Tests.Live Task CreateTestInstanceStub(string name, CancellationToken cancellationToken) => instanceManagerClient.CreateOrAttach(new InstanceCreateRequest { Name = name, - Path = Path.Combine(testRootPath, Guid.NewGuid().ToString()), + Path = Path.Combine(testRootPath, $"Instance-{name}"), Online = true, ChatBotLimit = 2 }, cancellationToken); diff --git a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs index 8a85b74abc..c21f2dc4c9 100644 --- a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs +++ b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs @@ -15,6 +15,7 @@ using Tgstation.Server.Host; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Extensions; +using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Setup; using Tgstation.Server.Host.Utils; @@ -22,6 +23,33 @@ namespace Tgstation.Server.Tests.Live { sealed class LiveTestingServer : IServer, IDisposable { + public static string BaseDirectory { get; } + + static LiveTestingServer() + { + SerilogContextHelper.AddSwarmNodeIdentifierToTemplate(); + BaseDirectory = Environment.GetEnvironmentVariable("TGS_TEST_TEMP_DIRECTORY"); + if (string.IsNullOrWhiteSpace(BaseDirectory)) + { + BaseDirectory = Path.Combine(Path.GetTempPath(), "TGS_INTEGRATION_TEST"); + Cleanup(BaseDirectory).GetAwaiter().GetResult(); + } + } + + static async Task Cleanup(string directory) + { + for (int i = 0; i < 5; ++i) + try + { + new DefaultIOManager().DeleteDirectory(directory, default).GetAwaiter().GetResult(); + } + catch + { + GC.Collect(int.MaxValue, GCCollectionMode.Forced, false); + await Task.Delay(3000); + } + } + public Uri Url { get; } public string Directory { get; } @@ -44,25 +72,10 @@ namespace Tgstation.Server.Tests.Live public IServer RealServer { get; private set; } - static LiveTestingServer() - { - SerilogContextHelper.AddSwarmNodeIdentifierToTemplate(); - } - public LiveTestingServer(SwarmConfiguration swarmConfiguration, bool enableOAuth, ushort port = 15010) { Assert.IsTrue(port >= 10000); // for testing bridge request limit - Directory = Environment.GetEnvironmentVariable("TGS_TEST_TEMP_DIRECTORY"); - if (string.IsNullOrWhiteSpace(Directory)) - { - Directory = Path.Combine(Path.GetTempPath(), "TGS_INTEGRATION_TEST"); - if (System.IO.Directory.Exists(Directory) && swarmConfiguration == null) - try - { - System.IO.Directory.Delete(Directory, true); - } - catch { } - } + Directory = BaseDirectory; Directory = Path.Combine(Directory, Guid.NewGuid().ToString()); System.IO.Directory.CreateDirectory(Directory); @@ -83,7 +96,7 @@ namespace Tgstation.Server.Tests.Live Assert.Inconclusive("No connection string configured in env var TGS_TEST_CONNECTION_STRING!"); if (String.IsNullOrEmpty(gitHubAccessToken)) - Console.WriteLine("WARNING: No GitHub access token configured, test may fail due to rate limits!"); + System.Console.WriteLine("WARNING: No GitHub access token configured, test may fail due to rate limits!"); DumpOpenApiSpecpath = !String.IsNullOrEmpty(dumpOpenAPISpecPathEnvVar); @@ -148,19 +161,7 @@ namespace Tgstation.Server.Tests.Live UpdatePath = Path.Combine(Directory, Guid.NewGuid().ToString()); } - public void Dispose() - { - for (int i = 0; i < 5; ++i) - try - { - //System.IO.Directory.Delete(Directory, true); - } - catch - { - GC.Collect(int.MaxValue, GCCollectionMode.Forced, false); - Thread.Sleep(3000); - } - } + public void Dispose() => Cleanup(Directory).GetAwaiter().GetResult(); public void UpdateSwarmArguments(SwarmConfiguration swarmConfiguration) { @@ -194,7 +195,7 @@ namespace Tgstation.Server.Tests.Live public async Task Run(CancellationToken cancellationToken) { var messageAddition = swarmNodeId != null ? $": {swarmNodeId}" : String.Empty; - Console.WriteLine("TEST SERVER START" + messageAddition); + System.Console.WriteLine("TEST SERVER START" + messageAddition); var firstRun = RealServer == null; var arrayArgs = args.Concat(swarmArgs).ToArray(); RealServer = await Application @@ -213,7 +214,7 @@ namespace Tgstation.Server.Tests.Live ? LogContext.PushProperty(SerilogContextHelper.SwarmIdentifierContextProperty, swarmNodeId) : null) await RealServer.Run(cancellationToken); - Console.WriteLine($"TEST SERVER END" + messageAddition); + System.Console.WriteLine($"TEST SERVER END" + messageAddition); } } } diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index 5de00a2cf3..38d2d28b1e 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -4,6 +4,7 @@ using System.Data.SqlClient; using System.Diagnostics; using System.IO; using System.Linq; +using System.Management; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -46,52 +47,88 @@ namespace Tgstation.Server.Tests.Live [TestCategory("SkipWhenLiveUnitTesting")] public sealed class TestLiveServer { - public static ushort DDPort { get; } = FreeTcpPort(); - public static ushort DMPort { get; } = GetDMPort(); - public static readonly Version TestUpdateVersion = new(5, 11, 0); + static readonly ushort mainDDPort = FreeTcpPort(); + static readonly ushort mainDMPort = FreeTcpPort(mainDDPort); + static readonly ushort compatDMPort = FreeTcpPort(mainDDPort, mainDMPort); + static readonly ushort compatDDPort = FreeTcpPort(mainDDPort, mainDMPort, compatDMPort); + readonly IServerClientFactory clientFactory = new ServerClientFactory(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name, Assembly.GetExecutingAssembly().GetName().Version.ToString())); - public static List GetAllDDProcesses() + public static List GetDDProcessesOnPort(ushort? port) { var result = new List(); result.AddRange(System.Diagnostics.Process.GetProcessesByName("DreamDaemon")); - if(new PlatformIdentifier().IsWindows) + if (new PlatformIdentifier().IsWindows) result.AddRange(System.Diagnostics.Process.GetProcessesByName("dd")); + if (port.HasValue) + result = result.Where(x => + { + if (GetCommandLine(x).Contains($"-port {port.Value}")) + return true; + + x.Dispose(); + return false; + }).ToList(); + return result; } + private static string GetCommandLine(System.Diagnostics.Process process) + { + if (new PlatformIdentifier().IsWindows) + { + var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id); + var objects = searcher.Get(); + return objects.Cast().SingleOrDefault()?["CommandLine"]?.ToString(); + } + + var cmdlineFile = File.ReadAllText($"/proc/{process.Id}/cmdline"); + var parsed = cmdlineFile.Replace('\0', ' '); + return parsed; + } + static void TerminateAllDDs() { - foreach (var proc in GetAllDDProcesses()) + foreach (var proc in GetDDProcessesOnPort(null)) using (proc) proc.Kill(); } - static ushort GetDMPort() + static ushort FreeTcpPort(params ushort[] usedPorts) { ushort result; - do - { - result = FreeTcpPort(); - } while (result == DDPort); - return result; - } - - static ushort FreeTcpPort() - { - var l = new TcpListener(IPAddress.Loopback, 0); - l.Start(); + var listeners = new List(); try { - return (ushort)((IPEndPoint)l.LocalEndpoint).Port; + do + { + var l = new TcpListener(IPAddress.Loopback, 0); + l.Start(); + try + { + listeners.Add(l); + } + catch + { + l.Stop(); + throw; + } + + result = (ushort)((IPEndPoint)l.LocalEndpoint).Port; + } + while (usedPorts.Contains(result)); } finally { - l.Stop(); + foreach(var l in listeners) + { + l.Stop(); + } } + return result; } [ClassInitialize] @@ -1016,6 +1053,7 @@ namespace Tgstation.Server.Tests.Live // main run var serverTask = server.Run(cancellationToken); + var fileDownloader = ((Host.Server)server.RealServer).Host.Services.GetRequiredService(); try { Api.Models.Instance instance; @@ -1041,11 +1079,7 @@ namespace Tgstation.Server.Tests.Live { await task; } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) + catch (Exception ex) when (ex is not OperationCanceledException) { Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex}"); serverCts.Cancel(); @@ -1056,27 +1090,42 @@ namespace Tgstation.Server.Tests.Live var rootTest = FailFast(RawRequestTests.Run(clientFactory, adminClient, cancellationToken)); var adminTest = FailFast(new AdministrationTest(adminClient.Administration).Run(cancellationToken)); var usersTest = FailFast(new UsersTest(adminClient).Run(cancellationToken)); - var instanceMangagerTest = new InstanceManagerTest(adminClient, server.Directory); - instance = await instanceMangagerTest.CreateTestInstance(cancellationToken); - var instancesTest = FailFast(instanceMangagerTest.RunPreTest(cancellationToken)); + var instanceManagerTest = new InstanceManagerTest(adminClient, server.Directory); + var compatInstanceTask = instanceManagerTest.CreateTestInstance("LiveTestsInstance", cancellationToken); + instance = await instanceManagerTest.CreateTestInstance("CompatTestsInstance", cancellationToken); + var compatInstance = await compatInstanceTask; + var instancesTest = FailFast(instanceManagerTest.RunPreTest(cancellationToken)); Assert.IsTrue(Directory.Exists(instance.Path)); var instanceClient = adminClient.Instances.CreateClient(instance); Assert.IsTrue(Directory.Exists(instanceClient.Metadata.Path)); - var instanceTests = FailFast( - new InstanceTest( - instanceClient, + var instanceTest = new InstanceTest( adminClient.Instances, - ((Host.Server)server.RealServer).Host.Services.GetRequiredService(), + fileDownloader, GetInstanceManager(), - (ushort)server.Url.Port) - .RunTests( - server.HighPriorityDreamDaemon, - server.LowPriorityDeployments, - cancellationToken)); + (ushort)server.Url.Port); - await Task.WhenAll(rootTest, adminTest, instancesTest, instanceTests, usersTest); + var instanceTests = FailFast( + instanceTest + .RunTests( + instanceClient, + mainDMPort, + mainDDPort, + server.HighPriorityDreamDaemon, + server.LowPriorityDeployments, + cancellationToken)); + + var compatTests = FailFast( + instanceTest + .RunCompatTests( + adminClient.Instances.CreateClient(compatInstance), + compatDMPort, + compatDDPort, + server.HighPriorityDreamDaemon, + cancellationToken)); + + await Task.WhenAll(rootTest, adminTest, instancesTest, instanceTests, usersTest, compatTests); var dd = await instanceClient.DreamDaemon.Read(cancellationToken); Assert.AreEqual(WatchdogStatus.Online, dd.Status.Value); @@ -1093,10 +1142,10 @@ namespace Tgstation.Server.Tests.Live // test the reattach message queueing // for the code coverage really... - var topicRequestResult = await WatchdogTest.TopicClient.SendTopic( + var topicRequestResult = await WatchdogTest.StaticTopicClient.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics6=1", - DDPort, + mainDDPort, cancellationToken); Assert.IsNotNull(topicRequestResult); @@ -1169,10 +1218,10 @@ namespace Tgstation.Server.Tests.Live var chatReadTask = instanceClient.ChatBots.List(null, cancellationToken); // Check the DMAPI got the channels again https://github.com/tgstation/tgstation-server/issues/1490 - topicRequestResult = await WatchdogTest.TopicClient.SendTopic( + topicRequestResult = await WatchdogTest.StaticTopicClient.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics7=1", - DDPort, + mainDDPort, cancellationToken); Assert.IsNotNull(topicRequestResult); @@ -1186,7 +1235,7 @@ namespace Tgstation.Server.Tests.Live Assert.AreEqual(connectedChannelCount, channelsPresent); - await WatchdogTest.TellWorldToReboot2(instanceClient, cancellationToken); + await WatchdogTest.TellWorldToReboot2(instanceClient, mainDDPort, cancellationToken); dd = await instanceClient.DreamDaemon.Read(cancellationToken); Assert.AreEqual(WatchdogStatus.Online, dd.Status.Value); @@ -1238,6 +1287,7 @@ namespace Tgstation.Server.Tests.Live preStartupTime = DateTimeOffset.UtcNow; serverTask = server.Run(cancellationToken); long expectedCompileJobId, expectedStaged; + var edgeByond = await ByondTest.GetEdgeVersion(fileDownloader, cancellationToken); using (var adminClient = await CreateAdminClient(server.Url, cancellationToken)) { var instanceClient = adminClient.Instances.CreateClient(instance); @@ -1248,7 +1298,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, server.HighPriorityDreamDaemon); + var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort); await wdt.WaitForJob(compileJob, 30, false, null, cancellationToken); dd = await instanceClient.DreamDaemon.Read(cancellationToken); @@ -1295,7 +1345,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, server.HighPriorityDreamDaemon); + var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort); currentDD = await wdt.TellWorldToReboot(cancellationToken); Assert.AreEqual(expectedStaged, currentDD.ActiveCompileJob.Job.Id.Value); Assert.IsNull(currentDD.StagedCompileJob);