Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,25 @@ public async Task AsyncAlternateLookupGetOrAddAsyncWithArgUsesActualKeyOnMissAnd
cache.TryGet("42", out var value).Should().BeTrue();
value.Should().Be("value-42");
}

[Fact]
public async Task AsyncAlternateLookupGetOrAddAsyncWithRefStructArgUsesActualKeyOnMissAndHit()
{
var alternate = cache.GetAsyncAlternateLookup<ReadOnlySpan<char>>();
var factoryCalls = 0;

var result = await alternate.GetOrAddAsync("42".AsSpan(), static (key, argument) => Task.FromResult($"{key}-{argument.Length}"), "xx".AsSpan());
result.Should().Be("42-2");

result = await alternate.GetOrAddAsync("42".AsSpan(), (key, argument) =>
{
factoryCalls++;
return Task.FromResult($"{key}-{argument.Length}");
}, "ignored".AsSpan());

result.Should().Be("42-2");
factoryCalls.Should().Be(0);
}
}
}
#endif
10 changes: 10 additions & 0 deletions BitFaster.Caching.UnitTests/Atomic/AtomicFactoryAsyncCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ public async Task WhenItemIsAddedWithArgValueIsCorrect()
value.Should().Be(3);
}

#if NET9_0_OR_GREATER
[Fact]
public async Task GetOrAddAsyncWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
var value = await this.cache.GetOrAddAsync(1, static (key, argument) => Task.FromResult(key + argument.Length), "xx".AsSpan());

value.Should().Be(3);
}
#endif

#if NET9_0_OR_GREATER
[Fact]
public void ComparerReturnsConfiguredComparer()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ public void AlternateLookupGetOrAddWithArgUsesActualKeyOnMissAndHit()
factoryCalls.Should().Be(1);
}

[Fact]
public void AlternateLookupGetOrAddWithRefStructArgUsesActualKeyOnMissAndHit()
{
var alternate = cache.GetAlternateLookup<ReadOnlySpan<char>>();
var factoryCalls = 0;

var result = alternate.GetOrAdd("42".AsSpan(), static (key, argument) => $"{key}-{argument.Length}", "xx".AsSpan());
result.Should().Be("42-2");

result = alternate.GetOrAdd("42".AsSpan(), (key, argument) =>
{
factoryCalls++;
return $"{key}-{argument.Length}";
}, "ignored".AsSpan());

result.Should().Be("42-2");
factoryCalls.Should().Be(0);
}

[Fact]
public void AlternateLookupTryUpdateReturnsFalseForMissingKeyAndUpdatesExistingValue()
{
Expand Down
8 changes: 8 additions & 0 deletions BitFaster.Caching.UnitTests/Atomic/AtomicFactoryCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ public void WhenItemIsAddedWithArgValueIsCorrect()
value.Should().Be(3);
}

#if NET9_0_OR_GREATER
[Fact]
public void GetOrAddWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
this.cache.GetOrAdd(1, static (key, argument) => key + argument.Length, "xx".AsSpan()).Should().Be(3);
}
#endif

#if NET9_0_OR_GREATER
[Fact]
public void ComparerReturnsConfiguredComparer()
Expand Down
54 changes: 39 additions & 15 deletions BitFaster.Caching.UnitTests/CacheTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BitFaster.Caching.Lru;
using FluentAssertions;
using Moq;
using Xunit;

Expand All @@ -11,10 +12,10 @@ namespace BitFaster.Caching.UnitTests
// Tests for interface default implementations.
public class CacheTests
{
// backcompat: remove conditional compile
#if NETCOREAPP3_0_OR_GREATER
[Fact]
public void WhenCacheInterfaceDefaultGetOrAddFallback()
// backcompat: remove conditional compile
#if NETCOREAPP3_0_OR_GREATER && !NET9_0_OR_GREATER
[Fact]
public void WhenCacheInterfaceDefaultGetOrAddFallback()
{
var cache = new Mock<ICache<int, int>>();
cache.CallBase = true;
Expand Down Expand Up @@ -51,8 +52,8 @@ public void WhenCacheInterfaceDefaultTryRemoveKeyValueThrows()
}

[Fact]
public async Task WhenAsyncCacheInterfaceDefaultGetOrAddFallback()
{
public async Task WhenAsyncCacheInterfaceDefaultGetOrAddFallback()
{
var cache = new Mock<IAsyncCache<int, int>>();
cache.CallBase = true;

Expand All @@ -64,11 +65,14 @@ public async Task WhenAsyncCacheInterfaceDefaultGetOrAddFallback()
(k, a) => Task.FromResult(k + a),
2);

r.Should().Be(3);
}

[Fact]
public void WhenAsyncCacheInterfaceDefaultTryRemoveKeyThrows()
r.Should().Be(3);
}
#endif

#if NETCOREAPP3_0_OR_GREATER

[Fact]
public void WhenAsyncCacheInterfaceDefaultTryRemoveKeyThrows()
{
var cache = new Mock<IAsyncCache<int, int>>();
cache.CallBase = true;
Expand Down Expand Up @@ -138,6 +142,26 @@ public async Task WhenScopedAsyncCacheInterfaceDefaultGetOrAddFallback()
}

#if NET9_0_OR_GREATER
[Fact]
public void GetOrAddWithRefStructArgViaCacheInterfaceWhenValueMissingReturnsCreatedValue()
{
ICache<int, int> cache = new ClassicLru<int, int>(3);

var value = cache.GetOrAdd(1, static (key, argument) => key + argument.Length, "xx".AsSpan());

value.Should().Be(3);
}

