Skip to content

feat: Add test for named message sent and named message received metrics #861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions com.unity.multiplayer.mlapi/Runtime/Metrics/NetworkMetrics.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using MLAPI.Connection;
using Unity.Multiplayer.NetStats.Dispatch;
using Unity.Multiplayer.NetStats.Metrics;
using Unity.Multiplayer.NetworkProfiler;
Expand All @@ -11,11 +10,11 @@ public interface INetworkMetrics
{
void TrackNetworkObject(NetworkObject networkObject);

void TrackNamedMessageSent(ulong receiver, string messageName, ulong bytesCount);
void TrackNamedMessageSent(ulong receiverClientId, string messageName, ulong bytesCount);

void TrackNamedMessageSent(IReadOnlyCollection<ulong> receivers, string messageName, ulong bytesCount);
void TrackNamedMessageSent(IReadOnlyCollection<ulong> receiverClientIds, string messageName, ulong bytesCount);

void TrackNamedMessageReceived(ulong sender, string messageName, ulong bytesCount);
void TrackNamedMessageReceived(ulong senderClientId, string messageName, ulong bytesCount);

void DispatchFrame();
}
Expand All @@ -26,15 +25,15 @@ public void TrackNetworkObject(NetworkObject networkObject)
{
}

public void TrackNamedMessageSent(ulong receiver, string messageName, ulong bytesCount)
public void TrackNamedMessageSent(ulong receiverClientId, string messageName, ulong bytesCount)
{
}

public void TrackNamedMessageSent(IReadOnlyCollection<ulong> receivers, string messageName, ulong bytesCount)
public void TrackNamedMessageSent(IReadOnlyCollection<ulong> receiverClientIds, string messageName, ulong bytesCount)
{
}

public void TrackNamedMessageReceived(ulong sender, string messageName, ulong bytesCount)
public void TrackNamedMessageReceived(ulong senderClientId, string messageName, ulong bytesCount)
{
}

Expand All @@ -46,7 +45,6 @@ public void DispatchFrame()
#if true
public class NetworkMetrics : INetworkMetrics
{
private readonly IMetricDispatcher m_Dispatcher;
private readonly NetworkManager m_NetworkManager;

private EventMetric<NamedMessageEvent> m_NamedMessageSentEvent = new EventMetric<NamedMessageEvent>("Named Message Sent");
Expand All @@ -57,13 +55,15 @@ public class NetworkMetrics : INetworkMetrics
public NetworkMetrics(NetworkManager networkManager)
{
m_NetworkManager = networkManager;
m_Dispatcher = new MetricDispatcherBuilder()
Dispatcher = new MetricDispatcherBuilder()
.WithMetricEvents(m_NamedMessageSentEvent, m_NamedMessageReceivedEvent)
.Build();

m_Dispatcher.RegisterObserver(MLAPIObserver.Observer);
Dispatcher.RegisterObserver(MLAPIObserver.Observer);
}

internal IMetricDispatcher Dispatcher { get; }

public void TrackNetworkObject(NetworkObject networkObject)
{
if (!m_NetworkGameObjects.ContainsKey(networkObject.NetworkObjectId))
Expand All @@ -72,27 +72,27 @@ public void TrackNetworkObject(NetworkObject networkObject)
}
}

public void TrackNamedMessageSent(ulong receiver, string messageName, ulong bytesCount)
public void TrackNamedMessageSent(ulong receiverClientId, string messageName, ulong bytesCount)
{
m_NamedMessageSentEvent.Mark(new NamedMessageEvent(new ConnectionInfo(receiver), messageName, bytesCount));
m_NamedMessageSentEvent.Mark(new NamedMessageEvent(new ConnectionInfo(receiverClientId), messageName, bytesCount));
}

public void TrackNamedMessageSent(IReadOnlyCollection<ulong> receivers, string messageName, ulong bytesCount)
public void TrackNamedMessageSent(IReadOnlyCollection<ulong> receiverClientIds, string messageName, ulong bytesCount)
{
foreach (var receiver in receivers)
foreach (var receiver in receiverClientIds)
{
TrackNamedMessageSent(receiver, messageName, bytesCount);
}
}

