From 600d34726bab2d44f66d9509c12cb53945e648ac Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Tue, 7 Jul 2026 10:30:39 +1000 Subject: [PATCH] Add repo tests and fix build --- .github/workflows/build-test.yml | 44 ++++++++++++++ Content.Server/LocalHostConGroup.cs | 4 +- Content.Tests/Content.Tests.csproj | 25 ++++++++ Content.Tests/StartupTest.cs | 94 +++++++++++++++++++++++++++++ README.md | 2 +- RobustTemplate.sln | 21 +++---- RobustToolbox | 2 +- 7 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/build-test.yml create mode 100644 Content.Tests/Content.Tests.csproj create mode 100644 Content.Tests/StartupTest.cs diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000..c0ccf41 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,44 @@ +name: Build & Test + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest] + + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v4.2.2 + with: + submodules: recursive + + - name: Setup .NET Core + uses: actions/setup-dotnet@v4.1.0 + with: + dotnet-version: 10.0.x + + - name: Install dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore /p:WarningsAsErrors=nullable + + - name: Run tests + shell: pwsh + run: dotnet test --no-build Content.Tests/Content.Tests.csproj -- NUnit.ConsoleOut=0 NUnit.TestOutputXml="logs" NUnit.WorkDirectory="$(pwd)/test_results" + + - name: Archive NUnit3 test results. + if: always() + uses: actions/upload-artifact@v4 + with: + name: nunit3-results-${{ matrix.os }} + path: test_results/* + retention-days: 7 diff --git a/Content.Server/LocalHostConGroup.cs b/Content.Server/LocalHostConGroup.cs index bc85b00..0e5c6f4 100644 --- a/Content.Server/LocalHostConGroup.cs +++ b/Content.Server/LocalHostConGroup.cs @@ -13,9 +13,9 @@ namespace Content.Server; /// Debug/example ConGroup controller implementation that gives any client connected through localhost every permission. /// [UsedImplicitly] -public sealed class LocalHostConGroup : IConGroupControllerImplementation, IPostInjectInit +public sealed partial class LocalHostConGroup : IConGroupControllerImplementation, IPostInjectInit { - [Dependency] private readonly IConGroupController _controller = null!; + [Dependency] private IConGroupController _controller = null!; public bool CanCommand(ICommonSession session, string cmdName) { return IsLocal(session); diff --git a/Content.Tests/Content.Tests.csproj b/Content.Tests/Content.Tests.csproj new file mode 100644 index 0000000..111af38 --- /dev/null +++ b/Content.Tests/Content.Tests.csproj @@ -0,0 +1,25 @@ + + + + + false + enable + enable + nullable + false + ..\bin\Content.Tests\ + + + + + + + + + + + + + + + diff --git a/Content.Tests/StartupTest.cs b/Content.Tests/StartupTest.cs new file mode 100644 index 0000000..1a359de --- /dev/null +++ b/Content.Tests/StartupTest.cs @@ -0,0 +1,94 @@ +using Content.Client; +using NUnit.Framework; +using Robust.Server.Player; +using Robust.Shared.Enums; +using Robust.Shared.IoC; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.UnitTesting; + +namespace Content.Tests; + +[TestFixture] +public sealed class StartupTest : RobustIntegrationTest +{ + [Test] + public async Task ServerStarts() + { + var server = StartServer(ServerOptions()); + + await server.WaitIdleAsync(); + + Assert.That(server.IsAlive, Is.True); + Assert.That(server.UnhandledException, Is.Null); + } + + [Test] + public async Task ClientStarts() + { + var client = StartClient(ClientOptions()); + + await client.WaitIdleAsync(); + + Assert.That(client.IsAlive, Is.True); + Assert.That(client.UnhandledException, Is.Null); + } + + [Test] + public async Task ClientConnectsToServer() + { + await using var pair = await StartConnectedPair(ServerOptions(), ClientOptions(), "TemplateTester"); + var server = pair.Server; + var client = pair.Client; + + await RunTicksSync(server, client, 10); + + await server.WaitAssertion(() => + { + var playerManager = IoCManager.Resolve(); + + Assert.That(playerManager.PlayerCount, Is.EqualTo(1)); + Assert.That(playerManager.Sessions, Has.One.Matches( + session => session.Status == SessionStatus.Connected && session.Channel.IsConnected)); + }); + + await client.WaitAssertion(() => + { + var netManager = IoCManager.Resolve(); + + Assert.That(netManager.IsConnected, Is.True); + Assert.That(netManager.ServerChannel, Is.Not.Null); + Assert.That(netManager.ServerChannel!.IsConnected, Is.True); + }); + } + + private static ServerIntegrationOptions ServerOptions() + { + return new ServerIntegrationOptions + { + ContentStart = true, + ContentAssemblies = [typeof(Content.Server.EntryPoint).Assembly], + Options = new Robust.Server.ServerOptions + { + ContentModulePrefix = "Content.", + LoadConfigAndUserData = false, + LoadContentResources = false, + }, + }; + } + + private static ClientIntegrationOptions ClientOptions() + { + return new ClientIntegrationOptions + { + ContentStart = true, + ContentAssemblies = [typeof(EntryPoint).Assembly], + Options = new Robust.Client.GameControllerOptions + { + ContentModulePrefix = "Content.", + LoadConfigAndUserData = false, + LoadContentResources = false, + }, + }; + } +} diff --git a/README.md b/README.md index ba3a686..b257ded 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This template additionally includes some helpful IDE settings if you're using Vi An example file structure for Resources is also provided. ### Wait, why would I use RobustToolbox? -RobustToolbox is a fairly modern ECS game engine with strong multiplayer support. Out of the box, it's netcode is capable of handling hundreds of players (as demonstrated by the game it was originally written for, [Space Station 14](https://spacestation14.io/)) and is relatively easy to use with less risk of common mistakes like giving clients authority over game state compared to other engines. +RobustToolbox is a fairly modern ECS game engine with strong multiplayer support. Out of the box, its netcode is capable of handling hundreds of players (as demonstrated by the game it was originally written for, [Space Station 14](https://spacestation14.io/)) and is relatively easy to use with less risk of common mistakes like giving clients authority over game state compared to other engines. It provides prediction-based server authoritative netcode by default, with options to both autogenerate simple state synchronization for components and to manually implement more complex state application algorithms as necessary. Additionally, basic RPC is provided through networked entity events (which can be sent both ways, and optionally targeted at specific entities) alongside direct access to the underlying transit layer for more direct control when necessary. diff --git a/RobustTemplate.sln b/RobustTemplate.sln index fb1dc2e..d06feae 100644 --- a/RobustTemplate.sln +++ b/RobustTemplate.sln @@ -37,8 +37,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.IL.Cecil", "RobustToo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.Runtime", "RobustToolbox\XamlX\src\XamlX.Runtime\XamlX.Runtime.csproj", "{19BC1F51-45E8-4BA6-854C-FDB7670AB4E3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenToolkit.GraphicsLibraryFramework", "RobustToolbox\OpenToolkit.GraphicsLibraryFramework\OpenToolkit.GraphicsLibraryFramework.csproj", "{6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Robust.LoaderApi", "Robust.LoaderApi", "{EDB842C6-67AE-4F0E-A9F2-09C2DF1024C9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.LoaderApi", "RobustToolbox\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj", "{EC94C69F-F0E2-44B0-BE5E-C385BECB6AD3}" @@ -68,6 +66,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project Files", "Project Fi Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.Tests", "Content.Tests\Content.Tests.csproj", "{72C482DE-2310-4CDD-BE0A-0BFF29E25987}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -183,14 +183,6 @@ Global {19BC1F51-45E8-4BA6-854C-FDB7670AB4E3}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU {19BC1F51-45E8-4BA6-854C-FDB7670AB4E3}.Tools|Any CPU.ActiveCfg = Release|Any CPU {19BC1F51-45E8-4BA6-854C-FDB7670AB4E3}.Tools|Any CPU.Build.0 = Release|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.Release|Any CPU.Build.0 = Release|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.DebugOpt|Any CPU.ActiveCfg = DebugOpt|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.DebugOpt|Any CPU.Build.0 = DebugOpt|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.Tools|Any CPU.ActiveCfg = Tools|Any CPU - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F}.Tools|Any CPU.Build.0 = Tools|Any CPU {EC94C69F-F0E2-44B0-BE5E-C385BECB6AD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EC94C69F-F0E2-44B0-BE5E-C385BECB6AD3}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC94C69F-F0E2-44B0-BE5E-C385BECB6AD3}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -279,6 +271,14 @@ Global {DBF1DF9E-6359-4179-9AB9-F38E5A348ABB}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU {DBF1DF9E-6359-4179-9AB9-F38E5A348ABB}.Tools|Any CPU.ActiveCfg = Debug|Any CPU {DBF1DF9E-6359-4179-9AB9-F38E5A348ABB}.Tools|Any CPU.Build.0 = Debug|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.Release|Any CPU.Build.0 = Release|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.DebugOpt|Any CPU.ActiveCfg = DebugOpt|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.DebugOpt|Any CPU.Build.0 = DebugOpt|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.Tools|Any CPU.ActiveCfg = Tools|Any CPU + {72C482DE-2310-4CDD-BE0A-0BFF29E25987}.Tools|Any CPU.Build.0 = Tools|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {2A829DE3-FCB2-4FEA-A6F3-B85122C8D11E} = {F5F87A9D-C304-4833-B107-D666317F6931} @@ -294,7 +294,6 @@ Global {7380FFC8-AFEF-4B66-8426-2D8E34221CF4} = {91AF0388-356D-4929-9A86-1B1F5196E921} {A406137A-DEC7-4CC5-99A8-6595ACC70B92} = {91AF0388-356D-4929-9A86-1B1F5196E921} {19BC1F51-45E8-4BA6-854C-FDB7670AB4E3} = {91AF0388-356D-4929-9A86-1B1F5196E921} - {6E9EFBDA-DC7F-4AFE-8BEF-D75C71FFB11F} = {F5F87A9D-C304-4833-B107-D666317F6931} {EDB842C6-67AE-4F0E-A9F2-09C2DF1024C9} = {F5F87A9D-C304-4833-B107-D666317F6931} {EC94C69F-F0E2-44B0-BE5E-C385BECB6AD3} = {EDB842C6-67AE-4F0E-A9F2-09C2DF1024C9} {AF551ED1-6F07-4035-8ADE-E647A9E533AB} = {F5F87A9D-C304-4833-B107-D666317F6931} diff --git a/RobustToolbox b/RobustToolbox index dbde802..b366e29 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit dbde8023ed256ac69ae4b419422c936f78361653 +Subproject commit b366e29750a1b41352bc0ca0c7c47b618df36838