UnityGame/Library/PackageCache/com.unity.multiplayer.center/Tests/Editor/RecommenderSystemTests.cs

238 lines
12 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
using System;
using System.Linq;
using NUnit.Framework;
using Unity.Multiplayer.Center.Common;
using Unity.Multiplayer.Center.Questionnaire;
using Unity.Multiplayer.Center.Recommendations;
namespace Unity.MultiplayerCenterTests
{
/// <summary>
/// Basic test for the recommender system, without checking that the recommendation itself make sense.
/// </summary>
[TestFixture]
class RecommenderSystemUnitTests
{
const string k_NoNetcodeTitle = "No Netcode";
[SetUp]
[TearDown]
public void Cleanup()
{
UnityEngine.Object.DestroyImmediate(RecommenderSystemDataObject.instance); // force reload from disk if accessed
}
[Test]
public void TestEmptyQuestionnaireAndAnswer_ThrowsArgumentException()
{
var questionnaireData = new QuestionnaireData();
var answerData = new AnswerData();
// Having an empty questionnaire is an invalid state and should be caught.
Assert.Throws<ArgumentException>(() => RecommenderSystem.GetRecommendation(questionnaireData, answerData));
}
[Test]
public void TestEmptyQuestionnaire_ThrowsArgumentException()
{
var questionnaireData = new QuestionnaireData();
var answerData = UtilsForRecommendationTests.BuildAnswerMatching(UtilsForRecommendationTests.GetProjectQuestionnaire());
// Having an empty questionnaire is an invalid state and should be caught.
Assert.Throws<ArgumentException>(() => RecommenderSystem.GetRecommendation(questionnaireData, answerData));
}
[Test]
public void TestEmptyAnswer_ReturnsNull()
{
var questionnaireData = UtilsForRecommendationTests.GetProjectQuestionnaire();
var answerData = new AnswerData();
// Having empty answers is a normal state, where we don't have any recommendation to make.
var recommendation = RecommenderSystem.GetRecommendation(questionnaireData, answerData);
Assert.IsNull(recommendation);
}
[Test]
public void TestGetRecommendationForMatchingAnswers_NothingNull()
{
var questionnaireData = UtilsForRecommendationTests.GetProjectQuestionnaire();
var answerData = UtilsForRecommendationTests.BuildAnswerMatching(questionnaireData);
var recommendation = RecommenderSystem.GetRecommendation(questionnaireData, answerData);
Assert.NotNull(recommendation);
foreach (var solution in recommendation.NetcodeOptions)
{
if (solution.Solution is PossibleSolution.CustomNetcode or PossibleSolution.NoNetcode) continue;
UtilsForRecommendationTests.AssertRecommendedSolutionNotNull(solution);
}
foreach (var solution in recommendation.ServerArchitectureOptions)
{
UtilsForRecommendationTests.AssertRecommendedSolutionNotNull(solution, false);
}
}
[Test]
public void TestSolutionToPackageViewData_NothingNull()
{
var allPackages = RecommenderSystem.GetSolutionsToRecommendedPackageViewData();
UtilsForRecommendationTests.AssertAllRecommendedPackageNotNull(allPackages);
}
[Test]
public void TestGetRecommendationForMatchingAnswers_OnlyOneMainArchitecturePerCategory()
{
var questionnaireData = UtilsForRecommendationTests.GetProjectQuestionnaire();
var answerData = UtilsForRecommendationTests.BuildAnswerMatching(questionnaireData);
var recommendation = RecommenderSystem.GetRecommendation(questionnaireData, answerData);
AssertNoNetcodeIsTheLastNetcodeRecommendation(recommendation);
Assert.AreEqual(1, recommendation.NetcodeOptions.Count(x => x.RecommendationType == RecommendationType.MainArchitectureChoice));
Assert.AreEqual(1, recommendation.NetcodeOptions.Count(x => x.Selected));
Assert.AreEqual(1, recommendation.ServerArchitectureOptions.Count(x => x.RecommendationType == RecommendationType.MainArchitectureChoice));
Assert.AreEqual(1, recommendation.ServerArchitectureOptions.Count(x => x.Selected));
}
[Test]
public void TestGetSolutionsToRecommendedPackageViewData_AllSelectionsHaveSameCount()
{
var allPackages = RecommenderSystem.GetSolutionsToRecommendedPackageViewData();
var expectedCount = 16; // 4 netcode options * 4 hosting options
Assert.AreEqual(expectedCount, allPackages.Selections.Length);
PossibleSolution[] netcodeSolutions = {PossibleSolution.NGO, PossibleSolution.N4E, PossibleSolution.CustomNetcode, PossibleSolution.NoNetcode};
PossibleSolution[] hostingSolutions = {PossibleSolution.LS, PossibleSolution.DS, PossibleSolution.CloudCode, PossibleSolution.DA};
var referencePackageCount = allPackages.GetPackagesForSelection(PossibleSolution.NGO, PossibleSolution.LS).Length;
Assert.True(referencePackageCount > 0);
foreach (var netcode in netcodeSolutions)
{
foreach (var hosting in hostingSolutions)
{
var packages = allPackages.GetPackagesForSelection(netcode, hosting);
Assert.NotNull(packages);
Assert.AreEqual(referencePackageCount, packages.Length);
}
}
}
[TestCase(PossibleSolution.DS)]
[TestCase(PossibleSolution.LS)]
[TestCase(PossibleSolution.DA)]
[TestCase(PossibleSolution.CloudCode)]
public void RecommendationData_AllHostingOverridesExistInNetcodeData(PossibleSolution hostingModel)
{
var data = RecommenderSystemDataObject.instance.RecommenderSystemData;
var hostingOverrides = data.SolutionsByType[hostingModel].RecommendedPackages;
var netcodePackages = data.SolutionsByType[PossibleSolution.NGO].RecommendedPackages;
foreach (var package in hostingOverrides)
{
var index = Array.FindIndex(netcodePackages, p => p.PackageId == package.PackageId);
Assert.True(index > -1, $"Did not find package {package.PackageId} in Netcode packages for {hostingModel}");
}
}
[TestCase(PossibleSolution.NGO)]
[TestCase(PossibleSolution.N4E)]
[TestCase(PossibleSolution.CustomNetcode)]
[TestCase(PossibleSolution.NoNetcode)]
public void RecommendationData_NetcodeSolutionsHaveRecommendationDataForAllPackages(PossibleSolution netcode)
{
var data = RecommenderSystemDataObject.instance.RecommenderSystemData;
var packagesForNetcode = data.SolutionsByType[netcode].RecommendedPackages;
var solutionPackages = data.RecommendedSolutions.Where(e => e.MainPackageId != null).Select(e => e.MainPackageId).ToArray();
foreach (var package in data.Packages)
{
if(solutionPackages.Contains(package.Id))
continue;
var index = Array.FindIndex(packagesForNetcode, p => p.PackageId == package.Id);
Assert.True(index > -1, $"Did not find package {package.Id} in packages of {netcode}");
}
}
// Regression test: selecting back and forth between netcode options should not change whether some solutions
// are compatible or not
[Test]
public void TestAdaptRecommendationToNetcodeSelection_SelectBackAndForthDoesNotChangeRecommendations()
{
var recommendation = UtilsForRecommendationTests.ComputeRecommendationForPreset(Preset.Adventure);
var selectedNetcode = RecommendationUtils.GetSelectedNetcode(recommendation);
Assert.AreEqual(PossibleSolution.NGO, selectedNetcode.Solution, "Wrong test setup: expected NGO to be selected");
AssertCompatibilityInView(true, PossibleSolution.DA, recommendation.ServerArchitectureOptions, "Wrong test setup: expected DA to be compatible");
UtilsForRecommendationTests.SimulateSelectionChange(PossibleSolution.N4E, recommendation.NetcodeOptions);
RecommenderSystem.AdaptRecommendationToNetcodeSelection(recommendation);
Assert.AreEqual(PossibleSolution.N4E, RecommendationUtils.GetSelectedNetcode(recommendation).Solution, "Wrong test setup: expected N4E to be selected");
AssertCompatibilityInView(false, PossibleSolution.DA, recommendation.ServerArchitectureOptions,"Wrong test setup: expected DA to be incompatible with N4E");
UtilsForRecommendationTests.SimulateSelectionChange(PossibleSolution.NGO, recommendation.NetcodeOptions);
RecommenderSystem.AdaptRecommendationToNetcodeSelection(recommendation);
Assert.AreEqual(PossibleSolution.NGO, RecommendationUtils.GetSelectedNetcode(recommendation).Solution, "Wrong test setup: expected NGO to be selected");
AssertCompatibilityInView(true, PossibleSolution.DA, recommendation.ServerArchitectureOptions, "Fail: expected DA to still be compatible with NGO after selecting other solution");
}
[Test]
public void TestAdaptIncompatibility_AllValuesMatch()
{
bool ngoDaCompatibleBefore = AreCompatible(PossibleSolution.NGO, PossibleSolution.DA);
Assert.True(ngoDaCompatibleBefore, "Wrong test setup: expected NGO to be compatible with DA");
var ngoSolBefore = GetSolution(PossibleSolution.NGO);
Assert.AreEqual(-1, Array.FindIndex(ngoSolBefore.IncompatibleSolutions, s => s.Solution == PossibleSolution.DA), "Wrong test setup: expected DA to be in the incompatible list of NGO");
RecommenderSystemDataObject.instance.RecommenderSystemData.UpdateIncompatibility(PossibleSolution.NGO,
PossibleSolution.DA, false, "test");
bool ngoDaCompatibleAfter = AreCompatible(PossibleSolution.NGO, PossibleSolution.DA);
Assert.False(ngoDaCompatibleAfter);
var ngoSolAfter = GetSolution(PossibleSolution.NGO);
Assert.AreNotEqual(-1, Array.FindIndex(ngoSolAfter.IncompatibleSolutions, s => s.Solution == PossibleSolution.DA));
RecommenderSystemDataObject.instance.RecommenderSystemData.UpdateIncompatibility(PossibleSolution.NGO, PossibleSolution.DA, true);
bool ngoDaCompatibleEnd = AreCompatible(PossibleSolution.NGO, PossibleSolution.DA);
Assert.True(ngoDaCompatibleEnd, "Wrong test setup: expected NGO to be compatible with DA");
var ngoSolEnd = GetSolution(PossibleSolution.NGO);
Assert.AreEqual(-1, Array.FindIndex(ngoSolEnd.IncompatibleSolutions, s => s.Solution == PossibleSolution.DA));
}
static bool AreCompatible(PossibleSolution netcode, PossibleSolution hostingModel)
=> RecommenderSystemDataObject.instance.RecommenderSystemData.IsHostingModelCompatibleWithNetcode(
PossibleSolution.NGO, PossibleSolution.DA, out _);
static RecommendedSolution GetSolution(PossibleSolution type)
=> RecommenderSystemDataObject.instance.RecommenderSystemData.SolutionsByType[type];
static void AssertNoNetcodeIsTheLastNetcodeRecommendation(RecommendationViewData recommendation)
{
var netcodeSolutionCount = recommendation.NetcodeOptions.Length;
Assert.AreEqual(4, netcodeSolutionCount);
var lastSolution = recommendation.NetcodeOptions[netcodeSolutionCount - 1];
Assert.AreEqual(k_NoNetcodeTitle, lastSolution.Title);
Assert.False(lastSolution.Selected);
}
static void AssertCompatibilityInView(bool expectedCompatible, PossibleSolution hostingModel, RecommendedSolutionViewData[] hostingSolutions, string msg)
{
foreach (var hosting in hostingSolutions)
{
if(hosting.Solution == hostingModel)
{
if(expectedCompatible)
Assert.AreNotEqual(RecommendationType.Incompatible, hosting.RecommendationType, msg);
else
Assert.AreEqual(RecommendationType.Incompatible, hosting.RecommendationType, msg);
}
}
}
}
}