Skip to content

Commit

Permalink
EventBuffer implement ICollection<object> (#36)
Browse files Browse the repository at this point in the history
* To improve Copy actions of the event buffer, implement ICollection, but mostly explicit.
* Implement IReadOnlyCollection<object>.
  • Loading branch information
Corniel committed Jun 7, 2023
1 parent f985623 commit 33652a6
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 7 deletions.
29 changes: 28 additions & 1 deletion src/Qowaiv.DomainModel/EventBuffer_TId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
/// </typeparam>
[DebuggerDisplay("{DebuggerDisplay}")]
[DebuggerTypeProxy(typeof(CollectionDebugView))]
public readonly struct EventBuffer<TId> : IEnumerable<object>
public readonly struct EventBuffer<TId> : IReadOnlyCollection<object>, ICollection<object>
{
private readonly AppendOnlyCollection Buffer;
private readonly int Offset;
Expand All @@ -51,6 +51,13 @@ internal EventBuffer(TId aggregateId, int offset, int committed, AppendOnlyColle
/// <summary>Gets the committed version of the event buffer.</summary>
public int CommittedVersion { get; }

/// <summary>Gets the number of events in the event buffer.</summary>
/// <remarks>
/// This number is not the version. It can be lower when the event buffer has
/// been created using an initial version.
/// </remarks>
public int Count => Buffer.Count;

/// <summary>Get all committed events in the event buffer.</summary>
public IEnumerable<object> Committed => Buffer.Take(CommittedVersion - Offset);

Expand Down Expand Up @@ -98,6 +105,13 @@ public IEnumerable<TStoredEvent> SelectUncommitted<TStoredEvent>(ConvertToStored
return Uncommitted.Select((@event, index) => convert(self.AggregateId, self.CommittedVersion + index + 1, @event));
}

/// <inheritdoc />
public void CopyTo(object[] array, int arrayIndex) => Buffer.CopyTo(array, arrayIndex);

/// <inheritdoc />
[Pure]
public bool Contains(object item) => Buffer.Contains(item);

/// <inheritdoc cref="IEnumerable{T}.GetEnumerator()" />
[Pure]
public Enumerator GetEnumerator() => Buffer.GetEnumerator();
Expand All @@ -116,4 +130,17 @@ internal string DebuggerDisplay
=> Version == CommittedVersion
? $"Version: {Version}, Aggregate: {AggregateId}"
: $"Version: {Version} (Committed: {CommittedVersion}), Aggregate: {AggregateId}";

/// <inheritdoc />
bool ICollection<object>.IsReadOnly => true;

/// <inheritdoc />
void ICollection<object>.Add(object item) => throw new NotSupportedException();

/// <inheritdoc />
void ICollection<object>.Clear() => throw new NotSupportedException();

/// <inheritdoc />
[Pure]
bool ICollection<object>.Remove(object item) => throw new NotSupportedException();
}
9 changes: 9 additions & 0 deletions src/Qowaiv.DomainModel/Internal/AppendOnlyCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ public AppendOnlyCollection Add(object? item)
}
}

/// <inheritdoc cref="ICollection.CopyTo(Array, int)" />
public void CopyTo(object[] array, int arrayIndex)
{
if (Count > 0)
{
Array.Copy(Buffer, 0, array, arrayIndex, Count);
}
}

/// <summary>Returns a specified range of contiguous elements from the collection.</summary>
[Pure]
public Enumerator Take(int count) => new(Buffer, Math.Min(count, Count));
Expand Down
3 changes: 2 additions & 1 deletion src/Qowaiv.DomainModel/Qowaiv.DomainModel.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
<PackageReleaseNotes>
NextRelease
- Re-implemented ImmutableCollection. #34 (breaking)
- EventStream as struct. #34 (breaking)
- EventBuffer as struct. #34 (breaking)
- EventBuffer implements ICollection&lt;object&gt;. #36
- EventDispatcher without dynamics. #34 (breaking)
- Enable annotations. #31
- Publish .NET 7.0. #29
Expand Down
113 changes: 108 additions & 5 deletions test/Qowaiv.DomainModel.UnitTests/Event_buffer_specs.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Qowaiv.DomainModel;
using Qowaiv.DomainModel.UnitTests;
using NUnit.Framework.Internal.Execution;
using Qowaiv.DomainModel;
using Qowaiv.DomainModel.UnitTests.Models;

