From 0978394d9c00abcd7ec21da14aa5912627af335c Mon Sep 17 00:00:00 2001
From: rlittlesii <6969701+RLittlesII@users.noreply.github.com>
Date: Fri, 23 Dec 2022 15:53:02 -0600
Subject: [PATCH 1/3] enhancement: add concurrency to ApplicationStartup
add Microsoft Logging Extensions to Startup
---
Airframe.sln | 8 +-
directory.packages.props | 164 +++++++++---------
src/Core/AppStart/ApplicationStartup.cs | 24 +--
.../AppStart/ApplicationStartupExtensions.cs | 17 ++
src/Core/AppStart/IApplicationStartup.cs | 12 +-
.../AppStart/LoggableApplicationStartup.cs | 56 ++++++
src/Core/AppStart/LoggableStartupOperation.cs | 50 ++++++
src/Core/Core.csproj | 4 +-
.../Core/AppStart/ApplicationStartupTest.cs | 59 -------
.../Core/AppStart/TestOperation.cs | 25 ---
.../Core/Geofence/GeofenceServiceTests.cs | 17 --
.../AppStart/ApplicationStartupFixture.cs | 11 +-
.../AppStart/ApplicationStartupTest.cs | 64 +++++++
test/Core.Tests/AppStart/TestOperation.cs | 37 ++++
test/Core.Tests/Core.Tests.csproj | 28 +++
.../Geofence/GeofenceServiceFixture.cs | 10 ++
.../Geofence/GeofenceServiceTests.cs | 7 +
test/Core.Tests/LoggerMock.cs | 21 +++
18 files changed, 405 insertions(+), 209 deletions(-)
create mode 100644 src/Core/AppStart/ApplicationStartupExtensions.cs
create mode 100644 src/Core/AppStart/LoggableApplicationStartup.cs
create mode 100644 src/Core/AppStart/LoggableStartupOperation.cs
delete mode 100644 test/Airframe.Tests/Core/AppStart/ApplicationStartupTest.cs
delete mode 100644 test/Airframe.Tests/Core/AppStart/TestOperation.cs
delete mode 100644 test/Airframe.Tests/Core/Geofence/GeofenceServiceTests.cs
rename test/{Airframe.Tests/Core => Core.Tests}/AppStart/ApplicationStartupFixture.cs (71%)
create mode 100644 test/Core.Tests/AppStart/ApplicationStartupTest.cs
create mode 100644 test/Core.Tests/AppStart/TestOperation.cs
create mode 100644 test/Core.Tests/Core.Tests.csproj
create mode 100644 test/Core.Tests/Geofence/GeofenceServiceFixture.cs
create mode 100644 test/Core.Tests/Geofence/GeofenceServiceTests.cs
create mode 100644 test/Core.Tests/LoggerMock.cs
diff --git a/Airframe.sln b/Airframe.sln
index 819fe481e..cb4a52577 100644
--- a/Airframe.sln
+++ b/Airframe.sln
@@ -21,7 +21,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{
NuGet.config = NuGet.config
README.md = README.md
stylecop.json = stylecop.json
- packages.props = packages.props
.gitignore = .gitignore
.editorconfig = .editorconfig
.codecov.yml = .codecov.yml
@@ -90,6 +89,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Airframe.ViewModels.Tests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicrosoftDependencyInjection.Tests", "test\MicrosoftDependencyInjection.Tests\MicrosoftDependencyInjection.Tests.csproj", "{6C3C3C73-C46B-4BF2-8F78-F417135D5D3E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Tests", "test\Core.Tests\Core.Tests.csproj", "{A250A99B-0225-4B5D-9753-ED1DF770D61D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -178,6 +179,10 @@ Global
{6C3C3C73-C46B-4BF2-8F78-F417135D5D3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C3C3C73-C46B-4BF2-8F78-F417135D5D3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C3C3C73-C46B-4BF2-8F78-F417135D5D3E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A250A99B-0225-4B5D-9753-ED1DF770D61D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A250A99B-0225-4B5D-9753-ED1DF770D61D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A250A99B-0225-4B5D-9753-ED1DF770D61D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A250A99B-0225-4B5D-9753-ED1DF770D61D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -204,6 +209,7 @@ Global
{903C1B9D-F4F8-4540-8F46-E8EB8F15EAA5} = {843C3844-BA79-40AE-B102-F5AFA0F4AD77}
{64AEB8AB-619C-4AF3-BA3F-407A4FA9A305} = {843C3844-BA79-40AE-B102-F5AFA0F4AD77}
{6C3C3C73-C46B-4BF2-8F78-F417135D5D3E} = {843C3844-BA79-40AE-B102-F5AFA0F4AD77}
+ {A250A99B-0225-4B5D-9753-ED1DF770D61D} = {843C3844-BA79-40AE-B102-F5AFA0F4AD77}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8F0DF64A-C8D5-41DF-B4C9-5C70526644DF}
diff --git a/directory.packages.props b/directory.packages.props
index fab708b03..a351b8037 100644
--- a/directory.packages.props
+++ b/directory.packages.props
@@ -1,86 +1,82 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Core/AppStart/ApplicationStartup.cs b/src/Core/AppStart/ApplicationStartup.cs
index 364375101..11941eb69 100644
--- a/src/Core/AppStart/ApplicationStartup.cs
+++ b/src/Core/AppStart/ApplicationStartup.cs
@@ -1,29 +1,21 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Reactive;
-using System.Reactive.Linq;
+using Microsoft.Extensions.Logging;
namespace Rocket.Surgery.Airframe
{
///
/// Represents the application startup sequence.
///
- public sealed class ApplicationStartup : IApplicationStartup
+ public sealed class ApplicationStartup : LoggableApplicationStartup, IApplicationStartup
{
- private readonly IEnumerable _startupTasks;
-
///
/// Initializes a new instance of the class.
///
- /// The startup tasks.
- public ApplicationStartup(IEnumerable startupTasks) =>
- _startupTasks = startupTasks;
-
- ///
- public IObservable Startup() => _startupTasks
- .Where(x => x.CanExecute())
- .Select(x => x.Start())
- .Concat();
+ /// The logger factory.
+ /// The startup operations.
+ public ApplicationStartup(ILoggerFactory loggerFactory, IEnumerable startupOperations)
+ : base(loggerFactory, startupOperations)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/AppStart/ApplicationStartupExtensions.cs b/src/Core/AppStart/ApplicationStartupExtensions.cs
new file mode 100644
index 000000000..e6f7cba0a
--- /dev/null
+++ b/src/Core/AppStart/ApplicationStartupExtensions.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Reactive;
+
+namespace Rocket.Surgery.Airframe;
+
+///
+/// Future default interface implementations. Some call them mixins.
+///
+public static class ApplicationStartupExtensions
+{
+ ///
+ /// Starts the application life cycle.
+ ///
+ /// The application startup.
+ /// A completion notification.
+ public static IObservable Startup(this IApplicationStartup startup) => startup.Startup(1);
+}
\ No newline at end of file
diff --git a/src/Core/AppStart/IApplicationStartup.cs b/src/Core/AppStart/IApplicationStartup.cs
index c844c7d06..112d1a7b1 100644
--- a/src/Core/AppStart/IApplicationStartup.cs
+++ b/src/Core/AppStart/IApplicationStartup.cs
@@ -1,5 +1,6 @@
using System;
using System.Reactive;
+using System.Reactive.Concurrency;
namespace Rocket.Surgery.Airframe
{
@@ -11,7 +12,16 @@ public interface IApplicationStartup
///
/// Starts the application life cycle.
///
+ /// The maximum concurrent operations.
/// A completion notification.
- IObservable Startup();
+ IObservable Startup(int concurrentOperations);
+
+ ///
+ /// Starts the application life cycle.
+ ///
+ /// The maximum concurrent operations.
+ /// The scheduler.
+ /// A completion notification.
+ IObservable Startup(int concurrentOperations, IScheduler scheduler);
}
}
\ No newline at end of file
diff --git a/src/Core/AppStart/LoggableApplicationStartup.cs b/src/Core/AppStart/LoggableApplicationStartup.cs
new file mode 100644
index 000000000..2a2be0c0c
--- /dev/null
+++ b/src/Core/AppStart/LoggableApplicationStartup.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Extensions.Logging;
+
+namespace Rocket.Surgery.Airframe;
+
+///
+/// Represents the application startup sequence.
+///
+public abstract class LoggableApplicationStartup : IApplicationStartup
+{
+ private readonly ILogger _logger;
+ private readonly IEnumerable _startupOperations;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger factory.
+ /// The startup tasks.
+ protected LoggableApplicationStartup(ILoggerFactory loggerFactory, IEnumerable startupOperations)
+ {
+ _logger = loggerFactory.CreateLogger(GetType());
+ _startupOperations = startupOperations;
+ }
+
+ ///
+ IObservable IApplicationStartup.Startup(int concurrentOperations) => Startup(concurrentOperations)
+ .Finally(() => _logger.LogTrace("{Startup} completed", GetType().Name));
+
+ ///
+ IObservable IApplicationStartup.Startup(int concurrentOperations, IScheduler scheduler) => null;
+
+ ///
+ /// Executes the provided .
+ ///
+ /// The maximum concurrent operations.
+ /// The scheduler.
+ /// A completion notification of the startup operation execution.
+ protected virtual IObservable Startup(int concurrentOperations, IScheduler scheduler) => _startupOperations
+ .Where(operation => operation.CanExecute())
+ .Select(operation => operation.Start())
+ .Merge(concurrentOperations, scheduler)
+ .PublishLast()
+ .RefCount();
+
+ ///
+ /// Executes the provided .
+ ///
+ /// The maximum concurrent operations.
+ /// A completion notification of the startup operation execution.
+ protected virtual IObservable Startup(int concurrentOperations = 1) => Startup(concurrentOperations, CurrentThreadScheduler.Instance);
+}
\ No newline at end of file
diff --git a/src/Core/AppStart/LoggableStartupOperation.cs b/src/Core/AppStart/LoggableStartupOperation.cs
new file mode 100644
index 000000000..7791e1218
--- /dev/null
+++ b/src/Core/AppStart/LoggableStartupOperation.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Reactive;
+using System.Reactive.Linq;
+using Microsoft.Extensions.Logging;
+
+namespace Rocket.Surgery.Airframe
+{
+ ///
+ /// Represents a that logs using .
+ ///
+ public abstract class LoggableStartupOperation : IStartupOperation
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger factory.
+ protected LoggableStartupOperation(ILoggerFactory factory) => Logger = factory.CreateLogger(GetType());
+
+ ///
+ /// Gets the logger.
+ ///
+ protected ILogger Logger { get; }
+
+ ///
+ IObservable IStartupOperation.Start() =>
+
+ // Add logging.
+ Start().Finally(() => Logger.LogTrace("Completed {Operation}", GetType().Name));
+
+ ///
+ bool IStartupOperation.CanExecute()
+ {
+ var canExecute = CanExecute();
+ Logger.LogTrace("Can Execute: {CanExecute}", canExecute);
+ return canExecute;
+ }
+
+ ///
+ /// Template method for the startup operation.
+ ///
+ /// A completion notification.
+ protected abstract IObservable Start();
+
+ ///
+ /// Template method for whether or not the startup operation will execute.
+ ///
+ /// A completion notification.
+ protected virtual bool CanExecute() => true;
+ }
+}
\ No newline at end of file
diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
index 33e43b42f..deb95ea20 100644
--- a/src/Core/Core.csproj
+++ b/src/Core/Core.csproj
@@ -8,5 +8,7 @@
latest
Abstractions and base implementations for Airframe.
-
+
+
+
diff --git a/test/Airframe.Tests/Core/AppStart/ApplicationStartupTest.cs b/test/Airframe.Tests/Core/AppStart/ApplicationStartupTest.cs
deleted file mode 100644
index b2adfb59f..000000000
--- a/test/Airframe.Tests/Core/AppStart/ApplicationStartupTest.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using FluentAssertions;
-using Microsoft.Reactive.Testing;
-using ReactiveUI.Testing;
-using Rocket.Surgery.Airframe;
-using System;
-using System.Reactive.Linq;
-using Xunit;
-
-namespace Airframe.Tests.Core.AppStart
-{
- public class ApplicationStartupTest : TestBase
- {
- [Fact]
- public void Should_Notify_When_Operations_Complete()
- {
- // Given
- var result = false;
- var testScheduler = new TestScheduler();
- ApplicationStartup sut = new ApplicationStartupFixture().WithStartupOperations(new TestOperation(testScheduler, TimeSpan.FromSeconds(3)));
-
- // When
- sut.Startup()
- .Select(_ => true)
- .Subscribe(
- x =>
- {
- // Then
- result = x;
- }
- );
- testScheduler.AdvanceByMs(1000);
-
- result.Should().BeFalse();
-
- testScheduler.AdvanceByMs(2001);
- result.Should().BeTrue();
- }
-
-
- [Fact]
- public void Should_Skip_If_Cannot_Execute()
- {
- // Given
- var testScheduler = new TestScheduler();
- ApplicationStartup sut = new ApplicationStartupFixture().WithStartupOperations(new TestOperation(testScheduler, TimeSpan.FromSeconds(3), false));
-
- // When
- sut.Startup()
- .Select(_ => true)
- .Subscribe(
- x =>
- {
- // Then
- x.Should().BeTrue();
- }
- );
- }
- }
-}
\ No newline at end of file
diff --git a/test/Airframe.Tests/Core/AppStart/TestOperation.cs b/test/Airframe.Tests/Core/AppStart/TestOperation.cs
deleted file mode 100644
index 8fab38c8d..000000000
--- a/test/Airframe.Tests/Core/AppStart/TestOperation.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Rocket.Surgery.Airframe;
-using System;
-using System.Reactive;
-using System.Reactive.Concurrency;
-using System.Reactive.Linq;
-
-namespace Airframe.Tests.Core.AppStart
-{
- internal class TestOperation : StartupOperationBase
- {
- private readonly IScheduler _scheduler;
- private readonly TimeSpan _delay;
- private readonly bool _canExecute;
-
- public TestOperation(IScheduler scheduler, TimeSpan delay, bool canExecute = true)
- {
- _scheduler = scheduler;
- _delay = delay;
- _canExecute = canExecute;
- }
-
- protected override IObservable Start() => Observable.Return(Unit.Default).Delay(_delay, _scheduler);
- protected override bool CanExecute() => _canExecute;
- }
-}
\ No newline at end of file
diff --git a/test/Airframe.Tests/Core/Geofence/GeofenceServiceTests.cs b/test/Airframe.Tests/Core/Geofence/GeofenceServiceTests.cs
deleted file mode 100644
index c238235e6..000000000
--- a/test/Airframe.Tests/Core/Geofence/GeofenceServiceTests.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Rocket.Surgery.Airframe;
-using Rocket.Surgery.Extensions.Testing.Fixtures;
-
-namespace Airframe.Tests.Core.Geofence
-{
- public class GeofenceServiceTests
- {
-
- }
-
- internal class GeofenceServiceFixture : ITestFixtureBuilder
- {
- public static implicit operator GeofenceService(GeofenceServiceFixture fixture) => fixture.Build();
-
- private GeofenceService Build() => new GeofenceService();
- }
-}
\ No newline at end of file
diff --git a/test/Airframe.Tests/Core/AppStart/ApplicationStartupFixture.cs b/test/Core.Tests/AppStart/ApplicationStartupFixture.cs
similarity index 71%
rename from test/Airframe.Tests/Core/AppStart/ApplicationStartupFixture.cs
rename to test/Core.Tests/AppStart/ApplicationStartupFixture.cs
index 979edfb9c..defddaa64 100644
--- a/test/Airframe.Tests/Core/AppStart/ApplicationStartupFixture.cs
+++ b/test/Core.Tests/AppStart/ApplicationStartupFixture.cs
@@ -1,18 +1,19 @@
-using Rocket.Surgery.Airframe;
+using Microsoft.Extensions.Logging;
+using NSubstitute;
using Rocket.Surgery.Extensions.Testing.Fixtures;
-using System.Collections.Generic;
-using System.Linq;
-namespace Airframe.Tests.Core.AppStart
+namespace Rocket.Surgery.Airframe.Core.Tests.AppStart
{
internal class ApplicationStartupFixture : ITestFixtureBuilder
{
private IEnumerable _startupOperations = Enumerable.Empty();
+ private ILoggerFactory _loggerFactory = Substitute.For();
+
public static implicit operator ApplicationStartup(ApplicationStartupFixture fixture) => fixture.Build();
public ApplicationStartupFixture WithStartupOperations(params StartupOperationBase[] startupOperations)
=> this.With(ref _startupOperations, startupOperations);
- private ApplicationStartup Build() => new ApplicationStartup(_startupOperations);
+ private ApplicationStartup Build() => new ApplicationStartup(_loggerFactory, _startupOperations);
}
}
\ No newline at end of file
diff --git a/test/Core.Tests/AppStart/ApplicationStartupTest.cs b/test/Core.Tests/AppStart/ApplicationStartupTest.cs
new file mode 100644
index 000000000..997488bba
--- /dev/null
+++ b/test/Core.Tests/AppStart/ApplicationStartupTest.cs
@@ -0,0 +1,64 @@
+using FluentAssertions;
+using Microsoft.Reactive.Testing;
+using ReactiveUI.Testing;
+using System.Reactive;
+using Xunit;
+
+namespace Rocket.Surgery.Airframe.Core.Tests.AppStart
+{
+ public class ApplicationStartupTest
+ {
+ [Fact]
+ public void GivenOperation_WhenStartupComplete_ThenCompletionRecieved()
+ {
+ // Given
+ Unit? result = null;
+ var testScheduler = new TestScheduler();
+ ApplicationStartup sut = new ApplicationStartupFixture().WithStartupOperations(new ScheduledTestOperation(testScheduler, TimeSpan.FromSeconds(3)));
+
+ // When
+ sut.Startup()
+ .Subscribe(
+ x =>
+ {
+ // Then
+ result = x;
+ }
+ );
+ testScheduler.AdvanceByMs(1000);
+
+ result.Should().BeNull();
+
+ testScheduler.AdvanceByMs(2001);
+ result.Should().NotBeNull();
+ }
+
+ [Fact]
+ public void GivenOperationCannotExecute_WhenStartup_ThenNotExecuted()
+ {
+ // Given
+ var testOperation = new TestOperation(false);
+ ApplicationStartup sut = new ApplicationStartupFixture().WithStartupOperations(testOperation);
+
+ // When
+ using var _ = sut.Startup().Subscribe();
+
+ // Then
+ testOperation.Executed.Should().BeFalse();
+ }
+
+ [Fact]
+ public void GivenOperationCanExecute_WhenStartup_ThenExecuted()
+ {
+ // Given
+ var testOperation = new TestOperation();
+ ApplicationStartup sut = new ApplicationStartupFixture().WithStartupOperations(testOperation);
+
+ // When
+ using var _ = sut.Startup().Subscribe();
+
+ // Then
+ testOperation.Executed.Should().BeTrue();
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Core.Tests/AppStart/TestOperation.cs b/test/Core.Tests/AppStart/TestOperation.cs
new file mode 100644
index 000000000..871c9fdd8
--- /dev/null
+++ b/test/Core.Tests/AppStart/TestOperation.cs
@@ -0,0 +1,37 @@
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+
+namespace Rocket.Surgery.Airframe.Core.Tests.AppStart
+{
+ internal class ScheduledTestOperation : TestOperation
+ {
+ private readonly IScheduler _scheduler;
+ private readonly TimeSpan _delay;
+
+ public ScheduledTestOperation(IScheduler scheduler, TimeSpan delay, bool willExecute = true)
+ : base(willExecute)
+ {
+ _scheduler = scheduler;
+ _delay = delay;
+ }
+
+ ///
+ protected override IObservable Start() => Observable.Return(Unit.Default).Delay(_delay, _scheduler).Finally(() => Executed = true);
+ }
+
+ internal class TestOperation : StartupOperationBase
+ {
+ private readonly bool _canExecute;
+
+ public TestOperation(bool canExecute = true) => _canExecute = canExecute;
+
+ public bool Executed { get; protected set; }
+
+ ///
+ protected override IObservable Start() => Observable.Return(Unit.Default).Finally(() => Executed = true);
+
+ ///
+ protected override bool CanExecute() => _canExecute;
+ }
+}
\ No newline at end of file
diff --git a/test/Core.Tests/Core.Tests.csproj b/test/Core.Tests/Core.Tests.csproj
new file mode 100644
index 000000000..795e38d7d
--- /dev/null
+++ b/test/Core.Tests/Core.Tests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net6.0
+ enable
+ false
+ Rocket.Surgery.Airframe.Core.Tests
+ Rocket.Surgery.Airframe.Core.Tests
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/test/Core.Tests/Geofence/GeofenceServiceFixture.cs b/test/Core.Tests/Geofence/GeofenceServiceFixture.cs
new file mode 100644
index 000000000..a636ddd94
--- /dev/null
+++ b/test/Core.Tests/Geofence/GeofenceServiceFixture.cs
@@ -0,0 +1,10 @@
+using Rocket.Surgery.Extensions.Testing.Fixtures;
+
+namespace Rocket.Surgery.Airframe.Core.Tests.Geofence;
+
+internal class GeofenceServiceFixture : ITestFixtureBuilder
+{
+ public static implicit operator GeofenceService(GeofenceServiceFixture fixture) => fixture.Build();
+
+ private GeofenceService Build() => new GeofenceService();
+}
\ No newline at end of file
diff --git a/test/Core.Tests/Geofence/GeofenceServiceTests.cs b/test/Core.Tests/Geofence/GeofenceServiceTests.cs
new file mode 100644
index 000000000..13299d119
--- /dev/null
+++ b/test/Core.Tests/Geofence/GeofenceServiceTests.cs
@@ -0,0 +1,7 @@
+namespace Rocket.Surgery.Airframe.Core.Tests.Geofence
+{
+ public class GeofenceServiceTests
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/test/Core.Tests/LoggerMock.cs b/test/Core.Tests/LoggerMock.cs
new file mode 100644
index 000000000..65ed7127e
--- /dev/null
+++ b/test/Core.Tests/LoggerMock.cs
@@ -0,0 +1,21 @@
+using Microsoft.Extensions.Logging;
+using System.Reactive.Disposables;
+using Xunit.Abstractions;
+
+namespace Rocket.Surgery.Airframe.Core.Tests;
+
+internal class LoggerMock : ILogger
+{
+ private readonly ITestOutputHelper _testOutputHelper;
+
+ public LoggerMock(ITestOutputHelper testOutputHelper) => _testOutputHelper = testOutputHelper;
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => LogToOutputHelper(eventId, state, exception, formatter);
+
+ private void LogToOutputHelper(EventId eventId, TState state, Exception exception, Func formatter) =>
+ _testOutputHelper.WriteLine(formatter(state, exception));
+
+ public bool IsEnabled(LogLevel logLevel) => true;
+
+ public IDisposable BeginScope(TState state) => Disposable.Empty;
+}
\ No newline at end of file
From 4866f87bb1e4597b40ed20e8120a1da7ea48724e Mon Sep 17 00:00:00 2001
From: rlittlesii <6969701+RLittlesII@users.noreply.github.com>
Date: Fri, 23 Dec 2022 16:09:56 -0600
Subject: [PATCH 2/3] block scope because
---
.../AppStart/ApplicationStartupExtensions.cs | 21 ++---
.../AppStart/LoggableApplicationStartup.cs | 79 ++++++++++---------
2 files changed, 51 insertions(+), 49 deletions(-)
diff --git a/src/Core/AppStart/ApplicationStartupExtensions.cs b/src/Core/AppStart/ApplicationStartupExtensions.cs
index e6f7cba0a..0e87598bb 100644
--- a/src/Core/AppStart/ApplicationStartupExtensions.cs
+++ b/src/Core/AppStart/ApplicationStartupExtensions.cs
@@ -1,17 +1,18 @@
using System;
using System.Reactive;
-namespace Rocket.Surgery.Airframe;
-
-///
-/// Future default interface implementations. Some call them mixins.
-///
-public static class ApplicationStartupExtensions
+namespace Rocket.Surgery.Airframe
{
///
- /// Starts the application life cycle.
+ /// Future default interface implementations. Some call them mixins.
///
- /// The application startup.
- /// A completion notification.
- public static IObservable Startup(this IApplicationStartup startup) => startup.Startup(1);
+ public static class ApplicationStartupExtensions
+ {
+ ///
+ /// Starts the application life cycle.
+ ///
+ /// The application startup.
+ /// A completion notification.
+ public static IObservable Startup(this IApplicationStartup startup) => startup.Startup(1);
+ }
}
\ No newline at end of file
diff --git a/src/Core/AppStart/LoggableApplicationStartup.cs b/src/Core/AppStart/LoggableApplicationStartup.cs
index 2a2be0c0c..7242df8f6 100644
--- a/src/Core/AppStart/LoggableApplicationStartup.cs
+++ b/src/Core/AppStart/LoggableApplicationStartup.cs
@@ -6,51 +6,52 @@
using System.Reactive.Linq;
using Microsoft.Extensions.Logging;
-namespace Rocket.Surgery.Airframe;
-
-///
-/// Represents the application startup sequence.
-///
-public abstract class LoggableApplicationStartup : IApplicationStartup
+namespace Rocket.Surgery.Airframe
{
- private readonly ILogger _logger;
- private readonly IEnumerable _startupOperations;
-
///
- /// Initializes a new instance of the class.
+ /// Represents the application startup sequence.
///
- /// The logger factory.
- /// The startup tasks.
- protected LoggableApplicationStartup(ILoggerFactory loggerFactory, IEnumerable startupOperations)
+ public abstract class LoggableApplicationStartup : IApplicationStartup
{
- _logger = loggerFactory.CreateLogger(GetType());
- _startupOperations = startupOperations;
- }
+ private readonly ILogger _logger;
+ private readonly IEnumerable _startupOperations;
- ///
- IObservable IApplicationStartup.Startup(int concurrentOperations) => Startup(concurrentOperations)
- .Finally(() => _logger.LogTrace("{Startup} completed", GetType().Name));
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger factory.
+ /// The startup tasks.
+ protected LoggableApplicationStartup(ILoggerFactory loggerFactory, IEnumerable startupOperations)
+ {
+ _logger = loggerFactory.CreateLogger(GetType());
+ _startupOperations = startupOperations;
+ }
- ///
- IObservable IApplicationStartup.Startup(int concurrentOperations, IScheduler scheduler) => null;
+ ///
+ IObservable IApplicationStartup.Startup(int concurrentOperations) => Startup(concurrentOperations)
+ .Finally(() => _logger.LogTrace("{Startup} completed", GetType().Name));
- ///
- /// Executes the provided .
- ///
- /// The maximum concurrent operations.
- /// The scheduler.
- /// A completion notification of the startup operation execution.
- protected virtual IObservable Startup(int concurrentOperations, IScheduler scheduler) => _startupOperations
- .Where(operation => operation.CanExecute())
- .Select(operation => operation.Start())
- .Merge(concurrentOperations, scheduler)
- .PublishLast()
- .RefCount();
+ ///
+ IObservable IApplicationStartup.Startup(int concurrentOperations, IScheduler scheduler) => null;
- ///
- /// Executes the provided .
- ///
- /// The maximum concurrent operations.
- /// A completion notification of the startup operation execution.
- protected virtual IObservable Startup(int concurrentOperations = 1) => Startup(concurrentOperations, CurrentThreadScheduler.Instance);
+ ///
+ /// Executes the provided .
+ ///
+ /// The maximum concurrent operations.
+ /// The scheduler.
+ /// A completion notification of the startup operation execution.
+ protected virtual IObservable Startup(int concurrentOperations, IScheduler scheduler) => _startupOperations
+ .Where(operation => operation.CanExecute())
+ .Select(operation => operation.Start())
+ .Merge(concurrentOperations, scheduler)
+ .PublishLast()
+ .RefCount();
+
+ ///
+ /// Executes the provided .
+ ///
+ /// The maximum concurrent operations.
+ /// A completion notification of the startup operation execution.
+ protected virtual IObservable Startup(int concurrentOperations = 1) => Startup(concurrentOperations, CurrentThreadScheduler.Instance);
+ }
}
\ No newline at end of file
From 2006ac8de9f326a5f505cdb7243a40ee95fd7f7d Mon Sep 17 00:00:00 2001
From: rlittlesii <6969701+RLittlesII@users.noreply.github.com>
Date: Fri, 23 Dec 2022 16:21:31 -0600
Subject: [PATCH 3/3] fixup
---
.../AppStart/ApplicationStartupFixture.cs | 2 ++
.../AppStart/ApplicationStartupTest.cs | 1 +
.../AppStart/ScheduledTestOperation.cs | 23 +++++++++++++++++++
test/Core.Tests/AppStart/TestOperation.cs | 18 +--------------
test/Core.Tests/Core.Tests.csproj | 1 -
.../Geofence/GeofenceServiceFixture.cs | 10 --------
.../Geofence/GeofenceServiceTests.cs | 7 ------
test/Core.Tests/LoggerMock.cs | 22 ++++++++++--------
8 files changed, 39 insertions(+), 45 deletions(-)
create mode 100644 test/Core.Tests/AppStart/ScheduledTestOperation.cs
delete mode 100644 test/Core.Tests/Geofence/GeofenceServiceFixture.cs
delete mode 100644 test/Core.Tests/Geofence/GeofenceServiceTests.cs
diff --git a/test/Core.Tests/AppStart/ApplicationStartupFixture.cs b/test/Core.Tests/AppStart/ApplicationStartupFixture.cs
index defddaa64..b54006d38 100644
--- a/test/Core.Tests/AppStart/ApplicationStartupFixture.cs
+++ b/test/Core.Tests/AppStart/ApplicationStartupFixture.cs
@@ -1,6 +1,8 @@
using Microsoft.Extensions.Logging;
using NSubstitute;
using Rocket.Surgery.Extensions.Testing.Fixtures;
+using System.Collections.Generic;
+using System.Linq;
namespace Rocket.Surgery.Airframe.Core.Tests.AppStart
{
diff --git a/test/Core.Tests/AppStart/ApplicationStartupTest.cs b/test/Core.Tests/AppStart/ApplicationStartupTest.cs
index 997488bba..4a6e1261b 100644
--- a/test/Core.Tests/AppStart/ApplicationStartupTest.cs
+++ b/test/Core.Tests/AppStart/ApplicationStartupTest.cs
@@ -1,6 +1,7 @@
using FluentAssertions;
using Microsoft.Reactive.Testing;
using ReactiveUI.Testing;
+using System;
using System.Reactive;
using Xunit;
diff --git a/test/Core.Tests/AppStart/ScheduledTestOperation.cs b/test/Core.Tests/AppStart/ScheduledTestOperation.cs
new file mode 100644
index 000000000..c918d3749
--- /dev/null
+++ b/test/Core.Tests/AppStart/ScheduledTestOperation.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+
+namespace Rocket.Surgery.Airframe.Core.Tests.AppStart
+{
+ internal class ScheduledTestOperation : TestOperation
+ {
+ private readonly IScheduler _scheduler;
+ private readonly TimeSpan _delay;
+
+ public ScheduledTestOperation(IScheduler scheduler, TimeSpan delay, bool willExecute = true)
+ : base(willExecute)
+ {
+ _scheduler = scheduler;
+ _delay = delay;
+ }
+
+ ///
+ protected override IObservable Start() => Observable.Return(Unit.Default).Delay(_delay, _scheduler).Finally(() => Executed = true);
+ }
+}
\ No newline at end of file
diff --git a/test/Core.Tests/AppStart/TestOperation.cs b/test/Core.Tests/AppStart/TestOperation.cs
index 871c9fdd8..c0cdaa82e 100644
--- a/test/Core.Tests/AppStart/TestOperation.cs
+++ b/test/Core.Tests/AppStart/TestOperation.cs
@@ -1,25 +1,9 @@
+using System;
using System.Reactive;
-using System.Reactive.Concurrency;
using System.Reactive.Linq;
namespace Rocket.Surgery.Airframe.Core.Tests.AppStart
{
- internal class ScheduledTestOperation : TestOperation
- {
- private readonly IScheduler _scheduler;
- private readonly TimeSpan _delay;
-
- public ScheduledTestOperation(IScheduler scheduler, TimeSpan delay, bool willExecute = true)
- : base(willExecute)
- {
- _scheduler = scheduler;
- _delay = delay;
- }
-
- ///
- protected override IObservable Start() => Observable.Return(Unit.Default).Delay(_delay, _scheduler).Finally(() => Executed = true);
- }
-
internal class TestOperation : StartupOperationBase
{
private readonly bool _canExecute;
diff --git a/test/Core.Tests/Core.Tests.csproj b/test/Core.Tests/Core.Tests.csproj
index 795e38d7d..3f5743d33 100644
--- a/test/Core.Tests/Core.Tests.csproj
+++ b/test/Core.Tests/Core.Tests.csproj
@@ -2,7 +2,6 @@
net6.0
- enable
false
Rocket.Surgery.Airframe.Core.Tests
Rocket.Surgery.Airframe.Core.Tests
diff --git a/test/Core.Tests/Geofence/GeofenceServiceFixture.cs b/test/Core.Tests/Geofence/GeofenceServiceFixture.cs
deleted file mode 100644
index a636ddd94..000000000
--- a/test/Core.Tests/Geofence/GeofenceServiceFixture.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Rocket.Surgery.Extensions.Testing.Fixtures;
-
-namespace Rocket.Surgery.Airframe.Core.Tests.Geofence;
-
-internal class GeofenceServiceFixture : ITestFixtureBuilder
-{
- public static implicit operator GeofenceService(GeofenceServiceFixture fixture) => fixture.Build();
-
- private GeofenceService Build() => new GeofenceService();
-}
\ No newline at end of file
diff --git a/test/Core.Tests/Geofence/GeofenceServiceTests.cs b/test/Core.Tests/Geofence/GeofenceServiceTests.cs
deleted file mode 100644
index 13299d119..000000000
--- a/test/Core.Tests/Geofence/GeofenceServiceTests.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Rocket.Surgery.Airframe.Core.Tests.Geofence
-{
- public class GeofenceServiceTests
- {
-
- }
-}
\ No newline at end of file
diff --git a/test/Core.Tests/LoggerMock.cs b/test/Core.Tests/LoggerMock.cs
index 65ed7127e..eeb60db6f 100644
--- a/test/Core.Tests/LoggerMock.cs
+++ b/test/Core.Tests/LoggerMock.cs
@@ -1,21 +1,23 @@
using Microsoft.Extensions.Logging;
+using System;
using System.Reactive.Disposables;
using Xunit.Abstractions;
-namespace Rocket.Surgery.Airframe.Core.Tests;
-
-internal class LoggerMock : ILogger
+namespace Rocket.Surgery.Airframe.Core.Tests
{
- private readonly ITestOutputHelper _testOutputHelper;
+ internal class LoggerMock : ILogger
+ {
+ private readonly ITestOutputHelper _testOutputHelper;
- public LoggerMock(ITestOutputHelper testOutputHelper) => _testOutputHelper = testOutputHelper;
+ public LoggerMock(ITestOutputHelper testOutputHelper) => _testOutputHelper = testOutputHelper;
- public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => LogToOutputHelper(eventId, state, exception, formatter);
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => LogToOutputHelper(eventId, state, exception, formatter);
- private void LogToOutputHelper(EventId eventId, TState state, Exception exception, Func formatter) =>
- _testOutputHelper.WriteLine(formatter(state, exception));
+ private void LogToOutputHelper(EventId eventId, TState state, Exception exception, Func formatter) =>
+ _testOutputHelper.WriteLine(formatter(state, exception));
- public bool IsEnabled(LogLevel logLevel) => true;
+ public bool IsEnabled(LogLevel logLevel) => true;
- public IDisposable BeginScope(TState state) => Disposable.Empty;
+ public IDisposable BeginScope(TState state) => Disposable.Empty;
+ }
}
\ No newline at end of file