// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.IO; using System.Linq; using NUnit.Framework; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.IPC; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Visual.Navigation { [TestFixture] [Ignore("This test cannot be run headless, as it requires the game host running the nested game to have IPC bound.")] public partial class TestSceneInterProcessCommunication : OsuGameTestScene { private HeadlessGameHost ipcSenderHost = null!; private OsuSchemeLinkIPCChannel osuSchemeLinkIPCSender = null!; private ArchiveImportIPCChannel archiveImportIPCSender = null!; private const int requested_beatmap_set_id = 1; protected override TestOsuGame CreateTestGame() => new IpcGame(LocalStorage, API); [Resolved] private GameHost gameHost { get; set; } = null!; public override void SetUpSteps() { base.SetUpSteps(); AddStep("set up request handling", () => { ((DummyAPIAccess)API).HandleRequest = request => { switch (request) { case GetBeatmapSetRequest gbr: var apiBeatmapSet = CreateAPIBeatmapSet(); apiBeatmapSet.OnlineID = requested_beatmap_set_id; apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap { DifficultyName = "Target difficulty", OnlineID = 75, }).ToArray(); gbr.TriggerSuccess(apiBeatmapSet); return true; } return false; }; }); AddStep("create IPC sender channels", () => { ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true }); osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost); archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost); }); } [Test] public void TestOsuSchemeLinkIPCChannel() { AddStep("open beatmap via IPC", () => osuSchemeLinkIPCSender.HandleLinkAsync($@"osu://s/{requested_beatmap_set_id}").WaitSafely()); AddUntilStep("beatmap overlay displayed", () => Game.ChildrenOfType().FirstOrDefault()?.State.Value == Visibility.Visible); AddUntilStep("beatmap overlay showing content", () => Game.ChildrenOfType().FirstOrDefault()?.Header.BeatmapSet.Value.OnlineID == requested_beatmap_set_id); } [Test] public void TestArchiveImportLinkIPCChannel() { string? beatmapFilepath = null; AddStep("import beatmap via IPC", () => archiveImportIPCSender.ImportAsync(beatmapFilepath = TestResources.GetQuickTestBeatmapForImport()).WaitSafely()); AddUntilStep("import complete notification was presented", () => Game.Notifications.ChildrenOfType().Count(), () => Is.EqualTo(1)); AddAssert("original file deleted", () => File.Exists(beatmapFilepath), () => Is.False); } public override void TearDownSteps() { AddStep("dispose IPC senders", () => { osuSchemeLinkIPCSender.Dispose(); archiveImportIPCSender.Dispose(); ipcSenderHost.Dispose(); }); base.TearDownSteps(); } private partial class IpcGame : TestOsuGame { private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel; private ArchiveImportIPCChannel? archiveImportIPCChannel; public IpcGame(Storage storage, IAPIProvider api, string[]? args = null) : base(storage, api, args) { } protected override void LoadComplete() { base.LoadComplete(); osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this); archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this); } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); osuSchemeLinkIPCChannel?.Dispose(); archiveImportIPCChannel?.Dispose(); } } } }