diff --git a/source/Generic/GameEngineChecker/App.xaml b/source/Generic/GameEngineChecker/App.xaml new file mode 100644 index 0000000000..0e3490bb12 --- /dev/null +++ b/source/Generic/GameEngineChecker/App.xaml @@ -0,0 +1,12 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/Generic/GameEngineChecker/Views/ProgressView.xaml.cs b/source/Generic/GameEngineChecker/Views/ProgressView.xaml.cs new file mode 100644 index 0000000000..89928d8297 --- /dev/null +++ b/source/Generic/GameEngineChecker/Views/ProgressView.xaml.cs @@ -0,0 +1,17 @@ +using GameEngineChecker.ViewModels; +using System.Windows.Controls; + +namespace GameEngineChecker.Views +{ + /// + /// Interaction logic for ProgressView.xaml + /// + public partial class ProgressView : UserControl + { + public ProgressView(ProgressViewModel progressViewModel) + { + InitializeComponent(); + DataContext = progressViewModel; + } + } +} \ No newline at end of file diff --git a/source/Generic/GameEngineChecker/extension.yaml b/source/Generic/GameEngineChecker/extension.yaml index 8ae747bed3..12d8615d93 100644 --- a/source/Generic/GameEngineChecker/extension.yaml +++ b/source/Generic/GameEngineChecker/extension.yaml @@ -1,9 +1,9 @@ -Id: Game_Engine_Checker_7a21243e-c7cc-4ca7-85bd-f6f96f22e9db +Id: Game_Engine_Checker_7a21243e-c7cc-4ca7-85bd-f6f96f22e9db Name: Game Engine Checker Author: darklinkpower -Version: 2.13 -Module: GameEngineChecker.psm1 -Type: Script +Version: 3.0 +Module: GameEngineChecker.dll +Type: GenericPlugin Icon: icon.jpg Links: - Name: GitHub diff --git a/source/Generic/GameEngineChecker/packages.config b/source/Generic/GameEngineChecker/packages.config new file mode 100644 index 0000000000..9bc0f3f063 --- /dev/null +++ b/source/Generic/GameEngineChecker/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/GameEngineChecker.Tests.csproj b/source/GenericTests/GameEngineChecker.Tests/GameEngineChecker.Tests.csproj new file mode 100644 index 0000000000..804ebca0f5 --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/GameEngineChecker.Tests.csproj @@ -0,0 +1,118 @@ + + + + + + + + Debug + AnyCPU + {AC1FED92-44D4-4527-8DE9-AF4120DB3EF1} + Library + Properties + GameEngineChecker.Tests + GameEngineChecker.Tests + v4.6.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\Generic\GameEngineChecker\packages\AutoFixture.4.18.1\lib\net452\AutoFixture.dll + + + ..\..\Generic\GameEngineChecker\packages\AutoFixture.AutoFakeItEasy.4.18.1\lib\net462\AutoFixture.AutoFakeItEasy.dll + + + ..\..\Generic\GameEngineChecker\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll + + + ..\..\Generic\GameEngineChecker\packages\FakeItEasy.8.3.0\lib\net462\FakeItEasy.dll + + + ..\..\Generic\GameEngineChecker\packages\Fare.2.1.1\lib\net35\Fare.dll + + + ..\..\Generic\GameEngineChecker\packages\PlayniteSDK.6.15.0\lib\net462\Playnite.SDK.dll + + + + + + + ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + + + ..\..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll + + + ..\..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll + + + ..\..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll + + + + + + + + + + + + + + + + + + + + + + {4FDF1E89-5BC3-4C72-8FDA-0D580E7A5D5F} + GameEngineChecker + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/Properties/AssemblyInfo.cs b/source/GenericTests/GameEngineChecker.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e41ea80614 --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("GameEngineChecker.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GameEngineChecker.Tests")] +[assembly: AssemblyCopyright("Copyright © 2026")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("ac1fed92-44d4-4527-8de9-af4120db3ef1")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/source/GenericTests/GameEngineChecker.Tests/Services/EnginesParserTests.cs b/source/GenericTests/GameEngineChecker.Tests/Services/EnginesParserTests.cs new file mode 100644 index 0000000000..e12be3680b --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/Services/EnginesParserTests.cs @@ -0,0 +1,63 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using GameEngineChecker.Services; +using Xunit; + +namespace GameEngineChecker.Tests.Services +{ + public class EnginesParserTests + { + private readonly EnginesParser _sut; + + public EnginesParserTests() + { + var fixture = new Fixture(); + fixture.Customize(new AutoFakeItEasyCustomization()); + + _sut = fixture.Create(); + } + + [Fact] + public void Parse_ReturnsSingleEngine_WhenSingleEngine() + { + // Arrange + var engines = "Engine:Unity"; + + // Act + var parsedEngines = _sut.Parse(engines); + + // Assert + var engine = Assert.Single(parsedEngines); + Assert.Equal("Unity", engine); + } + + [Fact] + public void Parse_ReturnsTwoEngines_WhenTwoDifferentEngines() + { + // Arrange + var engines = "Engine:Unreal Engine 5,Engine:Gamebryo (TES Engine)"; + + // Act + var parsedEngines = _sut.Parse(engines); + + // Assert + Assert.Equal(2, parsedEngines.Count); + Assert.Contains("Unreal Engine 5", parsedEngines); + Assert.Contains("Gamebryo (TES Engine)", parsedEngines); + } + + [Fact] + public void Parse_ReturnsSingleEngine_WhenSeveralIdenticalEngines() + { + // Arrange + var engines = "Engine:Unity,Engine:Unity,Engine:Unity"; + + // Act + var parsedEngines = _sut.Parse(engines); + + // Assert + var engine = Assert.Single(parsedEngines); + Assert.Equal("Unity", engine); + } + } +} \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/Services/GameEngineCheckerServiceTests.cs b/source/GenericTests/GameEngineChecker.Tests/Services/GameEngineCheckerServiceTests.cs new file mode 100644 index 0000000000..5dd3571d66 --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/Services/GameEngineCheckerServiceTests.cs @@ -0,0 +1,92 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using FakeItEasy; +using Playnite.SDK.Models; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GameEngineChecker.Interfaces; +using GameEngineChecker.Services; +using Xunit; + +namespace GameEngineChecker.Tests.Services +{ + public class GameEngineCheckerServiceTests + { + private readonly Fixture _fixture; + private readonly GameEngineCheckerService _sut; + private readonly IGamesFilter _gamesFilter; + private readonly IPcGamingWikiLinkProvider _pcGamingWikiLinkProvider; + private readonly IPcGamingWikiClient _pcGamingWikiClient; + private readonly IEnginesParser _enginesParser; + private readonly ITagger _tagger; + + public GameEngineCheckerServiceTests() + { + _fixture = new Fixture(); + _fixture.Customize(new AutoFakeItEasyCustomization()); + + _gamesFilter = _fixture.Freeze(); + _pcGamingWikiLinkProvider = _fixture.Freeze(); + _pcGamingWikiClient = _fixture.Freeze(); + _enginesParser = _fixture.Freeze(); + _tagger = _fixture.Freeze(); + _sut = _fixture.Create(); + } + + [Fact] + public async Task AddGameEngineTags_AddsEngineTags_WhenTagsShouldBeAdded() + { + // Arrange + var game = _fixture.Create(); + var link = _fixture.Create(); + var engines = "Engine:Unity"; + var parsedEngines = new List { "Unity" }; + SetupSuccessfulRun(game, link, engines, parsedEngines); + + // Act + await _sut.AddGameEngineTags(new List { game }, x => { }, CancellationToken.None); + + // Assert + A.CallTo(() => _tagger.AddEngineTags(game, parsedEngines, CancellationToken.None)).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task AddGameEngineTags_DoesNotGenerateLink_WhenTagsShouldNotBeAdded() + { + // Arrange + var game = _fixture.Create(); + A.CallTo(() => _gamesFilter.ShouldTheGameBeProcessed(game)).Returns(false); + + // Act + await _sut.AddGameEngineTags(new List { game }, x => { }, CancellationToken.None); + + // Assert + A.CallTo(() => _pcGamingWikiLinkProvider.GetLink(A._, CancellationToken.None)).MustNotHaveHappened(); + } + + [Fact] + public async Task AddGameEngineTags_DoesNotCallPcGamingWiki_WhenLinkCouldNotBeGenerated() + { + // Arrange + var game = _fixture.Create(); + A.CallTo(() => _gamesFilter.ShouldTheGameBeProcessed(game)).Returns(true); + A.CallTo(() => _pcGamingWikiLinkProvider.GetLink(game, A._)).Returns(null); + + // Act + await _sut.AddGameEngineTags(new List { game }, x => { }, CancellationToken.None); + + // Assert + A.CallTo(() => _pcGamingWikiClient.GetEngines(A._, A._, A._)).MustNotHaveHappened(); + } + + private void SetupSuccessfulRun(Game game, Uri link, string engines, List parsedEngines) + { + A.CallTo(() => _gamesFilter.ShouldTheGameBeProcessed(game)).Returns(true); + A.CallTo(() => _pcGamingWikiLinkProvider.GetLink(game, A._)).Returns(link); + A.CallTo(() => _pcGamingWikiClient.GetEngines(link, A._, A._)).Returns(engines); + A.CallTo(() => _enginesParser.Parse(engines)).Returns(parsedEngines); + } + } +} \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/Services/GamesFilterTests.cs b/source/GenericTests/GameEngineChecker.Tests/Services/GamesFilterTests.cs new file mode 100644 index 0000000000..88c227a5c5 --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/Services/GamesFilterTests.cs @@ -0,0 +1,77 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using FakeItEasy; +using Playnite.SDK; +using Playnite.SDK.Models; +using System.Collections.Generic; +using GameEngineChecker.Services; +using Xunit; + +namespace GameEngineChecker.Tests.Services +{ + public class GamesFilterTests + { + private readonly Fixture _fixture; + private readonly GamesFilter _sut; + private readonly Tag _engineTag; + + public GamesFilterTests() + { + _fixture = new Fixture(); + _fixture.Customize(new AutoFakeItEasyCustomization()); + + var api = _fixture.Freeze(); + + var tags = new TestableItemCollection(new List()); + + _engineTag = _fixture.Create(); + _engineTag.Name = "[Engine] Unity"; + tags.Add(_engineTag); + + A.CallTo(() => api.Database.Tags).Returns(tags); + + _sut = _fixture.Create(); + } + + [Fact] + public void ShouldTheGameBeProcessed_ReturnsTrue_WhenGameHasNoEngineTags() + { + // Arrange + var game = _fixture.Create(); + + // Act + var result = _sut.ShouldTheGameBeProcessed(game); + + // Assert + Assert.True(result); + } + + [Fact] + public void ShouldTheGameBeProcessed_ReturnsTrue_WhenGameHasNoTags() + { + // Arrange + var game = _fixture.Create(); + game.TagIds = null; + + // Act + var result = _sut.ShouldTheGameBeProcessed(game); + + // Assert + Assert.True(result); + } + + [Fact] + public void ShouldTheGameBeProcessed_ReturnsFalse_WhenGameHasAnyEngineTag() + { + // Arrange + var game = _fixture.Create(); + game.TagIds.Add(_engineTag.Id); + + // Act + var result = _sut.ShouldTheGameBeProcessed(game); + + // Assert + Assert.False(result); + } + } +} \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/Services/PcGamingWikiLinkProviderTests.cs b/source/GenericTests/GameEngineChecker.Tests/Services/PcGamingWikiLinkProviderTests.cs new file mode 100644 index 0000000000..ad9f1a150e --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/Services/PcGamingWikiLinkProviderTests.cs @@ -0,0 +1,114 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using Playnite.SDK.Models; +using System; +using System.Threading; +using System.Threading.Tasks; +using GameEngineChecker.Services; +using Xunit; + +namespace GameEngineChecker.Tests.Services +{ + public class PcGamingWikiLinkProviderTests + { + private readonly Fixture _fixture; + private readonly PcGamingWikiLinkProvider _sut; + + public PcGamingWikiLinkProviderTests() + { + _fixture = new Fixture(); + _fixture.Customize(new AutoFakeItEasyCustomization()); + + _sut = _fixture.Create(); + } + + [Fact] + public async Task GetLink_UseGameId_WhenGameIsSteamGame() + { + // Arrange + var game = _fixture.Create(); + game.PluginId = Guid.Parse("CB91DFC9-B977-43BF-8E70-55F46E410FAB"); + + // Act + var result = await _sut.GetLink(game, CancellationToken.None); + + // Assert + Assert.Equal(new Uri($@"https://www.pcgamingwiki.com/w/api.php?action=cargoquery&format=json&tables=Infobox_game&fields=Engines,_pageName=title&where=Steam_AppID HOLDS ""{game.GameId}"""), result); + } + + [Theory] + [InlineData("AEBE8B7C-6DC3-4A66-AF31-E7375C6B5E9E")] // GOG + [InlineData("03689811-3F33-4DFB-A121-2EE168FB9A5C")] // GOG OSS + public async Task GetLink_UseGameId_WhenGameIsGogGame(string pluginId) + { + // Arrange + var game = _fixture.Create(); + game.PluginId = Guid.Parse(pluginId); + + // Act + var result = await _sut.GetLink(game, CancellationToken.None); + + // Assert + Assert.Equal(new Uri($@"https://www.pcgamingwiki.com/w/api.php?action=cargoquery&format=json&tables=Infobox_game&fields=Engines,_pageName=title&where=GOGcom_ID HOLDS ""{game.GameId}"""), result); + } + + [Theory] + [InlineData("Steam", "https://store.steampowered.com/app/3634520/Samson")] + [InlineData("LINK!", "store.steampowered.com/app/3634520")] + public async Task GetLink_UseSteamLink_WhenGameIsNonSteamNonGogGameAndHasSteamLink(string name, string url) + { + // Arrange + var game = _fixture.Create(); + game.Links.Add(new Link(name, url)); + + // Act + var result = await _sut.GetLink(game, CancellationToken.None); + + // Assert + Assert.Equal(new Uri($@"https://www.pcgamingwiki.com/w/api.php?action=cargoquery&format=json&tables=Infobox_game&fields=Engines,_pageName=title&where=Steam_AppID HOLDS ""3634520"""), result); + } + + [Theory] + [InlineData("Wikipedia", "https://en.wikipedia.org/wiki/Need_for_Speed_III:_Hot_Pursuit")] + [InlineData("LINK!", "wikipedia.org/wiki/Need_for_Speed_III:_Hot_Pursuit")] + public async Task GetLink_UseWikipediaLink_WhenGameIsNonSteamNonGogGameAndHasWikipediaLink(string name, string url) + { + // Arrange + var game = _fixture.Create(); + game.Links.Add(new Link(name, url)); + + // Act + var result = await _sut.GetLink(game, CancellationToken.None); + + // Assert + Assert.Equal(new Uri($@"https://www.pcgamingwiki.com/w/api.php?action=cargoquery&format=json&tables=Infobox_game&fields=Engines,_pageName=title&where=Wikipedia=""Need for Speed III: Hot Pursuit"""), result); + } + + [Fact] + public async Task GetLink_ReturnNull_WhenGameIsNonSteamNonGogAndHasNoLinks() + { + // Arrange + var game = _fixture.Create(); + game.Links = null; + + // Act + var result = await _sut.GetLink(game, CancellationToken.None); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task GetLink_ReturnNull_WhenGameLinkNotGenerated() + { + // Arrange + var game = _fixture.Create(); + + // Act + var result = await _sut.GetLink(game, CancellationToken.None); + + // Assert + Assert.Null(result); + } + } +} \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/Services/RateLimiterTests.cs b/source/GenericTests/GameEngineChecker.Tests/Services/RateLimiterTests.cs new file mode 100644 index 0000000000..9dde560e12 --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/Services/RateLimiterTests.cs @@ -0,0 +1,63 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using GameEngineChecker.Services; +using Xunit; + +namespace GameEngineChecker.Tests.Services +{ + public class RateLimiterTests + { + [Fact] + public async Task Limit_Bursts_WhenBatchIsSmallerThanLimitPerWindow() + { + // Arrange + var expected = 19; + var actual = 0; + var cts = new CancellationTokenSource(200); + + // Act + _ = await Record.ExceptionAsync(() => Task.Run(async () => + { + var sut = new RateLimiter(TimeSpan.FromMilliseconds(250), expected); + while (true) + { + await sut.Limit(expected, cts.Token); + actual++; + } + }, cts.Token)); + + // Assert + + Assert.Equal(expected, actual); + } + + [Fact] + public async Task Limit_Steady_WhenBatchIsHigherThanLimitPerWindow() + { + // Arrange + var batchSize = 4; + var maxRequestsPerWindow = 2; + var windowMilliseconds = 50; + + var cts = new CancellationTokenSource(500); + + // Act + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await Task.Run(async () => + { + var sut = new RateLimiter(TimeSpan.FromMilliseconds(windowMilliseconds), maxRequestsPerWindow); + for (var i = 0; i < batchSize; i++) + { + await sut.Limit(batchSize, cts.Token); + } + }, cts.Token); + stopwatch.Stop(); + + // Assert + Assert.True(stopwatch.ElapsedMilliseconds > windowMilliseconds * batchSize / maxRequestsPerWindow); + } + } +} \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/Services/TaggerTests.cs b/source/GenericTests/GameEngineChecker.Tests/Services/TaggerTests.cs new file mode 100644 index 0000000000..71370e0ebd --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/Services/TaggerTests.cs @@ -0,0 +1,100 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using FakeItEasy; +using Playnite.SDK; +using Playnite.SDK.Models; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GameEngineChecker.Services; +using Xunit; + +namespace GameEngineChecker.Tests.Services +{ + public class TaggerTests + { + private readonly Fixture _fixture; + private readonly IPlayniteAPI _api; + private readonly Tagger _sut; + private readonly TestableItemCollection _tags; + + public TaggerTests() + { + _fixture = new Fixture(); + _fixture.Customize(new AutoFakeItEasyCustomization()); + + _api = _fixture.Freeze(); + _sut = _fixture.Create(); + + _tags = new TestableItemCollection(new List()); + + A.CallTo(() => _api.Database.Tags).Returns(_tags); + } + + [Fact] + public void AddEngineTags_AddsNewTagAndUpdatesTheGame_WhenTagDoesNotExist() + { + // Arrange + var game = _fixture.Create(); + var engines = new List { "Unity" }; + + // Act + _sut.AddEngineTags(game, engines, CancellationToken.None); + + // Assert + var tag = Assert.Single(_api.Database.Tags); + Assert.Equal("[Engine] Unity", tag.Name); + Assert.Contains(tag.Id, game.TagIds); + } + + [Fact] + public void AddEngineTags_AddsNewTagAndUpdatesTheGame_WhenGameHasNoTags() + { + // Arrange + var game = _fixture.Create(); + var engines = new List { "Unity" }; + game.TagIds = null; + + // Act + _sut.AddEngineTags(game, engines, CancellationToken.None); + + // Assert + var tag = Assert.Single(_api.Database.Tags); + Assert.Equal("[Engine] Unity", tag.Name); + Assert.Contains(tag.Id, game.TagIds); + } + + [Fact] + public void AddEngineTags_DoesNotAddTheTagToTheGame_WhenGameHasTheTag() + { + // Arrange + var game = _fixture.Create(); + var engines = new List { "Unity" }; + var tagOnGame = _tags.Add("[Engine] Unity"); + game.TagIds.Add(tagOnGame.Id); + + // Act + _sut.AddEngineTags(game, engines, CancellationToken.None); + + // Assert + Assert.Single(game.TagIds.Where(x => x == tagOnGame.Id)); + } + + [Fact] + public void AddEngineTags_PreventsRaceCondition_WhenProcessingInParallel() + { + // Arrange + var game = _fixture.Create(); + var engines = new List { "Unity" }; + + // Act + Parallel.For(0, 2, x => _sut.AddEngineTags(game, engines, CancellationToken.None)); + + // Assert + var tag = Assert.Single(_api.Database.Tags); + Assert.Equal("[Engine] Unity", tag.Name); + Assert.Contains(tag.Id, game.TagIds); + } + } +} \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/TestableItemCollection.cs b/source/GenericTests/GameEngineChecker.Tests/TestableItemCollection.cs new file mode 100644 index 0000000000..cf81f573c8 --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/TestableItemCollection.cs @@ -0,0 +1,190 @@ +using Playnite.SDK; +using Playnite.SDK.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace GameEngineChecker.Tests +{ + internal class TestableItemCollection : IItemCollection where T : DatabaseObject, new() + { + private List _items; + + public TestableItemCollection(List items) + { + _items = items; + } + + public int UpdateCount { get; private set; } + + public void Dispose() + { + throw new NotImplementedException(); + } + + public bool ContainsItem(Guid id) + { + throw new NotImplementedException(); + } + + public GameDatabaseCollection CollectionType { get; } + + public IEnumerator GetEnumerator() + { + return _items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(T item) + { + _items.Add(item); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(T item) + { + throw new NotImplementedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool Remove(T item) + { + throw new NotImplementedException(); + } + + public int Count { get; } + public bool IsReadOnly { get; } + + public T Get(Guid id) + { + throw new NotImplementedException(); + } + + public List Get(IList ids) + { + throw new NotImplementedException(); + } + + public T Add(string itemName) + { + var alreadyExists = _items.FirstOrDefault(x => x.Name == itemName); + if (alreadyExists != null) + { + return alreadyExists; + } + + var toAdd = new T() + { + Id = Guid.NewGuid(), + Name = itemName, + }; + + _items.Add(toAdd); + return toAdd; + } + + public T Add(string itemName, Func existingComparer) + { + throw new NotImplementedException(); + } + + public IEnumerable Add(List items) + { + var alreadyExists = _items.Where(x => items.Contains(x.Name)).ToList(); + + var toAdd = items + .Except(alreadyExists.Select(x => x.Name)) + .Select(x => new T() + { + Id = Guid.NewGuid(), + Name = x, + }).ToList(); + + _items.AddRange(toAdd); + return toAdd.Concat(alreadyExists); + } + + public T Add(MetadataProperty property) + { + throw new NotImplementedException(); + } + + public IEnumerable Add(IEnumerable properties) + { + throw new NotImplementedException(); + } + + public IEnumerable Add(List items, Func existingComparer) + { + throw new NotImplementedException(); + } + + public void Add(IEnumerable items) + { + throw new NotImplementedException(); + } + + public bool Remove(Guid id) + { + throw new NotImplementedException(); + } + + public bool Remove(IEnumerable items) + { + throw new NotImplementedException(); + } + + public void Update(T item) + { + UpdateCount++; + } + + public void Update(IEnumerable items) + { + throw new NotImplementedException(); + } + + public IDisposable BufferedUpdate() + { + throw new NotImplementedException(); + } + + public void BeginBufferUpdate() + { + throw new NotImplementedException(); + } + + public void EndBufferUpdate() + { + throw new NotImplementedException(); + } + + public IEnumerable GetClone() + { + throw new NotImplementedException(); + } + + public T this[Guid id] + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public event EventHandler> ItemCollectionChanged; + + public event EventHandler> ItemUpdated; + } +} \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/app.config b/source/GenericTests/GameEngineChecker.Tests/app.config new file mode 100644 index 0000000000..900564731f --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/source/GenericTests/GameEngineChecker.Tests/packages.config b/source/GenericTests/GameEngineChecker.Tests/packages.config new file mode 100644 index 0000000000..6767c87718 --- /dev/null +++ b/source/GenericTests/GameEngineChecker.Tests/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/PlayniteExtensions.sln b/source/PlayniteExtensions.sln index a475eaa22c..a9fbcecc82 100644 --- a/source/PlayniteExtensions.sln +++ b/source/PlayniteExtensions.sln @@ -127,6 +127,9 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ThrottlerSharp", "Common\Th EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "WebViewCore", "Common\WebViewCore\WebViewCore.shproj", "{5FBF6DC4-DFDB-4A07-8E73-0EF038F19E16}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameEngineChecker.Tests", "GenericTests\GameEngineChecker.Tests\GameEngineChecker.Tests.csproj", "{AC1FED92-44D4-4527-8DE9-AF4120DB3EF1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameEngineChecker", "Generic\GameEngineChecker\GameEngineChecker.csproj", "{CA1D0577-BE3D-422D-808F-262D77A627DF}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisplayHelper.Tests", "DisplayHelper.Tests\DisplayHelper.Tests.csproj", "{76E46611-35FE-4309-B60C-3FD897061BC7}" EndProject Global @@ -307,6 +310,14 @@ Global {27F33F55-C75D-4FCD-90C3-13E7DAA5B67E}.Debug|Any CPU.Build.0 = Debug|Any CPU {27F33F55-C75D-4FCD-90C3-13E7DAA5B67E}.Release|Any CPU.ActiveCfg = Release|Any CPU {27F33F55-C75D-4FCD-90C3-13E7DAA5B67E}.Release|Any CPU.Build.0 = Release|Any CPU + {AC1FED92-44D4-4527-8DE9-AF4120DB3EF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC1FED92-44D4-4527-8DE9-AF4120DB3EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC1FED92-44D4-4527-8DE9-AF4120DB3EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC1FED92-44D4-4527-8DE9-AF4120DB3EF1}.Release|Any CPU.Build.0 = Release|Any CPU + {CA1D0577-BE3D-422D-808F-262D77A627DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA1D0577-BE3D-422D-808F-262D77A627DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA1D0577-BE3D-422D-808F-262D77A627DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA1D0577-BE3D-422D-808F-262D77A627DF}.Release|Any CPU.Build.0 = Release|Any CPU {76E46611-35FE-4309-B60C-3FD897061BC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {76E46611-35FE-4309-B60C-3FD897061BC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {76E46611-35FE-4309-B60C-3FD897061BC7}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -372,6 +383,8 @@ Global {A280169A-8BA9-473D-AEED-30502E510BD6} = {82DA706F-BCA9-4D05-912F-E23A589A66C8} {B3A1FEFC-8C7F-4AA7-9993-B1D4F51222A9} = {82DA706F-BCA9-4D05-912F-E23A589A66C8} {5FBF6DC4-DFDB-4A07-8E73-0EF038F19E16} = {82DA706F-BCA9-4D05-912F-E23A589A66C8} + {AC1FED92-44D4-4527-8DE9-AF4120DB3EF1} = {A94A7FEF-3AA0-42E2-BA9F-A32BD1623476} + {CA1D0577-BE3D-422D-808F-262D77A627DF} = {0B392A79-5131-438C-BDB2-AEF170C06F60} {76E46611-35FE-4309-B60C-3FD897061BC7} = {A94A7FEF-3AA0-42E2-BA9F-A32BD1623476} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution