Skip to content

Commit

Permalink
feature: add Listener (#253)
Browse files Browse the repository at this point in the history
* feature: cant log things out

* feature: add Listener

* add notifier interface for testing

* feature: getting these tests in order

* feature: not broke
  • Loading branch information
RLittlesII authored Feb 13, 2024
1 parent a047477 commit 333c252
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/Core/Core.csproj.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=appstart/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=connectivity/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=geofence/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=listeners/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=locations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=locations_005Cevents/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=operations/@EntryIndexedValue">True</s:Boolean>
Expand Down
34 changes: 34 additions & 0 deletions src/Core/DisposableBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Reactive.Disposables;

Check warning on line 2 in src/Core/DisposableBase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

namespace Rocket.Surgery.Airframe;

/// <summary>
/// Represents a disposable base object.
/// </summary>
public abstract class DisposableBase : IDisposable
{
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Gets the garbage disposables.
/// </summary>
protected CompositeDisposable Garbage { get; } = new();

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <param name="disposing">Is disposing.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Garbage.Dispose();
}
}
}
28 changes: 28 additions & 0 deletions src/Core/Listeners/IListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Reactive;

Check warning on line 2 in src/Core/Listeners/IListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

namespace Rocket.Surgery.Airframe;

/// <summary>
/// Interface that represents an object that can connect and disconnect from as observable sequence.
/// </summary>
public interface IListener : IListener<Unit>;

/// <summary>
/// Interface that represents an object that can connect and disconnect from as observable sequence.
/// </summary>
/// <typeparam name="T">The type of object to listen for.</typeparam>
public interface IListener<out T>
{
/// <summary>
/// Start listening.
/// </summary>
/// <returns>A observable sequence of <see cref="T"/>.</returns>
IObservable<T> Listen();

/// <summary>
/// Ignore the information.
/// </summary>
/// <returns>A completion notification.</returns>
IObservable<Unit> Ignore();
}
84 changes: 84 additions & 0 deletions src/Core/Listeners/Listener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Reactive;

Check warning on line 2 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Reactive.Disposables;

Check warning on line 3 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Reactive.Linq;

Check warning on line 4 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Reactive.Subjects;

Check warning on line 5 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using JetBrains.Annotations;

Check warning on line 6 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using Microsoft.Extensions.Logging;

Check warning on line 7 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

namespace Rocket.Surgery.Airframe;

/// <summary>
/// Base extension point for an <see cref="IListener"/>.
/// </summary>
[PublicAPI]
public abstract class Listener : Listener<Unit>, IListener
{
/// <summary>
/// Initializes a new instance of the <see cref="Listener"/> class.
/// </summary>
/// <param name="loggerFactory">The logger factory.</param>
protected Listener(ILoggerFactory loggerFactory)
: base(loggerFactory)
{
}
}

/// <summary>
/// Base extension point for an <see cref="IListener{T}"/>.
/// </summary>
/// <typeparam name="T">The observable sequence type.</typeparam>
[PublicAPI]
public abstract class Listener<T> : DisposableBase, IListener<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="Listener{T}"/> class.
/// </summary>
/// <param name="loggerFactory">The logger factory.</param>
protected Listener(ILoggerFactory loggerFactory)
{
_stop = Disposable.Empty;
_stop.DisposeWith(Garbage);
Logger = loggerFactory.CreateLogger(GetType());
Listening = Observable.Empty<T>().Publish();
}

/// <inheritdoc />
IObservable<T> IListener<T>.Listen() => StartListening();

/// <inheritdoc cref="IListener"/>
IObservable<Unit> IListener<T>.Ignore() => StopListening();

/// <summary>
/// Connect to the observable sequence.
/// </summary>
/// <returns>Returns a signal.</returns>
protected virtual IConnectableObservable<T> Listen() => Listening;

/// <summary>
/// Disconnects from the observable sequence.
/// </summary>
/// <returns>Returns a signal.</returns>
protected virtual IObservable<Unit> Ignore() => Observable.Return(Unit.Default);

/// <summary>
/// Gets or sets the <see cref="IConnectableObservable{T}"/> that the listener listens to.

Check warning on line 65 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Invalid XML documentation comment

Cannot resolve symbol 'IConnectableObservable'
/// </summary>
protected IConnectableObservable<T> Listening { get; set; }

/// <summary>
/// Gets the logger.
/// </summary>
protected ILogger Logger { get; }

Check warning on line 72 in src/Core/Listeners/Listener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Auto-property accessor is never used (non-private accessibility)

Auto-property accessor 'Logger.get' is never used

private IObservable<T> StartListening() => Observable.Create<T>(observer =>
{
_stop = Listen().Connect();
return Listen().Subscribe(observer);
});

private IObservable<Unit> StopListening() => Ignore().Finally(() => _stop.Dispose());

private IDisposable _stop;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
namespace Rocket.Surgery.Airframe.ViewModels;

/// <summary>
/// Interface representing an element that subscribes to network connectivity changes.
/// Interface representing an element that subscribes to network state changes.
/// </summary>
public interface INetworkAware
public interface IHaveNetworkState
{
/// <summary>
/// Gets the connectivity.
/// Gets the network state changes.
/// </summary>
IObservable<NetworkStateChangedEvent> Connectivity { get; }
IObservable<NetworkStateChangedEvent> NetworkState { get; }
}
14 changes: 14 additions & 0 deletions test/Airframe.Testing/INotifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Airframe.Testing;

