From 912bfa1cbc4e90a3476a0804ead69f80c668220f Mon Sep 17 00:00:00 2001 From: jbe2277 Date: Fri, 8 Dec 2023 20:06:03 +0100 Subject: [PATCH] NR Migrate unit tests to xunit; add unit test project for applications layer; minor code improvements --- src/NewsReader/Directory.Build.props | 4 +- .../Controllers/AppControllerTest.cs | 21 +++++ .../MockPresentationModule.cs | 29 ++++++ .../NewsReader.Applications.Test.csproj | 15 +++ .../Services/MockAppInfoService.cs | 10 ++ .../Services/MockDataService.cs | 26 +++++ .../Services/MockLauncherService.cs | 7 ++ .../Services/MockMessageService.cs | 16 ++++ .../Services/MockNetworkInfoService.cs | 10 ++ .../Services/MockSyndicationService.cs | 11 +++ .../Services/MockWebStorageService.cs | 32 +++++++ .../NewsReader.Applications.Test/TestBase.cs | 42 +++++++++ .../Views/MockFeedView .cs | 8 ++ .../Views/MockSettingsView.cs | 8 ++ .../Views/MockShellView.cs | 31 ++++++ .../NewsReader.Applications.csproj | 2 + .../NewsReader.Domain.Test/FeedItemTest.cs | 42 ++++----- .../NewsReader.Domain.Test/FeedManagerTest.cs | 74 +++++++-------- .../NewsReader.Domain.Test/FeedTest.cs | 94 +++++++++---------- .../NewsReader.Domain.Test.csproj | 2 +- .../UnitTesting/DomainTest.cs | 25 ----- .../NewsReader.Domain.csproj | 2 +- .../Converters/NullToBoolConverterTest.cs | 15 ++- .../NewsReader.Presentation.Test.csproj | 2 +- .../LocalizeDisplayMaxItemsLimitConverter.cs | 2 +- .../Services/AppTraceListener.cs | 2 +- .../Services/NetworkInfoService.cs | 6 +- .../Services/WebStorageService.cs | 4 +- src/NewsReader/NewsReader.sln | 6 ++ 29 files changed, 393 insertions(+), 155 deletions(-) create mode 100644 src/NewsReader/NewsReader.Applications.Test/Controllers/AppControllerTest.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/MockPresentationModule.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/NewsReader.Applications.Test.csproj create mode 100644 src/NewsReader/NewsReader.Applications.Test/Services/MockAppInfoService.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Services/MockDataService.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Services/MockLauncherService.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Services/MockMessageService.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Services/MockNetworkInfoService.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Services/MockSyndicationService.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Services/MockWebStorageService.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/TestBase.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Views/MockFeedView .cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Views/MockSettingsView.cs create mode 100644 src/NewsReader/NewsReader.Applications.Test/Views/MockShellView.cs delete mode 100644 src/NewsReader/NewsReader.Domain.Test/UnitTesting/DomainTest.cs diff --git a/src/NewsReader/Directory.Build.props b/src/NewsReader/Directory.Build.props index 0864a87b..bfd75209 100644 --- a/src/NewsReader/Directory.Build.props +++ b/src/NewsReader/Directory.Build.props @@ -16,8 +16,8 @@ - - + + diff --git a/src/NewsReader/NewsReader.Applications.Test/Controllers/AppControllerTest.cs b/src/NewsReader/NewsReader.Applications.Test/Controllers/AppControllerTest.cs new file mode 100644 index 00000000..0c57e205 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Controllers/AppControllerTest.cs @@ -0,0 +1,21 @@ +using Test.NewsReader.Applications.Views; +using Waf.NewsReader.Applications.ViewModels; +using Xunit; + +namespace Test.NewsReader.Applications.Controllers; + +public class AppControllerTest : TestBase +{ + [Fact] + public async Task OpenSettingsView() + { + StartApp(); + await Task.Delay(50); // TODO: Waiting for some time is not the best way to solve this + var feed = Shell.View.GetCurrentView(); + Assert.Equal(FeedsController.FeedManager.Feeds.Single(), feed.ViewModel.Feed); + + Shell.ViewModel.SelectedFooterMenu = Shell.ViewModel.FooterMenu[1]; + var settings = Shell.View.GetCurrentView(); + Assert.NotNull(settings.View); + } +} diff --git a/src/NewsReader/NewsReader.Applications.Test/MockPresentationModule.cs b/src/NewsReader/NewsReader.Applications.Test/MockPresentationModule.cs new file mode 100644 index 00000000..9f938ec1 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/MockPresentationModule.cs @@ -0,0 +1,29 @@ +using Autofac; +using System.Waf.Applications.Services; +using System.Waf.UnitTesting.Mocks; +using Test.NewsReader.Applications.Services; +using Test.NewsReader.Applications.Views; +using Waf.NewsReader.Applications.Services; +using Waf.NewsReader.Applications.Views; + +namespace Test.NewsReader.Applications; + +public class MockPresentationModule : Module +{ + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + builder.RegisterType().AsSelf().As().SingleInstance(); + } +} + diff --git a/src/NewsReader/NewsReader.Applications.Test/NewsReader.Applications.Test.csproj b/src/NewsReader/NewsReader.Applications.Test/NewsReader.Applications.Test.csproj new file mode 100644 index 00000000..1eb4b65c --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/NewsReader.Applications.Test.csproj @@ -0,0 +1,15 @@ + + + net8.0 + Test.NewsReader.Applications + Test.NewsReader.Applications + + + + + + + + + + diff --git a/src/NewsReader/NewsReader.Applications.Test/Services/MockAppInfoService.cs b/src/NewsReader/NewsReader.Applications.Test/Services/MockAppInfoService.cs new file mode 100644 index 00000000..6c87832e --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Services/MockAppInfoService.cs @@ -0,0 +1,10 @@ +using Waf.NewsReader.Applications.Services; + +namespace Test.NewsReader.Applications.Services; + +internal class MockAppInfoService : IAppInfoService +{ + public string AppName => "TestApp"; + + public string VersionString => "1.2.3"; +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Services/MockDataService.cs b/src/NewsReader/NewsReader.Applications.Test/Services/MockDataService.cs new file mode 100644 index 00000000..a1e28a84 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Services/MockDataService.cs @@ -0,0 +1,26 @@ +using Waf.NewsReader.Applications.Services; + +namespace Test.NewsReader.Applications.Services; + +public class MockDataService : IDataService +{ + public string GetHash() + { + throw new NotImplementedException(); + } + + public Stream GetReadStream() + { + throw new NotImplementedException(); + } + + public T? Load(Stream? dataStream = null) where T : class + { + throw new NotImplementedException(); + } + + public void Save(object data) + { + throw new NotImplementedException(); + } +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Services/MockLauncherService.cs b/src/NewsReader/NewsReader.Applications.Test/Services/MockLauncherService.cs new file mode 100644 index 00000000..c25d6cc8 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Services/MockLauncherService.cs @@ -0,0 +1,7 @@ +using Waf.NewsReader.Applications.Services; + +namespace Test.NewsReader.Applications.Services; +public class MockLauncherService : ILauncherService +{ + public Task LaunchBrowser(Uri uri) => throw new NotImplementedException(); +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Services/MockMessageService.cs b/src/NewsReader/NewsReader.Applications.Test/Services/MockMessageService.cs new file mode 100644 index 00000000..02265a0b --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Services/MockMessageService.cs @@ -0,0 +1,16 @@ +using Waf.NewsReader.Applications.Services; + +namespace Test.NewsReader.Applications.Services; + +public class MockMessageService : IMessageService +{ + public Task ShowMessage(string message) + { + throw new NotImplementedException(); + } + + public Task ShowYesNoQuestion(string message) + { + throw new NotImplementedException(); + } +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Services/MockNetworkInfoService.cs b/src/NewsReader/NewsReader.Applications.Test/Services/MockNetworkInfoService.cs new file mode 100644 index 00000000..c3b5ceef --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Services/MockNetworkInfoService.cs @@ -0,0 +1,10 @@ +using Waf.NewsReader.Applications.Services; + +namespace Test.NewsReader.Applications.Services; + +public class MockNetworkInfoService : Model, INetworkInfoService +{ + private bool internetAccess; + + public bool InternetAccess { get => internetAccess; set => SetProperty(ref internetAccess, value); } +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Services/MockSyndicationService.cs b/src/NewsReader/NewsReader.Applications.Test/Services/MockSyndicationService.cs new file mode 100644 index 00000000..8ea66560 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Services/MockSyndicationService.cs @@ -0,0 +1,11 @@ +using Waf.NewsReader.Applications.Services; + +namespace Test.NewsReader.Applications.Services; + +public class MockSyndicationService : ISyndicationService +{ + public Task RetrieveFeed(Uri uri) + { + throw new NotImplementedException(); + } +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Services/MockWebStorageService.cs b/src/NewsReader/NewsReader.Applications.Test/Services/MockWebStorageService.cs new file mode 100644 index 00000000..848f0256 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Services/MockWebStorageService.cs @@ -0,0 +1,32 @@ +using Waf.NewsReader.Applications.Services; + +namespace Test.NewsReader.Applications.Services; + +public class MockWebStorageService : Model, IWebStorageService +{ + private UserAccount? currentAccount; + + public UserAccount? CurrentAccount { get => currentAccount; set => SetProperty(ref currentAccount, value); } + + public Task<(Stream? stream, string? cTag)> DownloadFile(string? cTag) + { + throw new NotImplementedException(); + } + + public Task SignIn() + { + throw new NotImplementedException(); + } + + public Task SignOut() + { + throw new NotImplementedException(); + } + + public Task TrySilentSignIn() => Task.FromResult(false); + + public Task UploadFile(Stream source) + { + throw new NotImplementedException(); + } +} diff --git a/src/NewsReader/NewsReader.Applications.Test/TestBase.cs b/src/NewsReader/NewsReader.Applications.Test/TestBase.cs new file mode 100644 index 00000000..f42a4139 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/TestBase.cs @@ -0,0 +1,42 @@ +using Autofac; +using System.Waf.Applications; +using Waf.NewsReader.Applications.ViewModels; +using Waf.NewsReader.Applications; +using Waf.NewsReader.Applications.Controllers; +using Test.NewsReader.Applications.Views; + +namespace Test.NewsReader.Applications; + +public record ViewPair(TView View) where TView : IView where TViewModel : IViewModelCore +{ + public TViewModel ViewModel => (TViewModel)View.DataContext!; +} + +public abstract class TestBase +{ + protected TestBase() + { + var builder = new ContainerBuilder(); + builder.RegisterModule(new ApplicationsModule()); + builder.RegisterModule(new MockPresentationModule()); + Container = builder.Build(); + } + + public Autofac.IContainer Container { get; } + + internal AppController AppController { get; private set; } = null!; + internal FeedsController FeedsController { get; private set; } = null!; + internal SettingsController SettingsController { get; private set; } = null!; + public ViewPair Shell { get; private set; } = null!; + + public T Resolve() where T : notnull => Container.Resolve(); + + public void StartApp() + { + AppController = Resolve(); + FeedsController = Resolve(); + SettingsController = Resolve(); + AppController.Start(); + Shell = new((MockShellView)AppController.MainView); + } +} \ No newline at end of file diff --git a/src/NewsReader/NewsReader.Applications.Test/Views/MockFeedView .cs b/src/NewsReader/NewsReader.Applications.Test/Views/MockFeedView .cs new file mode 100644 index 00000000..c2cd0f72 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Views/MockFeedView .cs @@ -0,0 +1,8 @@ +using System.Waf.UnitTesting.Mocks; +using Waf.NewsReader.Applications.Views; + +namespace Test.NewsReader.Applications.Views; + +public class MockFeedView : MockView, IFeedView +{ +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Views/MockSettingsView.cs b/src/NewsReader/NewsReader.Applications.Test/Views/MockSettingsView.cs new file mode 100644 index 00000000..6477f2fa --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Views/MockSettingsView.cs @@ -0,0 +1,8 @@ +using System.Waf.UnitTesting.Mocks; +using Waf.NewsReader.Applications.Views; + +namespace Test.NewsReader.Applications.Views; + +public class MockSettingsView : MockView, ISettingsView +{ +} diff --git a/src/NewsReader/NewsReader.Applications.Test/Views/MockShellView.cs b/src/NewsReader/NewsReader.Applications.Test/Views/MockShellView.cs new file mode 100644 index 00000000..5a6989a6 --- /dev/null +++ b/src/NewsReader/NewsReader.Applications.Test/Views/MockShellView.cs @@ -0,0 +1,31 @@ +using System.Waf.Applications; +using System.Waf.UnitTesting.Mocks; +using Waf.NewsReader.Applications.ViewModels; +using Waf.NewsReader.Applications.Views; + +namespace Test.NewsReader.Applications.Views; + +public class MockShellView : MockView, IShellView +{ + private readonly Stack pageStack = new(); + private object currentPage = null!; + + public object CurrentPage + { + get => currentPage; + private set + { + currentPage = value; + ((ShellViewModel)DataContext!).InternalSetCurrentPage(CurrentPage); + } + } + + public Task PushAsync(object page) { pageStack.Push(page); CurrentPage = page; return Task.CompletedTask; } + + public Task PopAsync() { CurrentPage = pageStack.Pop(); return Task.CompletedTask; } + + public void CloseFlyout() { } + + public ViewPair GetCurrentView() where TView : IView where TViewModel : IViewModelCore => new((TView)CurrentPage!); +} + diff --git a/src/NewsReader/NewsReader.Applications/NewsReader.Applications.csproj b/src/NewsReader/NewsReader.Applications/NewsReader.Applications.csproj index a45eef89..04392009 100644 --- a/src/NewsReader/NewsReader.Applications/NewsReader.Applications.csproj +++ b/src/NewsReader/NewsReader.Applications/NewsReader.Applications.csproj @@ -16,6 +16,8 @@ + + diff --git a/src/NewsReader/NewsReader.Domain.Test/FeedItemTest.cs b/src/NewsReader/NewsReader.Domain.Test/FeedItemTest.cs index 05a115f0..dd20d1af 100644 --- a/src/NewsReader/NewsReader.Domain.Test/FeedItemTest.cs +++ b/src/NewsReader/NewsReader.Domain.Test/FeedItemTest.cs @@ -1,57 +1,55 @@ using Waf.NewsReader.Domain; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Waf.UnitTesting; -using Test.NewsReader.Domain.UnitTesting; +using Xunit; namespace Test.NewsReader.Domain; -[TestClass] -public class FeedItemTest : DomainTest +public class FeedItemTest { - [TestMethod] + [Fact] public void ApplyValuesFromTest() { AssertHelper.ExpectedException(() => new FeedItem(null!, DateTimeOffset.Now, "test", "test")); var itemA1 = new FeedItem(new("http://www.test.com/rss/feed"), new(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), "name", "desc"); - Assert.AreEqual(new DateTimeOffset(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), itemA1.Date); - Assert.AreEqual("name", itemA1.Name); - Assert.AreEqual("desc", itemA1.Description); - Assert.IsFalse(itemA1.MarkAsRead); + Assert.Equal(new(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), itemA1.Date); + Assert.Equal("name", itemA1.Name); + Assert.Equal("desc", itemA1.Description); + Assert.False(itemA1.MarkAsRead); var itemA2 = new FeedItem(new("http://www.test.com/rss/feed"), new(2022, 5, 5, 12, 0, 0, new(1, 0, 0)), "name2", "desc2"); itemA2.MarkAsRead = true; itemA1.ApplyValuesFrom(itemA2); - Assert.AreEqual(new DateTimeOffset(2022, 5, 5, 12, 0, 0, new(1, 0, 0)), itemA1.Date); - Assert.AreEqual("name2", itemA1.Name); - Assert.AreEqual("desc2", itemA1.Description); - Assert.IsTrue(itemA1.MarkAsRead); + Assert.Equal(new(2022, 5, 5, 12, 0, 0, new(1, 0, 0)), itemA1.Date); + Assert.Equal("name2", itemA1.Name); + Assert.Equal("desc2", itemA1.Description); + Assert.True(itemA1.MarkAsRead); var itemB1 = new FeedItem(new("http://www.test.com/rss/feed2"), new(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), "name", "desc"); AssertHelper.ExpectedException(() => itemA1.ApplyValuesFrom(itemB1)); } - [TestMethod] + [Fact] public void CloneTest() { var item = new FeedItem(new("http://www.test.com/rss/feed"), new(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), "name", "desc"); item.MarkAsRead = true; var clone = item.Clone(); - Assert.AreNotSame(item, clone); - Assert.AreEqual(new DateTimeOffset(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), clone.Date); - Assert.AreEqual("name", clone.Name); - Assert.AreEqual("desc", clone.Description); - Assert.IsTrue(clone.MarkAsRead); + Assert.NotSame(item, clone); + Assert.Equal(new(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), clone.Date); + Assert.Equal("name", clone.Name); + Assert.Equal("desc", clone.Description); + Assert.True(clone.MarkAsRead); } - [TestMethod] + [Fact] public void SupportNull() { var item = new FeedItem(new("http://www.test.com/rss/feed"), new(2020, 5, 5, 12, 0, 0, new(1, 0, 0)), "name", "desc"); item.Name = null; item.Description = null; - Assert.IsNull(item.Name); - Assert.IsNull(item.Description); + Assert.Null(item.Name); + Assert.Null(item.Description); } } diff --git a/src/NewsReader/NewsReader.Domain.Test/FeedManagerTest.cs b/src/NewsReader/NewsReader.Domain.Test/FeedManagerTest.cs index 01a2e042..a81a1f2c 100644 --- a/src/NewsReader/NewsReader.Domain.Test/FeedManagerTest.cs +++ b/src/NewsReader/NewsReader.Domain.Test/FeedManagerTest.cs @@ -1,45 +1,43 @@ using Waf.NewsReader.Domain; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Test.NewsReader.Domain.UnitTesting; -using static Waf.NewsReader.Domain.FeedManager; +using Xunit; +using Comparer = Waf.NewsReader.Domain.FeedManager.FeedEqualityComparer; namespace Test.NewsReader.Domain; -[TestClass] -public class FeedManagerTest : DomainTest +public class FeedManagerTest { - [TestMethod] + [Fact] public void SetDataManager() { var feedManagerA = new FeedManager(); - Assert.AreSame(feedManagerA, feedManagerA.Feeds.Single().DataManager); + Assert.Same(feedManagerA, feedManagerA.Feeds.Single().DataManager); var feedA1 = new Feed(new("http://www.test.com/rss/feed")); - Assert.IsNull(feedA1.DataManager); + Assert.Null(feedA1.DataManager); feedManagerA.Feeds.Add(feedA1); - Assert.AreSame(feedManagerA, feedA1.DataManager); + Assert.Same(feedManagerA, feedA1.DataManager); feedManagerA.Feeds.Remove(feedA1); - Assert.IsNull(feedA1.DataManager); + Assert.Null(feedA1.DataManager); // Now use the serialzer with one Feed added feedManagerA.Feeds.Add(feedA1); var feedManagerB = SerializerHelper.Clone(feedManagerA); - Assert.AreSame(feedManagerB, feedManagerB.Feeds.First().DataManager); + Assert.Same(feedManagerB, feedManagerB.Feeds.First().DataManager); var feedB1 = feedManagerB.Feeds.Last(); - Assert.AreSame(feedManagerB, feedB1.DataManager); + Assert.Same(feedManagerB, feedB1.DataManager); feedManagerB.Feeds.Remove(feedB1); - Assert.IsNull(feedB1.DataManager); + Assert.Null(feedB1.DataManager); feedManagerB.Feeds.Add(feedB1); - Assert.AreSame(feedManagerB, feedB1.DataManager); + Assert.Same(feedManagerB, feedB1.DataManager); } - [TestMethod] + [Fact] public void MergeTest() { var feedManagerA = new FeedManager(); @@ -49,8 +47,8 @@ public void MergeTest() feedManagerA.Feeds.Add(feedA1); feedManagerA.Feeds.Add(feedA2); var item = new FeedItem(new("http://www.test.com/rss/feed"), new(2120, 5, 5, 12, 0, 0, new(1, 0, 0)), "name", "desc"); - feedA2.UpdateItems(new[] { item }); - Assert.IsFalse(feedManagerA.Feeds.Last().Items.Single().MarkAsRead); + feedA2.UpdateItems([ item ]); + Assert.False(feedManagerA.Feeds.Last().Items.Single().MarkAsRead); var feedManagerB = new FeedManager() { @@ -62,15 +60,15 @@ public void MergeTest() var feedB2 = new Feed(new("http://www.test.com/rss/feedA2")); feedManagerB.Feeds.Add(feedB1); feedManagerB.Feeds.Add(feedB2); - feedB2.UpdateItems(new[] { item }, cloneItemsBeforeInsert: true); + feedB2.UpdateItems([ item ], cloneItemsBeforeInsert: true); feedB2.Items.Single().MarkAsRead = true; feedManagerA.Merge(feedManagerB); - Assert.AreEqual(42, feedManagerA.ItemLifetime!.Value.Days); - Assert.AreEqual(43u, feedManagerA.MaxItemsLimit!.Value); - Assert.IsTrue(new[] { "http://www.test.com/rss/feedB1", "http://www.test.com/rss/feedA2" }.SequenceEqual(feedManagerA.Feeds.Select(x => x.Uri.ToString()))); - Assert.IsTrue(feedManagerA.Feeds.Last().Items.Single().MarkAsRead); + Assert.Equal(42, feedManagerA.ItemLifetime!.Value.Days); + Assert.Equal(43u, feedManagerA.MaxItemsLimit!.Value); + Assert.Equal([ "http://www.test.com/rss/feedB1", "http://www.test.com/rss/feedA2" ], feedManagerA.Feeds.Select(x => x.Uri.ToString())); + Assert.True(feedManagerA.Feeds.Last().Items.Single().MarkAsRead); // Just remove one element => this way the Merge does not create new instances @@ -78,30 +76,30 @@ public void MergeTest() feedManagerC.Feeds.Remove(feedManagerC.Feeds.Single()); var feedC2 = new Feed(new("http://www.test.com/rss/feedA2")); feedManagerC.Feeds.Add(feedC2); - feedC2.UpdateItems(new[] { item }, cloneItemsBeforeInsert: true); + feedC2.UpdateItems([ item ], cloneItemsBeforeInsert: true); feedC2.Items.Single().MarkAsRead = false; var itemA = feedManagerA.Feeds.Last().Items.Single(); feedManagerA.Merge(feedManagerC); - Assert.IsTrue(new[] { "http://www.test.com/rss/feedA2" }.SequenceEqual(feedManagerA.Feeds.Select(x => x.Uri.ToString()))); - Assert.AreSame(itemA, feedManagerA.Feeds.Last().Items.Single()); - Assert.IsFalse(itemA.MarkAsRead); + Assert.Equal([ "http://www.test.com/rss/feedA2" ], feedManagerA.Feeds.Select(x => x.Uri.ToString())); + Assert.Same(itemA, feedManagerA.Feeds.Last().Items.Single()); + Assert.False(itemA.MarkAsRead); } - [TestMethod] + [Fact] public void FeedEqualityComparerTest() { - Assert.IsTrue(FeedEqualityComparer.Default.Equals(new Feed(new("http://microsoft.com")), new Feed(new("http://microsoft.com")))); - Assert.IsFalse(FeedEqualityComparer.Default.Equals(new Feed(new("http://microsoft.com")), new Feed(new("http://google.com")))); - Assert.IsFalse(FeedEqualityComparer.Default.Equals(new Feed(new("http://microsoft.com")), new Feed(null!))); - Assert.IsFalse(FeedEqualityComparer.Default.Equals(new Feed(null!), new Feed(new("http://microsoft.com")))); - Assert.IsFalse(FeedEqualityComparer.Default.Equals(new Feed(new("http://microsoft.com")), null!)); - Assert.IsFalse(FeedEqualityComparer.Default.Equals(null!, new Feed(new("http://microsoft.com")))); - Assert.IsTrue(FeedEqualityComparer.Default.Equals(new Feed(null!), new Feed(null!))); - Assert.IsTrue(FeedEqualityComparer.Default.Equals(null!, null!)); - - Assert.AreEqual(0, FeedEqualityComparer.Default.GetHashCode(new Feed(null!))); - Assert.AreEqual(0, FeedEqualityComparer.Default.GetHashCode(null!)); + Assert.True(Comparer.Default.Equals(new(new("http://microsoft.com")), new(new("http://microsoft.com")))); + Assert.False(Comparer.Default.Equals(new(new("http://microsoft.com")), new(new("http://google.com")))); + Assert.False(Comparer.Default.Equals(new(new("http://microsoft.com")), new(null!))); + Assert.False(Comparer.Default.Equals(new(null!), new(new("http://microsoft.com")))); + Assert.False(Comparer.Default.Equals(new(new("http://microsoft.com")), null!)); + Assert.False(Comparer.Default.Equals(null!, new(new("http://microsoft.com")))); + Assert.True(Comparer.Default.Equals(new(null!), new(null!))); + Assert.True(Comparer.Default.Equals(null!, null!)); + + Assert.Equal(0, Comparer.Default.GetHashCode(new(null!))); + Assert.Equal(0, Comparer.Default.GetHashCode(null!)); } } \ No newline at end of file diff --git a/src/NewsReader/NewsReader.Domain.Test/FeedTest.cs b/src/NewsReader/NewsReader.Domain.Test/FeedTest.cs index 54150808..b583eaf9 100644 --- a/src/NewsReader/NewsReader.Domain.Test/FeedTest.cs +++ b/src/NewsReader/NewsReader.Domain.Test/FeedTest.cs @@ -1,17 +1,15 @@ using Waf.NewsReader.Domain; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Test.NewsReader.Domain.UnitTesting; using System.Waf.UnitTesting; +using Xunit; namespace Test.NewsReader.Domain; -[TestClass] -public class FeedTest : DomainTest +public class FeedTest { - [TestMethod] + [Fact] public void IsLoadingTest1() => IsLoadingCoreTest(false); - [TestMethod] + [Fact] public void IsLoadingTest2() => IsLoadingCoreTest(true); private static void IsLoadingCoreTest(bool useSerializer) @@ -25,34 +23,34 @@ private static void IsLoadingCoreTest(bool useSerializer) feedManager.Feeds.Add(feed1); feedManager.Feeds.Add(feed2); - Assert.IsFalse(feed1.IsLoading); + Assert.False(feed1.IsLoading); feed1.StartLoading(); - Assert.IsTrue(feed1.IsLoading); + Assert.True(feed1.IsLoading); feed1.SetLoadError(new InvalidOperationException("test"), "display test"); - Assert.IsFalse(feed1.IsLoading); - Assert.AreEqual("test", feed1.LoadError!.Message); - Assert.AreEqual("display test", feed1.LoadErrorMessage); + Assert.False(feed1.IsLoading); + Assert.Equal("test", feed1.LoadError!.Message); + Assert.Equal("display test", feed1.LoadErrorMessage); feed1.StartLoading(); - Assert.IsNull(feed1.LoadError); - Assert.IsNull(feed1.LoadErrorMessage); + Assert.Null(feed1.LoadError); + Assert.Null(feed1.LoadErrorMessage); - Assert.IsFalse(feed2.IsLoading); + Assert.False(feed2.IsLoading); feed2.StartLoading(); - Assert.IsTrue(feed2.IsLoading); + Assert.True(feed2.IsLoading); feed2.UpdateItems([]); - Assert.IsFalse(feed2.IsLoading); + Assert.False(feed2.IsLoading); } - [TestMethod] + [Fact] public void UpdateItemsTest1() => UpdateItemsCoreTest(false, false); - [TestMethod] + [Fact] public void UpdateItemsTest2() => UpdateItemsCoreTest(false, true); - [TestMethod] + [Fact] public void UpdateItemsTest3() => UpdateItemsCoreTest(true, false); - [TestMethod] + [Fact] public void UpdateItemsTest4() => UpdateItemsCoreTest(true, true); private static void UpdateItemsCoreTest(bool cloneItemsBeforeInsert, bool useSerializer) @@ -64,8 +62,8 @@ private static void UpdateItemsCoreTest(bool cloneItemsBeforeInsert, bool useSer ]); feed = !useSerializer ? feed : SerializerHelper.Clone(feed); - Assert.AreEqual(2, feed.Items.Count); - Assert.IsTrue(new[] { "name2", "name1" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal(2, feed.Items.Count); + Assert.Equal([ "name2", "name1" ], feed.Items.Select(x => x.Name)); var newItems = new[] { @@ -74,22 +72,22 @@ private static void UpdateItemsCoreTest(bool cloneItemsBeforeInsert, bool useSer }; feed.UpdateItems(newItems, cloneItemsBeforeInsert: cloneItemsBeforeInsert); - Assert.AreEqual(3, feed.Items.Count); - Assert.IsTrue(new[] { "name2b", "name3", "name1" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal(3, feed.Items.Count); + Assert.Equal([ "name2b", "name3", "name1" ], feed.Items.Select(x => x.Name)); if (cloneItemsBeforeInsert) { - Assert.AreNotSame(feed.Items[1], newItems[1]); + Assert.NotSame(feed.Items[1], newItems[1]); } else { - Assert.AreSame(feed.Items[1], newItems[1]); + Assert.Same(feed.Items[1], newItems[1]); } } - [TestMethod] + [Fact] public void UnreadItemsCountTest1() => UnreadItemsCountCoreTest(false); - [TestMethod] + [Fact] public void UnreadItemsCountTest2() => UnreadItemsCountCoreTest(true); private static void UnreadItemsCountCoreTest(bool useSerializer) @@ -103,28 +101,28 @@ private static void UnreadItemsCountCoreTest(bool useSerializer) ]); feed = !useSerializer ? feed : SerializerHelper.Clone(feed); - Assert.AreEqual(2, feed.UnreadItemsCount); + Assert.Equal(2, feed.UnreadItemsCount); feed.Items[0].MarkAsRead = true; - Assert.AreEqual(1, feed.UnreadItemsCount); + Assert.Equal(1, feed.UnreadItemsCount); AssertHelper.PropertyChangedEvent(feed, x => x.UnreadItemsCount, () => { feed.UpdateItems([new FeedItem(new("http://www.test.com/rss/feed/3"), new(2020, 5, 5, 12, 0, 3, new(1, 0, 0)), "name3", "desc")]); }); - Assert.AreEqual(2, feed.UnreadItemsCount); + Assert.Equal(2, feed.UnreadItemsCount); AssertHelper.PropertyChangedEvent(feed, x => x.UnreadItemsCount, () => feed.Items[2].MarkAsRead = true); - Assert.AreEqual(1, feed.UnreadItemsCount); + Assert.Equal(1, feed.UnreadItemsCount); } - [TestMethod] + [Fact] public void TrimItemsListWithMaxItemsLimitTest1() => TrimItemsListWithMaxItemsLimitTest(false); - [TestMethod] + [Fact] public void TrimItemsListWithMaxItemsLimitTest2() => TrimItemsListWithMaxItemsLimitTest(true); private static void TrimItemsListWithMaxItemsLimitTest(bool useSerializer) @@ -136,29 +134,29 @@ private static void TrimItemsListWithMaxItemsLimitTest(bool useSerializer) feedManager.Feeds.Add(feed); UpdateFeedItems(feed); - Assert.IsTrue(new[] { "name3", "name2", "name1" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2", "name1" ], feed.Items.Select(x => x.Name)); feedManager.MaxItemsLimit = 2; - Assert.IsTrue(new[] { "name3", "name2" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2" ], feed.Items.Select(x => x.Name)); UpdateFeedItems(feed); - Assert.IsTrue(new[] { "name3", "name2" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2" ], feed.Items.Select(x => x.Name)); feedManager.MaxItemsLimit = 3; UpdateFeedItems(feed); - Assert.IsTrue(new[] { "name3", "name2", "name1" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2", "name1" ], feed.Items.Select(x => x.Name)); feedManager.MaxItemsLimit = 1; - Assert.IsTrue(new[] { "name3" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3" ], feed.Items.Select(x => x.Name)); feedManager.MaxItemsLimit = 0; - Assert.IsTrue(new string[0].SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Empty(feed.Items.Select(x => x.Name)); } - [TestMethod] + [Fact] public void TrimItemsListWithItemLifetimeTest1() => TrimItemsListWithItemLifetimeTest(false); - [TestMethod] + [Fact] public void TrimItemsListWithItemLifetimeTest2() => TrimItemsListWithItemLifetimeTest(false); private static void TrimItemsListWithItemLifetimeTest(bool useSerializer) @@ -170,23 +168,23 @@ private static void TrimItemsListWithItemLifetimeTest(bool useSerializer) feedManager.Feeds.Add(feed); UpdateFeedItems(feed); - Assert.IsTrue(new[] { "name3", "name2", "name1" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2", "name1" ], feed.Items.Select(x => x.Name)); feedManager.ItemLifetime = TimeSpan.FromDays(6); - Assert.IsTrue(new[] { "name3", "name2" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2" ], feed.Items.Select(x => x.Name)); UpdateFeedItems(feed); - Assert.IsTrue(new[] { "name3", "name2" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2" ], feed.Items.Select(x => x.Name)); feedManager.ItemLifetime = TimeSpan.FromDays(11); UpdateFeedItems(feed); - Assert.IsTrue(new[] { "name3", "name2", "name1" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3", "name2", "name1" ], feed.Items.Select(x => x.Name)); feedManager.ItemLifetime = TimeSpan.FromDays(2); - Assert.IsTrue(new[] { "name3" }.SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Equal([ "name3" ], feed.Items.Select(x => x.Name)); feedManager.ItemLifetime = TimeSpan.FromDays(0.5); - Assert.IsTrue(new string[0].SequenceEqual(feed.Items.Select(x => x.Name))); + Assert.Empty(feed.Items.Select(x => x.Name)); } private static void UpdateFeedItems(Feed feed) diff --git a/src/NewsReader/NewsReader.Domain.Test/NewsReader.Domain.Test.csproj b/src/NewsReader/NewsReader.Domain.Test/NewsReader.Domain.Test.csproj index a7f936d1..addbbbdf 100644 --- a/src/NewsReader/NewsReader.Domain.Test/NewsReader.Domain.Test.csproj +++ b/src/NewsReader/NewsReader.Domain.Test/NewsReader.Domain.Test.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/NewsReader/NewsReader.Domain.Test/UnitTesting/DomainTest.cs b/src/NewsReader/NewsReader.Domain.Test/UnitTesting/DomainTest.cs deleted file mode 100644 index cd420f10..00000000 --- a/src/NewsReader/NewsReader.Domain.Test/UnitTesting/DomainTest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Globalization; - -namespace Test.NewsReader.Domain.UnitTesting; - -[TestClass] -public abstract class DomainTest -{ - public TestContext TestContext { get; set; } = null!; - - [TestInitialize] - public void Initialize() - { - CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US"); - CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US"); - OnInitialize(); - } - - [TestCleanup] - public void Cleanup() => OnCleanup(); - - protected virtual void OnInitialize() { } - - protected virtual void OnCleanup() { } -} diff --git a/src/NewsReader/NewsReader.Domain/NewsReader.Domain.csproj b/src/NewsReader/NewsReader.Domain/NewsReader.Domain.csproj index a38dc19a..e5c22329 100644 --- a/src/NewsReader/NewsReader.Domain/NewsReader.Domain.csproj +++ b/src/NewsReader/NewsReader.Domain/NewsReader.Domain.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/NewsReader/NewsReader.Presentation.Test/Converters/NullToBoolConverterTest.cs b/src/NewsReader/NewsReader.Presentation.Test/Converters/NullToBoolConverterTest.cs index d6f4ef4c..6c791d86 100644 --- a/src/NewsReader/NewsReader.Presentation.Test/Converters/NullToBoolConverterTest.cs +++ b/src/NewsReader/NewsReader.Presentation.Test/Converters/NullToBoolConverterTest.cs @@ -1,19 +1,18 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Waf.NewsReader.Presentation.Converters; +using Waf.NewsReader.Presentation.Converters; +using Xunit; namespace Test.Presentation.Converters; -[TestClass] public class NullToBoolConverterTest { - [TestMethod] + [Fact] public void ConvertTest() { var c = new NullToBoolConverter(); - Assert.IsTrue((bool)c.Convert("Test", null, null, null)!); - Assert.IsFalse((bool)c.Convert(null, null, null, null)!); + Assert.True((bool)c.Convert("Test", null, null, null)!); + Assert.False((bool)c.Convert(null, null, null, null)!); - Assert.IsFalse((bool)c.Convert("Test", null, "Invert", null)!); - Assert.IsTrue((bool)c.Convert(null, null, "inverT", null)!); + Assert.False((bool)c.Convert("Test", null, "Invert", null)!); + Assert.True((bool)c.Convert(null, null, "inverT", null)!); } } diff --git a/src/NewsReader/NewsReader.Presentation.Test/NewsReader.Presentation.Test.csproj b/src/NewsReader/NewsReader.Presentation.Test/NewsReader.Presentation.Test.csproj index 8de89605..177e05c0 100644 --- a/src/NewsReader/NewsReader.Presentation.Test/NewsReader.Presentation.Test.csproj +++ b/src/NewsReader/NewsReader.Presentation.Test/NewsReader.Presentation.Test.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/NewsReader/NewsReader.Presentation/Converters/LocalizeDisplayMaxItemsLimitConverter.cs b/src/NewsReader/NewsReader.Presentation/Converters/LocalizeDisplayMaxItemsLimitConverter.cs index 1d430f30..93194923 100644 --- a/src/NewsReader/NewsReader.Presentation/Converters/LocalizeDisplayMaxItemsLimitConverter.cs +++ b/src/NewsReader/NewsReader.Presentation/Converters/LocalizeDisplayMaxItemsLimitConverter.cs @@ -9,7 +9,7 @@ public class LocalizeDisplayMaxItemsLimitConverter : IValueConverter public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) { var lifetime = (DisplayMaxItemsLimit)value!; - return lifetime.ToValue()?.ToString(CultureInfo.CurrentCulture) ?? Resources.Unlimited; + return lifetime.ToValue()?.ToString(culture) ?? Resources.Unlimited; } public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotSupportedException(); diff --git a/src/NewsReader/NewsReader.Presentation/Services/AppTraceListener.cs b/src/NewsReader/NewsReader.Presentation/Services/AppTraceListener.cs index 866b7429..a4dc4c70 100644 --- a/src/NewsReader/NewsReader.Presentation/Services/AppTraceListener.cs +++ b/src/NewsReader/NewsReader.Presentation/Services/AppTraceListener.cs @@ -21,7 +21,7 @@ public override void TraceEvent(TraceEventCache? eventCache, string source, Trac public override void TraceEvent(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, string? format, params object?[]? args) { - Log(eventType, source, TimeNow() + Format(eventType) + " " + string.Format(CultureInfo.InvariantCulture, format ?? "", args ?? Array.Empty())); + Log(eventType, source, TimeNow() + Format(eventType) + " " + string.Format(CultureInfo.InvariantCulture, format ?? "", args ?? [])); } private static string TimeNow() diff --git a/src/NewsReader/NewsReader.Presentation/Services/NetworkInfoService.cs b/src/NewsReader/NewsReader.Presentation/Services/NetworkInfoService.cs index c2fd3e08..b1ffa041 100644 --- a/src/NewsReader/NewsReader.Presentation/Services/NetworkInfoService.cs +++ b/src/NewsReader/NewsReader.Presentation/Services/NetworkInfoService.cs @@ -12,11 +12,7 @@ public NetworkInfoService() internetAccess = Connectivity.NetworkAccess == NetworkAccess.Internet; } - public bool InternetAccess - { - get => internetAccess; - set => SetProperty(ref internetAccess, value); - } + public bool InternetAccess { get => internetAccess; set => SetProperty(ref internetAccess, value); } private void ConnectivityChanged(object? sender, ConnectivityChangedEventArgs e) { diff --git a/src/NewsReader/NewsReader.Presentation/Services/WebStorageService.cs b/src/NewsReader/NewsReader.Presentation/Services/WebStorageService.cs index ae4fe700..bcb7a7dd 100644 --- a/src/NewsReader/NewsReader.Presentation/Services/WebStorageService.cs +++ b/src/NewsReader/NewsReader.Presentation/Services/WebStorageService.cs @@ -20,7 +20,7 @@ namespace Waf.NewsReader.Presentation.Services; internal sealed partial class WebStorageService : Model, IWebStorageService { private const string dataFileName = "data.zip"; - private static readonly string[] scopes = { "User.Read", "Files.ReadWrite.AppFolder" }; + private static readonly string[] scopes = [ "User.Read", "Files.ReadWrite.AppFolder" ]; private readonly IPublicClientApplication? publicClient; private GraphServiceClient? graphClient; @@ -125,7 +125,7 @@ private async Task InitGraphClient() requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken); })); var user = await graphClient.Me.Request().GetAsync(); - CurrentAccount = new UserAccount(!string.IsNullOrEmpty(user.DisplayName) ? user.DisplayName : user.UserPrincipalName, user.Mail); + CurrentAccount = new(!string.IsNullOrEmpty(user.DisplayName) ? user.DisplayName : user.UserPrincipalName, user.Mail); } public async Task<(Stream? stream, string? cTag)> DownloadFile(string? cTag) diff --git a/src/NewsReader/NewsReader.sln b/src/NewsReader/NewsReader.sln index a68b0b16..569572f9 100644 --- a/src/NewsReader/NewsReader.sln +++ b/src/NewsReader/NewsReader.sln @@ -22,6 +22,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NewsReader.Applications.Test", "NewsReader.Applications.Test\NewsReader.Applications.Test.csproj", "{A94B114C-3248-437E-8A98-53AF674A9C1A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -54,6 +56,10 @@ Global {698FAC6A-B6C7-4E4A-A0EB-DD932B8276BB}.Debug|Any CPU.Build.0 = Debug|Any CPU {698FAC6A-B6C7-4E4A-A0EB-DD932B8276BB}.Release|Any CPU.ActiveCfg = Release|Any CPU {698FAC6A-B6C7-4E4A-A0EB-DD932B8276BB}.Release|Any CPU.Build.0 = Release|Any CPU + {A94B114C-3248-437E-8A98-53AF674A9C1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A94B114C-3248-437E-8A98-53AF674A9C1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A94B114C-3248-437E-8A98-53AF674A9C1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A94B114C-3248-437E-8A98-53AF674A9C1A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE