Skip to content

Commit 9b99dae

Browse files
authored
Add item updated event (#306)
* add updated event * tests * docs
1 parent 2dcd7ac commit 9b99dae

20 files changed

+373
-38
lines changed

BitFaster.Caching.UnitTests/Atomic/AtomicFactoryAsyncCacheTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class AtomicFactoryAsyncCacheTests
1818
private readonly AtomicFactoryAsyncCache<int, int> cache = new(new ConcurrentLru<int, AsyncAtomicFactory<int, int>>(capacity));
1919

2020
private List<ItemRemovedEventArgs<int, int>> removedItems = new();
21+
private List<ItemUpdatedEventArgs<int, int>> updatedItems = new();
2122

2223
[Fact]
2324
public void WhenInnerCacheIsNullCtorThrows()
@@ -65,7 +66,7 @@ public void WhenNoInnerEventsNoOuterEvents()
6566
}
6667

6768
[Fact]
68-
public void WhenEventHandlerIsRegisteredItIsFired()
69+
public void WhenRemovedEventHandlerIsRegisteredItIsFired()
6970
{
7071
this.cache.Events.Value.ItemRemoved += OnItemRemoved;
7172

@@ -75,6 +76,19 @@ public void WhenEventHandlerIsRegisteredItIsFired()
7576
this.removedItems.First().Key.Should().Be(1);
7677
}
7778

79+
[Fact]
80+
public void WhenUpdatedEventHandlerIsRegisteredItIsFired()
81+
{
82+
this.cache.Events.Value.ItemUpdated += OnItemUpdated;
83+
84+
this.cache.AddOrUpdate(1, 2);
85+
this.cache.AddOrUpdate(1, 3);
86+
87+
this.updatedItems.First().Key.Should().Be(1);
88+
this.updatedItems.First().OldValue.Should().Be(2);
89+
this.updatedItems.First().NewValue.Should().Be(3);
90+
}
91+
7892
[Fact]
7993
public void WhenKeyDoesNotExistAddOrUpdateAddsNewItem()
8094
{
@@ -193,5 +207,10 @@ private void OnItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
193207
{
194208
this.removedItems.Add(e);
195209
}
210+
211+
private void OnItemUpdated(object sender, ItemUpdatedEventArgs<int, int> e)
212+
{
213+
this.updatedItems.Add(e);
214+
}
196215
}
197216
}

BitFaster.Caching.UnitTests/Atomic/AtomicFactoryCacheTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class AtomicFactoryCacheTests
1818
private readonly AtomicFactoryCache<int, int> cache = new(new ConcurrentLru<int, AtomicFactory<int, int>>(capacity));
1919

2020
private List<ItemRemovedEventArgs<int, int>> removedItems = new();
21+
private List<ItemUpdatedEventArgs<int, int>> updatedItems = new();
2122

2223
[Fact]
2324
public void WhenInnerCacheIsNullCtorThrows()
@@ -54,7 +55,7 @@ public void WhenItemIsAddedThenLookedUpMetricsAreCorrect()
5455
}
5556

5657
[Fact]
57-
public void WhenEventHandlerIsRegisteredItIsFired()
58+
public void WhenRemovedEventHandlerIsRegisteredItIsFired()
5859
{
5960
this.cache.Events.Value.ItemRemoved += OnItemRemoved;
6061

@@ -64,6 +65,19 @@ public void WhenEventHandlerIsRegisteredItIsFired()
6465
this.removedItems.First().Key.Should().Be(1);
6566
}
6667

68+
[Fact]
69+
public void WhenUpdatedEventHandlerIsRegisteredItIsFired()
70+
{
71+
this.cache.Events.Value.ItemUpdated += OnItemUpdated;
72+
73+
this.cache.AddOrUpdate(1, 2);
74+
this.cache.AddOrUpdate(1, 3);
75+
76+
this.updatedItems.First().Key.Should().Be(1);
77+
this.updatedItems.First().OldValue.Should().Be(2);
78+
this.updatedItems.First().NewValue.Should().Be(3);
79+
}
80+
6781
[Fact]
6882
public void WhenNoInnerEventsNoOuterEvents()
6983
{
@@ -193,5 +207,10 @@ private void OnItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
193207
{
194208
this.removedItems.Add(e);
195209
}
210+
211+
private void OnItemUpdated(object sender, ItemUpdatedEventArgs<int, int> e)
212+
{
213+
this.updatedItems.Add(e);
214+
}
196215
}
197216
}