/// <summary>
/// Interface representing a thing™ that can notify.
/// </summary>
/// <typeparam name="T">The notification type.</typeparam>
public interface INotifier<T>
{
/// <summary>
/// Notifies an item to a subscriber.
/// </summary>
/// <param name="item">The item.</param>
void Notify(T item);
}
56 changes: 56 additions & 0 deletions test/Core.Tests/Listeners/ListenerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using FluentAssertions;

Check warning on line 1 in test/Core.Tests/Listeners/ListenerTests.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System;

Check warning on line 2 in test/Core.Tests/Listeners/ListenerTests.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Reactive;

Check warning on line 3 in test/Core.Tests/Listeners/ListenerTests.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using Xunit;

Check warning on line 4 in test/Core.Tests/Listeners/ListenerTests.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

namespace Rocket.Surgery.Airframe.Core.Tests.Listeners;

public class ListenerTests
{
[Fact]
public void GivenListen_When_ThenResultNotNull()
{
// Given
Unit? result = null;
TestListener sut = new TestListenerFixture();
using var _ = sut.As<IListener>().Listen().Subscribe(actual => result = actual);

// When
sut.Notify(Unit.Default);

// Then
result.Should().NotBeNull();
}

[Fact]
public void GivenListen_WhenIgnore_ThenResultNull()
{
// Given
Unit? result = null;
TestListener sut = new TestListenerFixture();
using var _ = sut.As<IListener>().Listen().Subscribe(actual => result = actual);

// When
using var __ = sut.As<IListener>().Ignore().Subscribe();
sut.Notify(Unit.Default);

// Then
result.Should().BeNull();
}

[Fact]
public void GivenIgnore_WhenListen_ThenResultNotNull()
{
// Given
Unit? result = null;
TestListener sut = new TestListenerFixture();
using var _ = sut.As<IListener>().Ignore().Subscribe();

// When
using var __ = sut.As<IListener>().Listen().Subscribe(actual => result = actual);
sut.Notify(Unit.Default);

// Then
result.Should().NotBeNull();
}
}
23 changes: 23 additions & 0 deletions test/Core.Tests/Listeners/TestListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Airframe.Testing;

Check warning on line 1 in test/Core.Tests/Listeners/TestListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using Microsoft.Extensions.Logging;

Check warning on line 2 in test/Core.Tests/Listeners/TestListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using ReactiveMarbles.Extensions;

Check warning on line 3 in test/Core.Tests/Listeners/TestListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Reactive;

Check warning on line 4 in test/Core.Tests/Listeners/TestListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Reactive.Linq;

Check warning on line 5 in test/Core.Tests/Listeners/TestListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Reactive.Subjects;

Check warning on line 6 in test/Core.Tests/Listeners/TestListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

namespace Rocket.Surgery.Airframe.Core.Tests.Listeners;

internal class TestListener : Listener, INotifier<Unit>
{
public TestListener(ILoggerFactory loggerFactory)
: base(loggerFactory)
{
_subject = new Subject<Unit>().DisposeWith(Garbage);
Listening = _subject.AsObservable().Publish();
}

/// <inheritdoc/>

Check warning on line 19 in test/Core.Tests/Listeners/TestListener.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Usage of <inheritdoc /> is invalid

Usage of is invalid: No base candidate to inherit from
public void Notify(Unit item) => _subject.OnNext(item);

private readonly ISubject<Unit> _subject;
}
14 changes: 14 additions & 0 deletions test/Core.Tests/Listeners/TestListenerFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Logging;

Check warning on line 1 in test/Core.Tests/Listeners/TestListenerFixture.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using Microsoft.Extensions.Logging.Abstractions;

Check warning on line 2 in test/Core.Tests/Listeners/TestListenerFixture.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using Rocket.Surgery.Extensions.Testing.Fixtures;

Check warning on line 3 in test/Core.Tests/Listeners/TestListenerFixture.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

namespace Rocket.Surgery.Airframe.Core.Tests.Listeners;

internal class TestListenerFixture : ITestFixtureBuilder
{
public static implicit operator TestListener(TestListenerFixture fixture) => fixture.Build();

private TestListener Build() => new TestListener(_loggerFactory);

private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance;
}
9 changes: 7 additions & 2 deletions test/directory.build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="coverlet.msbuild" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="Microsoft.Reactive.Testing"/>
<PackageReference Include="Rocket.Surgery.Extensions.Testing.NSubstitute"/>
<PackageReference Include="NSubstitute" />
<PackageReference Include="ReactiveMarbles.Mvvm"/>
<PackageReference Include="ReactiveUI.Testing"/>
<PackageReference Include="Rocket.Surgery.Extensions.Testing.NSubstitute"/>
<PackageReference Include="Rocket.Surgery.Extensions.Testing.Fixtures" />
<PackageReference Include="xunit"/>
<PackageReference Include="xunit.runner.visualstudio"/>
<PackageReference Include="coverlet.collector"/>
<PackageReference Include="XunitXml.TestLogger" />
</ItemGroup>

<Import Project="$([MSBuild]::GetPathOfFileAbove('directory.build.props', '$(MSBuildThisFileDirectory)../'))"/>
Expand Down
10 changes: 0 additions & 10 deletions test/directory.build.targets
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
<Project>
<ItemGroup>
<PackageReference Include="Rocket.Surgery.Extensions.Testing.Fixtures" />
<PackageReference Include="coverlet.collector" />
<PackageReference Include="coverlet.msbuild" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="XunitXml.TestLogger" />
</ItemGroup>
<Import Project="$([MSBuild]::GetPathOfFileAbove('directory.build.targets', '$(MSBuildThisFileDirectory)../'))" />
</Project>

0 comments on commit 333c252

Please sign in to comment.