public void TrackNamedMessageReceived(ulong sender, string messageName, ulong bytesCount)
public void TrackNamedMessageReceived(ulong senderClientId, string messageName, ulong bytesCount)
{
m_NamedMessageReceivedEvent.Mark(new NamedMessageEvent(new ConnectionInfo(sender), messageName, bytesCount));
m_NamedMessageReceivedEvent.Mark(new NamedMessageEvent(new ConnectionInfo(senderClientId), messageName, bytesCount));
}

public void DispatchFrame()
{
m_Dispatcher.Dispatch();
Dispatcher.Dispatch();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo
SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject);
SpawnedObjectsList.Add(networkObject);

NetworkManager.Singleton.NetworkMetrics.TrackNetworkObject(networkObject);
NetworkManager.NetworkMetrics.TrackNetworkObject(networkObject);

if (ownerClientId != null)
{
Expand Down
8 changes: 8 additions & 0 deletions com.unity.multiplayer.mlapi/Tests/Runtime/Metrics.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MLAPI.Metrics;
using NUnit.Framework;
using Unity.Multiplayer.NetStats.Dispatch;
using Unity.Multiplayer.NetStats.Metrics;
using Unity.Multiplayer.NetworkProfiler;
using Unity.Multiplayer.NetworkProfiler.Models;
using UnityEngine;
using UnityEngine.TestTools;

namespace MLAPI.RuntimeTests.Metrics
{
#if true
public class NetworkMetricsTests
{
[UnityTest]
public IEnumerator NetworkMetrics_WhenNamedMessageSent_TracksNamedMessageSentMetric()
{
NetworkManagerHelper.StartNetworkManager(out var networkManager);
var networkMetrics = networkManager.NetworkMetrics as NetworkMetrics;
var messageName = Guid.NewGuid().ToString();

var clientId = 100UL;
networkMetrics.Dispatcher.RegisterObserver(new TestObserver(collection =>
{
var namedMessageSentMetric = AssertSingleMetricEventOfType<NamedMessageEvent>(collection, MetricNames.NamedMessageSent);
Assert.AreEqual(1, namedMessageSentMetric.Values.Count);

var namedMessageSent = namedMessageSentMetric.Values.First();
Assert.AreEqual(messageName, namedMessageSent.Name);
Assert.AreEqual(clientId, namedMessageSent.Connection.Id);
}));

networkManager.CustomMessagingManager.SendNamedMessage(messageName, clientId, Stream.Null);

yield return WaitForMetricsDispatch();

NetworkManagerHelper.ShutdownNetworkManager();
}

[UnityTest]
public IEnumerator NetworkMetrics_WhenNamedMessageSentToMultipleClients_TracksNamedMessageSentMetric()
{
NetworkManagerHelper.StartNetworkManager(out var networkManager);
var networkMetrics = networkManager.NetworkMetrics as NetworkMetrics;
var messageName = Guid.NewGuid().ToString();

networkMetrics.Dispatcher.RegisterObserver(new TestObserver(collection =>
{
var namedMessageSentMetric = AssertSingleMetricEventOfType<NamedMessageEvent>(collection, MetricNames.NamedMessageSent);
Assert.AreEqual(3, namedMessageSentMetric.Values.Count);

var clientIds = namedMessageSentMetric.Values.Select(x => x.Connection.Id).ToList();
Assert.Contains(100UL, clientIds);
Assert.Contains(200UL, clientIds);
Assert.Contains(300UL, clientIds);
}));

networkManager.CustomMessagingManager.SendNamedMessage(messageName, new List<ulong> { 100, 200, 300 }, Stream.Null);

yield return WaitForMetricsDispatch();

NetworkManagerHelper.ShutdownNetworkManager();
}

[UnityTest]
public IEnumerator NetworkMetrics_WhenNamedMessageReceived_TracksNamedMessageReceivedMetric()
{
var createServerAndSingleClient = new CreateServerAndSingleClient();
yield return createServerAndSingleClient.Run();
var server = createServerAndSingleClient.Server;
var client = createServerAndSingleClient.Client;
var clientMetrics = client.NetworkMetrics as NetworkMetrics;

var messageName = Guid.NewGuid().ToString();
LogAssert.Expect(LogType.Log, $"Received from {server.LocalClientId}");
client.CustomMessagingManager.RegisterNamedMessageHandler(messageName, (sender, payload) =>
{
Debug.Log($"Received from {sender}");
});

var found = false;
clientMetrics.Dispatcher.RegisterObserver(new TestObserver(collection =>
{
var namedMessageSentMetric = collection.Metrics.SingleOrDefault(x => x.Name == MetricNames.NamedMessageReceived);
Assert.NotNull(namedMessageSentMetric);

var typedMetric = namedMessageSentMetric as IEventMetric<NamedMessageEvent>;
Assert.NotNull(typedMetric);
if (typedMetric.Values.Any()) // We always get the metric, but when it has values, something has been tracked
{
Assert.AreEqual(1, typedMetric.Values.Count);

var namedMessageSent = typedMetric.Values.First();
Assert.AreEqual(messageName, namedMessageSent.Name);
Assert.AreEqual(server.LocalClientId, namedMessageSent.Connection.Id);

found = true;
}
}));

server.CustomMessagingManager.SendNamedMessage(messageName, client.LocalClientId, Stream.Null);

yield return WaitForAFewFrames(); // Client does not receive message synchronously

MultiInstanceHelpers.Destroy();

Assert.True(found);
}

private IEnumerator WaitForMetricsDispatch()
{
yield return new WaitForEndOfFrame();
yield return new WaitForEndOfFrame();
}

private IEnumerator WaitForAFewFrames()
{
yield return new WaitForSeconds(0.5f);
}

private IEventMetric<TEvent> AssertSingleMetricEventOfType<TEvent>(MetricCollection collection, string name)
{
var metric = collection.Metrics.SingleOrDefault(x => x.Name == name);
Assert.NotNull(metric);

var typedMetric = metric as IEventMetric<TEvent>;
Assert.NotNull(typedMetric);
Assert.IsNotEmpty(typedMetric.Values);

return typedMetric;
}

private class TestObserver : IMetricObserver
{
private readonly Action<MetricCollection> m_Assertion;

public TestObserver(Action<MetricCollection> assertion)
{
m_Assertion = assertion;
}

public void Observe(MetricCollection collection)
{
m_Assertion.Invoke(collection);
}
}

public class CreateServerAndSingleClient
{
public NetworkManager Server { get; private set; }

public NetworkManager Client { get; private set; }

public IEnumerator Run()
{
if (!MultiInstanceHelpers.Create(1, out var server, out NetworkManager[] clients))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}

if (!MultiInstanceHelpers.Start(true, server, clients))
{
Debug.LogError("Failed to start instances");
Assert.Fail("Failed to start instances");
}

yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnectedToServer(server));

Server = server;
Client = clients.SingleOrDefault();
}
}
}
#endif
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"name": "Unity.Multiplayer.MLAPI.RuntimeTests",
"rootNamespace": "",
"references": [
"Unity.Multiplayer.MLAPI.Runtime",
"Unity.Multiplayer.NetworkProfiler.Runtime",
"Unity.Multiplayer.NetStats",
Comment on lines +6 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't go into develop since we're going to be an optional dependency. Is the intent that this will be removed before we merge? Or, more preferably, we should begin wrapping all the relevant code in the optional dependency ifdefs now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this will be fixed once we figure out the networkprofiler package and the relevant ifdef.

I have a wacky "ifdef true" everywhere where we need it right now.

"Unity.Multiplayer.MLAPI.Editor"
],
"optionalUnityReferences": [
Expand Down