BitFaster.Caching.UnitTests/CacheEventProxyBaseTests.cs

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
64
using BitFaster.Caching.Atomic;
75
using FluentAssertions;
86
using Xunit;
@@ -15,6 +13,7 @@ public class CacheEventProxyBaseTests
1513
private EventProxy<int, int> eventProxy;
1614

1715
private List<ItemRemovedEventArgs<int, int>> removedItems = new();
16+
private List<ItemUpdatedEventArgs<int, int>> updatedItems = new();
1817

1918
public CacheEventProxyBaseTests()
2019
{
@@ -23,56 +22,107 @@ public CacheEventProxyBaseTests()
2322
}
2423

2524
[Fact]
26-
public void WhenEventHandlerIsRegisteredItIsFired()
25+
public void WheRemovedEventHandlerIsRegisteredItIsFired()
2726
{
2827
this.eventProxy.ItemRemoved += OnItemRemoved;
2928

30-
this.testCacheEvents.Fire(1, new AtomicFactory<int, int>(1), ItemRemovedReason.Removed);
29+
this.testCacheEvents.FireRemoved(1, new AtomicFactory<int, int>(1), ItemRemovedReason.Removed);
3130

3231
this.removedItems.First().Key.Should().Be(1);
3332
}
3433

3534
[Fact]
36-
public void WhenEventHandlerIsAddedThenRemovedItIsNotFired()
35+
public void WhenRemovedEventHandlerIsAddedThenRemovedItIsNotFired()
3736
{
3837
this.eventProxy.ItemRemoved += OnItemRemoved;
3938
this.eventProxy.ItemRemoved -= OnItemRemoved;
4039

41-
this.testCacheEvents.Fire(1, new AtomicFactory<int, int>(1), ItemRemovedReason.Removed);
40+
this.testCacheEvents.FireRemoved(1, new AtomicFactory<int, int>(1), ItemRemovedReason.Removed);
4241

4342
this.removedItems.Count.Should().Be(0);
4443
}
4544

4645
[Fact]
47-
public void WhenTwoEventHandlersAddedThenOneRemovedEventIsFired()
46+
public void WhenTwoRemovedEventHandlersAddedThenOneRemovedEventIsFired()
4847
{
4948
this.eventProxy.ItemRemoved += OnItemRemoved;
5049
this.eventProxy.ItemRemoved += OnItemRemovedThrow;
5150
this.eventProxy.ItemRemoved -= OnItemRemovedThrow;
5251

53-
this.testCacheEvents.Fire(1, new AtomicFactory<int, int>(1), ItemRemovedReason.Removed);
52+
this.testCacheEvents.FireRemoved(1, new AtomicFactory<int, int>(1), ItemRemovedReason.Removed);
5453

5554
this.removedItems.First().Key.Should().Be(1);
5655
}
5756

57+
[Fact]
58+
public void WheUpdatedEventHandlerIsRegisteredItIsFired()
59+
{
60+
this.eventProxy.ItemUpdated += OnItemUpdated;
61+
62+
this.testCacheEvents.FireUpdated(1, new AtomicFactory<int, int>(2), new AtomicFactory<int, int>(3));
63+
64+
this.updatedItems.First().Key.Should().Be(1);
65+
this.updatedItems.First().OldValue.Should().Be(2);
66+
this.updatedItems.First().NewValue.Should().Be(3);
67+
}
68+
69+
[Fact]
70+
public void WhenUpdatedEventHandlerIsAddedThenRemovedItIsNotFired()
71+
{
72+
this.eventProxy.ItemUpdated += OnItemUpdated;
73+
this.eventProxy.ItemUpdated -= OnItemUpdated;
74+
75+
this.testCacheEvents.FireUpdated(1, new AtomicFactory<int, int>(2), new AtomicFactory<int, int>(3));
76+
77+
this.updatedItems.Count.Should().Be(0);
78+
}
79+
80+
[Fact]
81+
public void WhenTwoUpdatedEventHandlersAddedThenOneRemovedEventIsFired()
82+
{
83+
this.eventProxy.ItemUpdated += OnItemUpdated;
84+
this.eventProxy.ItemUpdated += OnItemUpdatedThrow;
85+
this.eventProxy.ItemUpdated -= OnItemUpdatedThrow;
86+
87+
this.testCacheEvents.FireUpdated(1, new AtomicFactory<int, int>(2), new AtomicFactory<int, int>(3));
88+
89+
this.updatedItems.First().Key.Should().Be(1);
90+
}
91+
5892
private void OnItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
5993
{
6094
this.removedItems.Add(e);
6195
}
6296