namespace Event_buffer_specs;

Expand Down Expand Up @@ -78,6 +79,9 @@ public class IsEmpty
[Test]
public void True_for_empty_buffer() => EventBuffer.Empty(17).IsEmpty.Should().BeTrue();

[Test]
public void True_for_empty_buffer_with_offset() => EventBuffer.Empty(17, version: 2000).IsEmpty.Should().BeTrue();

[Test]
public void False_for_non_empty_buffer() => EventBuffer.Empty(17).Add(new EmptyEvent()).IsEmpty.Should().BeFalse();
}
Expand Down Expand Up @@ -136,8 +140,107 @@ public void committed_only()
}
}

[EmptyTestClass]
internal record EmptyEvent();
public class Implements_ICollection
{
[Test]
public void Count_is_equal_to_buffer_count()
{
var collection = EventBuffer.Empty(17).Add(new[] { 1, 2, 3, 4 });
collection.Count.Should().Be(4);
}

internal record StoredEvent(object Id, int Version, object Payload);
[Test]
public void returns_true_for_contained_events()
{
var @event = new EmptyEvent();
var buffer = EventBuffer.Empty(17).Add(@event);
buffer.Contains(@event).Should().BeTrue();
}

[Test]
public void returns_false_for_contained_events()
{
var buffer = EventBuffer.Empty(17).Add(new EmptyEvent());
buffer.Contains(new object()).Should().BeFalse();
}

[Test]
public void Copies_events_to_other_array()
{
var @event0 = new EmptyEvent();
var @event1 = new EmptyEvent();
var buffer = EventBuffer.Empty(17).Add(@event0).Add(event1);

var array = new object[5];

buffer.CopyTo(array, 1);

array.Should().BeEquivalentTo(new object?[]
{
null,
event0,
event1,
null,
null
});
}

[Test]
public void Copies_nothing_when_empty()
{
var buffer = default(EventBuffer<int>);

var array = new object[5];

buffer.CopyTo(array, 1);

array.Should().BeEquivalentTo(new object?[]
{
null,
null,
null,
null,
null
});
}

/// <remarks>
/// This is only exposed when explicitly <see cref="ICollection{object}"/>.
/// </remarks>
public class Explicitly
{
[Test]
public void Is_read_only()
{
ICollection<object> collection = EventBuffer.Empty(17);
collection.IsReadOnly.Should().BeTrue();
}

public class Does_not_support
{
[Test]
public void Add()
{
ICollection<object> collection = EventBuffer.Empty(17);
collection.Invoking(c => c.Add(new EmptyEvent()))
.Should().Throw<NotSupportedException>();
}

[Test]
public void Remove()
{
ICollection<object> collection = EventBuffer.Empty(17);
collection.Invoking(c => c.Remove(new EmptyEvent()))
.Should().Throw<NotSupportedException>();
}

[Test]
public void Clear()
{
ICollection<object> collection = EventBuffer.Empty(17);
collection.Invoking(c => c.Clear())
.Should().Throw<NotSupportedException>();
}
}
}
}
6 changes: 6 additions & 0 deletions test/Qowaiv.DomainModel.UnitTests/Models/_events.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Qowaiv.DomainModel.UnitTests.Models;

[EmptyTestClass]
internal record EmptyEvent();

internal record StoredEvent(object Id, int Version, object Payload);

0 comments on commit 33652a6

Please sign in to comment.