1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-17 12:02:58 +08:00
Files
osu-lazer/osu.Game.Tests/IPC/WebSocketTest.cs
T
Bartłomiej Dach 9a2846539f Implement WebSocket server skeleton for external integrations (#37335)
- Supersedes / closes https://github.com/ppy/osu/pull/18129. Reasons I
didn't use that PR are hopefully obvious upon comparing diffs but I can
elaborate if they are not.
- Single metric included for demonstration purposes.
- Do not want to talk about further schema design at this time.
- Specify `OSU_WEBSOCKET_SERVER=1` envvar to enable.
- Can test consumption with [this five minute html
job](https://github.com/user-attachments/files/26839923/index.html)
(works even as a standalone file opened in browser, no CORS bs!)
- There's a lot of inline comments, go read them. There are many WTFs
because the .NET frozen websocket API is weird and stanky and reeks of
the year 2007. The inline comments attempt to explain.
2026-04-21 18:25:12 +09:00

275 lines
7.5 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Game.IPC;
using osu.Game.Online.Multiplayer;
namespace osu.Game.Tests.IPC
{
[TestFixture]
public class WebSocketTest
{
[Test]
public async Task TestClientInitiatedDuplexCommunication()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
var duplexComplete = new ManualResetEventSlim(false);
server.MessageReceived += (clientId, msg) =>
{
if (msg != "PING")
return;
// ReSharper disable once AccessToDisposedClosure
server.SendAsync(clientId, "PONG").FireAndForget();
};
client.MessageReceived += msg =>
{
if (msg != "PONG")
return;
duplexComplete.Set();
};
await server.StartAsync();
await client.Start();
await client.SendAsync("PING");
Assert.That(duplexComplete.Wait(10_000));
await client.StopAsync();
await server.StopAsync();
client.Dispose();
server.Dispose();
}
[Test]
public async Task TestServerInitiatedDuplexCommunication()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
var clientConnected = new ManualResetEventSlim();
var duplexComplete = new ManualResetEventSlim();
client.MessageReceived += msg =>
{
if (msg != "PING")
return;
// ReSharper disable once AccessToDisposedClosure
client.SendAsync("PONG").FireAndForget();
};
server.ClientConnected += _ => clientConnected.Set();
server.MessageReceived += (_, msg) =>
{
if (msg != "PONG")
return;
duplexComplete.Set();
};
await server.StartAsync();
await client.Start();
Assert.That(clientConnected.Wait(10_000));
await server.SendAsync(1, "PING");
Assert.That(duplexComplete.Wait(10_000));
await client.StopAsync();
await server.StopAsync();
client.Dispose();
server.Dispose();
}
[Test]
public async Task TestServerBroadcast()
{
const int port = 54321;
const int client_count = 5;
var server = new WebSocketServer(port);
var clients = new List<WebSocketClient>(client_count);
var connectionCountdown = new CountdownEvent(client_count);
var receiptCountdown = new CountdownEvent(client_count);
for (int i = 0; i < client_count; ++i)
{
var client = new WebSocketClient(port);
client.MessageReceived += msg =>
{
if (msg != "HI ALL")
return;
receiptCountdown.Signal();
};
clients.Add(client);
}
server.ClientConnected += _ => connectionCountdown.Signal();
await server.StartAsync();
foreach (var client in clients)
await client.Start();
Assert.That(connectionCountdown.Wait(10_000));
await server.BroadcastAsync("HI ALL");
Assert.That(receiptCountdown.Wait(10_000));
foreach (var client in clients)
{
await client.StopAsync();
client.Dispose();
}
await server.StopAsync();
server.Dispose();
}
[Test]
public async Task TestClientSoftAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await client.StopAsync();
client.Dispose();
await server.StopAsync();
server.Dispose();
}
[Test]
public async Task TestClientHardAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await client.StopAsync(new CancellationToken(true));
client.Dispose();
await server.StopAsync();
server.Dispose();
}
[Test]
public async Task TestServerSoftAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await server.StopAsync();
server.Dispose();
await client.StopAsync();
client.Dispose();
}
[Test]
public async Task TestServerHardAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await server.StopAsync(new CancellationToken(true));
server.Dispose();
await client.StopAsync();
client.Dispose();
}
[Test]
public async Task TestClientMessageTooLong()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
var clientClosed = new ManualResetEventSlim();
client.Closed += clientClosed.Set;
await server.StartAsync();
await client.Start();
await client.SendAsync(new string('0', 9999));
Assert.That(clientClosed.Wait(10_000));
await client.StopAsync();
client.Dispose();
var client2 = new WebSocketClient(port);
var duplexComplete = new ManualResetEventSlim();
server.MessageReceived += (clientId, msg) =>
{
if (msg != "PING")
return;
// ReSharper disable once AccessToDisposedClosure
server.SendAsync(clientId, "PONG").FireAndForget();
};
client2.MessageReceived += msg =>
{
if (msg != "PONG")
return;
duplexComplete.Set();
};
await client2.Start();
await client2.SendAsync("PING");
Assert.That(duplexComplete.Wait(10000));
await client2.StopAsync();
await server.StopAsync();
client2.Dispose();
server.Dispose();
}
[Test]
public async Task TestStartStopServerWithoutReceivingClients()
{
const int port = 54321;
var server = new WebSocketServer(port);
await server.StartAsync();
await server.StopAsync();
server.Dispose();
}
}
}