97+
private void OnItemUpdated(object sender, ItemUpdatedEventArgs<int, int> e)
98+
{
99+
this.updatedItems.Add(e);
100+
}
101+
63102
private void OnItemRemovedThrow(object sender, ItemRemovedEventArgs<int, int> e)
64103
{
65104
throw new Exception("Should never happen");
66105
}
67106

107+
private void OnItemUpdatedThrow(object sender, ItemUpdatedEventArgs<int, int> e)
108+
{
109+
throw new Exception("Should never happen");
110+
}
111+
68112
private class TestCacheEvents<K, V> : ICacheEvents<K, AtomicFactory<K, V>>
69113
{
70114
public event EventHandler<ItemRemovedEventArgs<K, AtomicFactory<K, V>>> ItemRemoved;
115+
public event EventHandler<ItemUpdatedEventArgs<K, AtomicFactory<K, V>>> ItemUpdated;
71116

72-
public void Fire(K key, AtomicFactory<K, V> value, ItemRemovedReason reason)
117+
public void FireRemoved(K key, AtomicFactory<K, V> value, ItemRemovedReason reason)
73118
{
74119
ItemRemoved?.Invoke(this, new ItemRemovedEventArgs<K, AtomicFactory<K, V>>(key, value, reason));
75120
}
121+
122+
public void FireUpdated(K key, AtomicFactory<K, V> oldValue, AtomicFactory<K, V> newValue)
123+
{
124+
ItemUpdated?.Invoke(this, new ItemUpdatedEventArgs<K, AtomicFactory<K, V>>(key, oldValue, newValue));
125+
}
76126
}
77127

78128
private class EventProxy<K, V> : CacheEventProxyBase<K, AtomicFactory<K, V>, V>
@@ -86,6 +136,11 @@ protected override ItemRemovedEventArgs<K, V> TranslateOnRemoved(ItemRemovedEven
86136
{
87137
return new ItemRemovedEventArgs<K, V>(inner.Key, inner.Value.ValueIfCreated, inner.Reason);
88138
}
139+
140+
protected override ItemUpdatedEventArgs<K, V> TranslateOnUpdated(ItemUpdatedEventArgs<K, AtomicFactory<K, V>> inner)
141+
{
142+
return new ItemUpdatedEventArgs<K, V>(inner.Key, inner.OldValue.ValueIfCreated, inner.NewValue.ValueIfCreated);
143+
}
89144
}
90145
}
91146
}

BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
using FluentAssertions;
22
using BitFaster.Caching.Lru;
33
using System;
4+
using System.Collections;
45
using System.Collections.Generic;
56
using System.Linq;
6-
using System.Text;
77
using System.Threading.Tasks;
88
using Xunit;
99
using Xunit.Abstractions;
10-
using System.Collections;
1110

