using System; using System.Linq; using NUnit.Framework; using Unity.Multiplayer.Center.Common; using Unity.Multiplayer.Center.Questionnaire; using Unity.Multiplayer.Center.Recommendations; using UnityEngine; namespace Unity.MultiplayerCenterTests { internal static class UtilsForRecommendationTests { public static QuestionnaireData GetProjectQuestionnaire() { // TODO: maybe accessing from disk is a better idea than using the singleton? (side effects) var result = Clone(QuestionnaireObject.instance.Questionnaire); // sanity checks Assert.IsNotNull(result); Assert.IsNotNull(result.Questions); Assert.Greater(result.Questions.Length, 0); Assert.False(result.Questions.Any(x => x.Choices.Length == 0)); return result; } public static AnswerData BuildAnswerMatching(QuestionnaireData questionnaireData) { var answerData = new AnswerData(); foreach (var question in questionnaireData.Questions) { var middleChoice = question.Choices[question.Choices.Length / 2 - 1]; var answeredQuestion = new AnsweredQuestion() { QuestionId = question.Id, Answers = new(){middleChoice.Id} }; Logic.Update(answerData, answeredQuestion); } return answerData; } public static RecommendationViewData GetSomeRecommendation() { var questionnaireData = GetProjectQuestionnaire(); var answerData = BuildAnswerMatching(questionnaireData); return RecommenderSystem.GetRecommendation(questionnaireData, answerData); } public static void AssertRecommendedSolutionNotNull(RecommendedSolutionViewData solution, bool checkMainPackage = true) { Assert.NotNull(solution); Assert.False(string.IsNullOrEmpty(solution.Title)); Assert.That(solution.RecommendationType is RecommendationType.MainArchitectureChoice or RecommendationType.SecondArchitectureChoice or RecommendationType.NotRecommended or RecommendationType.Incompatible, $"Recommendation type: {solution.RecommendationType}"); if (checkMainPackage) Assert.NotNull(solution.MainPackage); } public static void AssertAllRecommendedPackageNotNull(SolutionsToRecommendedPackageViewData allPackages) { foreach (var selection in allPackages.Selections) { var packages = allPackages.GetPackagesForSelection(selection); var selectionString = $"Netcode {selection.Netcode} - Hosting {selection.HostingModel}"; Assert.NotNull(packages, selectionString); CollectionAssert.IsNotEmpty(packages, selectionString); foreach (var package in packages) { Assert.False(string.IsNullOrEmpty(package.Name), selectionString); Assert.NotNull(string.IsNullOrEmpty(package.PackageId), $"{selectionString} - {package.Name}"); Assert.That(package.RecommendationType != RecommendationType.MainArchitectureChoice && package.RecommendationType != RecommendationType.SecondArchitectureChoice, $"{selectionString} - {package.Name}"); } } } /// /// All solutions should mention all packages, however the actual recommendations (RecommendationType) should be different. /// This checks that all packages in solution 1 are also in solution 2, but that they don't all have the same recommendation. /// Note: this a current requirement, but this might evolve. Adapt if necessary. /// 2nd Note: With the introduction of the Multiplayer SDK this requirement changed for the server architecture options, but not for the netcode options. /// /// Solution 1 /// Solution 2 public static void AssertSamePackagesWithDifferentRecommendations(RecommendedPackageViewData[] reco1, RecommendedPackageViewData[] reco2, string msg) { Assert.AreEqual(reco1.Length, reco2.Length); var allTheSame = true; foreach (var p in reco1) { var matching = reco2.FirstOrDefault(x => x.PackageId == p.PackageId); Assert.IsNotNull(matching); if(matching.RecommendationType != p.RecommendationType) allTheSame = false; } Assert.False(allTheSame, $"The two solutions have exactly the same recommended packages!"); } public static T Clone(T obj) { return JsonUtility.FromJson(JsonUtility.ToJson(obj)); } public static RecommendationViewData ComputeRecommendationForPreset(Preset preset, string playerCount = "4") { var questionnaireData = UtilsForRecommendationTests.GetProjectQuestionnaire(); var answerData = GetAnswerDataForPreset(questionnaireData, preset, playerCount); var recommendation = RecommenderSystem.GetRecommendation(questionnaireData, answerData); return recommendation; } public static AnswerData GetAnswerDataForPreset(QuestionnaireData questionnaireData, Preset preset, string playerCount) { if (preset == Preset.None) return new AnswerData(); var indexOfPreset = Array.IndexOf(questionnaireData.PresetData.Presets, preset); var matchingAnswers = questionnaireData.PresetData.Answers[indexOfPreset].Clone(); var playerCountAnswer = new AnsweredQuestion() { QuestionId = "PlayerCount", Answers = new() {playerCount} }; Logic.Update(matchingAnswers, playerCountAnswer); return matchingAnswers; } public static void SimulateSelectionChange(PossibleSolution newSelectedSolution, RecommendedSolutionViewData[] solutionViewDatas) { foreach (var solution in solutionViewDatas) { solution.Selected = solution.Solution == newSelectedSolution; if(solution.Selected && solution.RecommendationType == RecommendationType.Incompatible) Assert.Fail("Selected incompatible solution"); } } } }