[Fact]
public async Task GetOrAddAsyncWithRefStructArgViaAsyncCacheInterfaceWhenValueMissingReturnsCreatedValue()
{
IAsyncCache<int, int> cache = new ClassicLru<int, int>(3);

var value = await cache.GetOrAddAsync(1, static (key, argument) => Task.FromResult(key + argument.Length), "xx".AsSpan());

value.Should().Be(3);
}

[Fact]
public void WhenCacheInterfaceDefaultGetAlternateLookupThrows()
{
Expand Down
48 changes: 34 additions & 14 deletions BitFaster.Caching.UnitTests/Lfu/ConcurrentLfuTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,24 @@ public void WhenKeyIsRequestedItIsCreatedAndCached()
}

[Fact]
public void WhenKeyIsRequestedWithArgItIsCreatedAndCached()
{
var result1 = cache.GetOrAdd(1, valueFactory.Create, 9);
var result2 = cache.GetOrAdd(1, valueFactory.Create, 17);
public void WhenKeyIsRequestedWithArgItIsCreatedAndCached()
{
var result1 = cache.GetOrAdd(1, valueFactory.Create, 9);
var result2 = cache.GetOrAdd(1, valueFactory.Create, 17);

valueFactory.timesCalled.Should().Be(1);
result1.Should().Be(result2);
}
valueFactory.timesCalled.Should().Be(1);
result1.Should().Be(result2);
}

#if NET9_0_OR_GREATER
[Fact]
public void GetOrAddWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
var result = cache.GetOrAdd(1, static (key, argument) => key + argument.Length, "xx".AsSpan());

result.Should().Be(3);
}
#endif

[Fact]
public async Task WhenKeyIsRequesteItIsCreatedAndCachedAsync()
Expand All @@ -99,14 +109,24 @@ public async Task WhenKeyIsRequesteItIsCreatedAndCachedAsync()
}

[Fact]
public async Task WhenKeyIsRequestedWithArgItIsCreatedAndCachedAsync()
{
var result1 = await cache.GetOrAddAsync(1, valueFactory.CreateAsync, 9);
var result2 = await cache.GetOrAddAsync(1, valueFactory.CreateAsync, 17);
public async Task WhenKeyIsRequestedWithArgItIsCreatedAndCachedAsync()
{
var result1 = await cache.GetOrAddAsync(1, valueFactory.CreateAsync, 9);
var result2 = await cache.GetOrAddAsync(1, valueFactory.CreateAsync, 17);

valueFactory.timesCalled.Should().Be(1);
result1.Should().Be(result2);
}
valueFactory.timesCalled.Should().Be(1);
result1.Should().Be(result2);
}

#if NET9_0_OR_GREATER
[Fact]
public async Task GetOrAddAsyncWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
var result = await cache.GetOrAddAsync(1, static (key, argument) => Task.FromResult(key + argument.Length), "xx".AsSpan());

result.Should().Be(3);
}
#endif

[Fact]
public void WhenItemsAddedExceedsCapacityItemsAreDiscarded()
Expand Down
20 changes: 20 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ public void WhenKeyIsRequestedWithArgItIsCreatedAndCached()
result1.Should().Be(result2);
}

#if NET9_0_OR_GREATER
[Fact]
public void GetOrAddWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
var result = lru.GetOrAdd(1, static (key, argument) => $"{key}-{argument.Length}", "xx".AsSpan());

result.Should().Be("1-2");
}
#endif

[Fact]
public async Task WhenKeyIsRequesteItIsCreatedAndCachedAsync()
{
Expand All @@ -223,6 +233,16 @@ public async Task WhenKeyIsRequestedWithArgItIsCreatedAndCachedAsync()
result1.Should().Be(result2);
}

#if NET9_0_OR_GREATER
[Fact]
public async Task GetOrAddAsyncWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
var result = await lru.GetOrAddAsync(1, static (key, argument) => Task.FromResult($"{key}-{argument.Length}"), "xx".AsSpan());

result.Should().Be("1-2");
}
#endif

[Fact]
public void WhenDifferentKeysAreRequestedValueIsCreatedForEach()
{
Expand Down
30 changes: 24 additions & 6 deletions BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,30 @@ public void WhenItemIsAddedCountIsCorrect()
}

[Fact]
public async Task WhenItemIsAddedCountIsCorrectAsync()
{
lru.Count.Should().Be(0);
await lru.GetOrAddAsync(0, valueFactory.CreateAsync);
lru.Count.Should().Be(1);
}
public async Task WhenItemIsAddedCountIsCorrectAsync()
{
lru.Count.Should().Be(0);
await lru.GetOrAddAsync(0, valueFactory.CreateAsync);
lru.Count.Should().Be(1);
}

#if NET9_0_OR_GREATER
[Fact]
public void GetOrAddWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
var result = lru.GetOrAdd(1, static (key, argument) => $"{key}-{argument.Length}", "xx".AsSpan());

result.Should().Be("1-2");
}

[Fact]
public async Task GetOrAddAsyncWithRefStructArgWhenValueMissingReturnsCreatedValue()
{
var result = await lru.GetOrAddAsync(1, static (key, argument) => Task.FromResult($"{key}-{argument.Length}"), "xx".AsSpan());

result.Should().Be("1-2");
}
#endif

[Fact]
public void WhenItemsAddedKeysContainsTheKeys()
Expand Down
Loading
Loading