1211
namespace BitFaster.Caching.UnitTests.Lru
1312
{
@@ -23,12 +22,18 @@ public class ConcurrentLruTests
2322
private ValueFactory valueFactory = new ValueFactory();
2423

2524
private List<ItemRemovedEventArgs<int, int>> removedItems = new List<ItemRemovedEventArgs<int, int>>();
25+
private List<ItemUpdatedEventArgs<int, int>> updatedItems = new List<ItemUpdatedEventArgs<int, int>>();
2626

2727
private void OnLruItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
2828
{
2929
removedItems.Add(e);
3030
}
3131

32+
private void OnLruItemUpdated(object sender, ItemUpdatedEventArgs<int, int> e)
33+
{
34+
updatedItems.Add(e);
35+
}
36+
3237
public ConcurrentLruTests(ITestOutputHelper testOutputHelper)
3338
{
3439
this.testOutputHelper = testOutputHelper;
@@ -708,6 +713,55 @@ public void WhenKeyDoesNotExistAddOrUpdateMaintainsLruOrder()
708713
lru.WarmCount.Should().Be(1); // items must have been enqueued and cycled for one of them to reach the warm queue
709714
}
710715

716+
[Fact]
717+
public void WhenItemExistsAddOrUpdateFiresUpdateEvent()
718+
{
719+
var lruEvents = new ConcurrentLru<int, int>(1, new EqualCapacityPartition(6), EqualityComparer<int>.Default);
720+
lruEvents.Events.Value.ItemUpdated += OnLruItemUpdated;
721+
722+
lruEvents.AddOrUpdate(1, 2);
723+
lruEvents.AddOrUpdate(2, 3);
724+
725+
lruEvents.AddOrUpdate(1, 3);
726+
727+
this.updatedItems.Count.Should().Be(1);
728+
this.updatedItems[0].Key.Should().Be(1);
729+
this.updatedItems[0].OldValue.Should().Be(2);
730+
this.updatedItems[0].NewValue.Should().Be(3);
731+
}
732+
733+
[Fact]
734+
public void WhenItemExistsTryUpdateFiresUpdateEvent()
735+
{
736+
var lruEvents = new ConcurrentLru<int, int>(1, new EqualCapacityPartition(6), EqualityComparer<int>.Default);
737+
lruEvents.Events.Value.ItemUpdated += OnLruItemUpdated;
738+
739+
lruEvents.AddOrUpdate(1, 2);
740+
lruEvents.AddOrUpdate(2, 3);
741+
742+
lruEvents.TryUpdate(1, 3);
743+
744+
this.updatedItems.Count.Should().Be(1);
745+
this.updatedItems[0].Key.Should().Be(1);
746+
this.updatedItems[0].OldValue.Should().Be(2);
747+
this.updatedItems[0].NewValue.Should().Be(3);
748+
}
749+
750+
[Fact]
751+
public void WhenItemUpdatedEventIsUnregisteredEventIsNotFired()
752+
{
753+
var lruEvents = new ConcurrentLru<int, int>(1, 6, EqualityComparer<int>.Default);
754+
755+
lruEvents.Events.Value.ItemUpdated += OnLruItemUpdated;
756+
lruEvents.Events.Value.ItemUpdated -= OnLruItemUpdated;
757+
758+
lruEvents.AddOrUpdate(1, 2);
759+
lruEvents.AddOrUpdate(1, 2);
760+
lruEvents.AddOrUpdate(1, 2);
761+
762+
updatedItems.Count.Should().Be(0);
763+
}
764+
711765
[Fact]
712766
public void WhenCacheIsEmptyClearIsNoOp()
713767
{

BitFaster.Caching.UnitTests/Lru/NoTelemetryPolicyTests.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
using FluentAssertions;
22
using BitFaster.Caching.Lru;
3-
using System;
4-
using System.Collections.Generic;
5-
using System.Text;
63
using Xunit;
74

85
namespace BitFaster.Caching.UnitTests.Lru
@@ -62,7 +59,7 @@ public void IncrementTotalCountIsNoOp()
6259
[Fact]
6360
public void OnItemUpdatedIsNoOp()
6461
{
65-
counter.Invoking(c => c.OnItemUpdated(1, 2)).Should().NotThrow();
62+
counter.Invoking(c => c.OnItemUpdated(1, 2, 3)).Should().NotThrow();
6663
}
6764

6865
[Fact]
@@ -72,14 +69,25 @@ public void OnItemRemovedIsNoOp()
7269
}
7370

7471
[Fact]
75-
public void RegisterEventHandlerIsNoOp()
72+
public void RegisterRemovedEventHandlerIsNoOp()
7673
{
7774
counter.ItemRemoved += OnItemRemoved;
7875
counter.ItemRemoved -= OnItemRemoved;
7976
}
8077

78+
[Fact]
79+
public void RegisterUpdateEventHandlerIsNoOp()
80+
{
81+
counter.ItemUpdated += OnItemUpdated;
82+
counter.ItemUpdated -= OnItemUpdated;
83+
}
84+
8185
private void OnItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
8286
{
8387
}
88+
89+
private void OnItemUpdated(object sender, ItemUpdatedEventArgs<int, int> e)
90+
{
91+
}
8492
}
8593
}

0 commit comments

Comments
 (0)