Skip to content
Merged
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
52 changes: 52 additions & 0 deletions BitFaster.Caching.UnitTests/Atomic/AsyncAtomicFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,58 @@ public async Task WhenCallersRunConcurrentlyResultIsFromWinner()
(await second).Should().Be(result);

winnerCount.Should().Be(1);
}

[Fact]
public void WhenValueNotCreatedHashCodeIsZero()
{
new AsyncAtomicFactory<int, int>()
.GetHashCode()
.Should().Be(0);
}

[Fact]
public void WhenValueCreatedHashCodeIsValueHashCode()
{
new AsyncAtomicFactory<int, int>(1)
.GetHashCode()
.Should().Be(1);
}

[Fact]
public void WhenValueNotCreatedEqualsFalse()
{
var a = new AsyncAtomicFactory<int, int>();
var b = new AsyncAtomicFactory<int, int>();

a.Equals(b).Should().BeFalse();
}

[Fact]
public void WhenOtherValueNotCreatedEqualsFalse()
{
var a = new AsyncAtomicFactory<int, int>(1);
var b = new AsyncAtomicFactory<int, int>();

a.Equals(b).Should().BeFalse();
}

[Fact]
public void WhenArgNullEqualsFalse()
{
new AsyncAtomicFactory<int, int>(1)
.Equals(null)
.Should().BeFalse();
}

[Fact]
public void WhenArgObjectValuesAreSameEqualsTrue()
{
object other = new AsyncAtomicFactory<int, int>(1);

new AsyncAtomicFactory<int, int>(1)
.Equals(other)
.Should().BeTrue();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ namespace BitFaster.Caching.UnitTests.Atomic
public class AtomicFactoryAsyncCacheTests
{
private const int capacity = 6;
private readonly AtomicFactoryAsyncCache<int, int> cache = new(new ConcurrentLru<int, AsyncAtomicFactory<int, int>>(capacity));
private readonly ConcurrentLru<int, AsyncAtomicFactory<int, int>> innerCache = new(capacity);
private readonly AtomicFactoryAsyncCache<int, int> cache;

private List<ItemRemovedEventArgs<int, int>> removedItems = new();
private List<ItemUpdatedEventArgs<int, int>> updatedItems = new();

public AtomicFactoryAsyncCacheTests()
{
cache = new(innerCache);
}

[Fact]
public void WhenInnerCacheIsNullCtorThrows()
{
Expand Down Expand Up @@ -250,8 +256,53 @@ public async Task WhenFactoryThrowsEmptyKeyIsNotEnumerable()
catch { }

cache.Keys.Count().Should().Be(0);
}

// backcompat: remove conditional compile
#if NETCOREAPP3_0_OR_GREATER
[Fact]
public void WhenRemovedValueIsReturned()
{
this.cache.AddOrUpdate(1, 1);
this.cache.TryRemove(1, out var value);

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

[Fact]
public void WhenNotRemovedValueIsDefault()
{
this.cache.AddOrUpdate(1, 1);
this.cache.TryRemove(2, out var value);

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

[Fact]
public void WhenRemoveKeyValueAndValueDoesntMatchDontRemove()
{
this.cache.AddOrUpdate(1, 1);
this.cache.TryRemove(new KeyValuePair<int, int>(1, 2)).Should().BeFalse();
}

[Fact]
public void WhenRemoveKeyValueAndValueDoesMatchThenRemove()
{
this.cache.AddOrUpdate(1, 1);
this.cache.TryRemove(new KeyValuePair<int, int>(1, 1)).Should().BeTrue();
}

[Fact]
public void WhenRemoveKeyValueAndValueIsNotCreatedDoesNotRemove()
{
// seed the inner cache with an not yet created value
this.innerCache.AddOrUpdate(1, new AsyncAtomicFactory<int, int>());

// try to remove with the default value (0)
this.cache.TryRemove(new KeyValuePair<int, int>(1, 0)).Should().BeFalse();
}
#endif

private void OnItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
{
this.removedItems.Add(e);
Expand Down
250 changes: 136 additions & 114 deletions BitFaster.Caching.UnitTests/CacheTests.cs
Original file line number Diff line number Diff line change
@@ -1,119 +1,141 @@

using System;

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using Xunit;

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()
{
var cache = new Mock<ICache<int, int>>();
cache.CallBase = true;

Func<int, Func<int, int>, int> evaluate = (k, f) => f(k);
cache.Setup(c => c.GetOrAdd(It.IsAny<int>(), It.IsAny<Func<int, int>>())).Returns(evaluate);

cache.Object.GetOrAdd(
1,
(k, a) => k + a,
2).Should().Be(3);
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using Xunit;

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()
{
var cache = new Mock<ICache<int, int>>();
cache.CallBase = true;

Func<int, Func<int, int>, int> evaluate = (k, f) => f(k);
cache.Setup(c => c.GetOrAdd(It.IsAny<int>(), It.IsAny<Func<int, int>>())).Returns(evaluate);

cache.Object.GetOrAdd(
1,
(k, a) => k + a,
2).Should().Be(3);
}

[Fact]
public void WhenCacheInterfaceDefaultTryRemoveKeyThrows()
{
var cache = new Mock<ICache<int, int>>();
cache.CallBase = true;

Action tryRemove = () => { cache.Object.TryRemove(1, out var value); };

tryRemove.Should().Throw<NotSupportedException>();
}

[Fact]
public void WhenCacheInterfaceDefaultTryRemoveKeyValueThrows()
{
var cache = new Mock<ICache<int, int>>();
cache.CallBase = true;

Action tryRemove = () => { cache.Object.TryRemove(new KeyValuePair<int, int>(1, 1)); };

tryRemove.Should().Throw<NotSupportedException>();
}

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

Func<int, Func<int, Task<int>>, ValueTask<int>> evaluate = (k, f) => new ValueTask<int>(f(k));
cache.Setup(c => c.GetOrAddAsync(It.IsAny<int>(), It.IsAny<Func<int, Task<int>>>())).Returns(evaluate);

var r = await cache.Object.GetOrAddAsync(
1,
(k, a) => Task.FromResult(k + a),
2);

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

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

Action tryRemove = () => { cache.Object.TryRemove(1, out var value); };

tryRemove.Should().Throw<NotSupportedException>();
}

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

Action tryRemove = () => { cache.Object.TryRemove(new KeyValuePair<int, int>(1, 1)); };

tryRemove.Should().Throw<NotSupportedException>();
}

[Fact]
public void WhenCacheInterfaceDefaultTryRemoveKeyThrows()
{
var cache = new Mock<ICache<int, int>>();
cache.CallBase = true;

Action tryRemove = () => { cache.Object.TryRemove(1, out var value); };

tryRemove.Should().Throw<NotSupportedException>();
[Fact]
public void WhenScopedCacheInterfaceDefaultGetOrAddFallback()
{
var cache = new Mock<IScopedCache<int, Disposable>>();
cache.CallBase = true;

Func<int, Func<int, Scoped<Disposable>>, Lifetime<Disposable>> evaluate = (k, f) =>
{
var scope = f(k);
scope.TryCreateLifetime(out var lifetime).Should().BeTrue();
return lifetime;
};

cache.Setup(c => c.ScopedGetOrAdd(It.IsAny<int>(), It.IsAny<Func<int, Scoped<Disposable>>>())).Returns(evaluate);

var l = cache.Object.ScopedGetOrAdd(
1,
(k, a) => new Scoped<Disposable>(new Disposable(k + a)),
2);

l.Value.State.Should().Be(3);
}

[Fact]
public void WhenCacheInterfaceDefaultTryRemoveKeyValueThrows()
{
var cache = new Mock<ICache<int, int>>();
cache.CallBase = true;

Action tryRemove = () => { cache.Object.TryRemove(new KeyValuePair<int, int>(1, 1)); };

tryRemove.Should().Throw<NotSupportedException>();
}

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

Func<int, Func<int, Task<int>>, ValueTask<int>> evaluate = (k, f) => new ValueTask<int>(f(k));
cache.Setup(c => c.GetOrAddAsync(It.IsAny<int>(), It.IsAny<Func<int, Task<int>>>())).Returns(evaluate);

var r = await cache.Object.GetOrAddAsync(
1,
(k, a) => Task.FromResult(k + a),
2);

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

[Fact]
public void WhenScopedCacheInterfaceDefaultGetOrAddFallback()
{
var cache = new Mock<IScopedCache<int, Disposable>>();
cache.CallBase = true;

Func<int, Func<int, Scoped<Disposable>>, Lifetime<Disposable>> evaluate = (k, f) =>
{
var scope = f(k);
scope.TryCreateLifetime(out var lifetime).Should().BeTrue();
return lifetime;
};

cache.Setup(c => c.ScopedGetOrAdd(It.IsAny<int>(), It.IsAny<Func<int, Scoped<Disposable>>>())).Returns(evaluate);

var l = cache.Object.ScopedGetOrAdd(
1,
(k, a) => new Scoped<Disposable>(new Disposable(k + a)),
2);

l.Value.State.Should().Be(3);
}

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

Func<int, Func<int, Task<Scoped<Disposable>>>, ValueTask<Lifetime<Disposable>>> evaluate = async (k, f) =>
{
var scope = await f(k);
scope.TryCreateLifetime(out var lifetime).Should().BeTrue();
return lifetime;
};

cache
.Setup(c => c.ScopedGetOrAddAsync(It.IsAny<int>(), It.IsAny<Func<int, Task<Scoped<Disposable>>>>()))
.Returns(evaluate);

var lifetime = await cache.Object.ScopedGetOrAddAsync(
1,
(k, a) => Task.FromResult(new Scoped<Disposable>(new Disposable(k + a))),
2);

lifetime.Value.State.Should().Be(3);
}
#endif
}
}
[Fact]
public async Task WhenScopedAsyncCacheInterfaceDefaultGetOrAddFallback()
{
var cache = new Mock<IScopedAsyncCache<int, Disposable>>();
cache.CallBase = true;

Func<int, Func<int, Task<Scoped<Disposable>>>, ValueTask<Lifetime<Disposable>>> evaluate = async (k, f) =>
{
var scope = await f(k);
scope.TryCreateLifetime(out var lifetime).Should().BeTrue();
return lifetime;
};

cache
.Setup(c => c.ScopedGetOrAddAsync(It.IsAny<int>(), It.IsAny<Func<int, Task<Scoped<Disposable>>>>()))
.Returns(evaluate);

var lifetime = await cache.Object.ScopedGetOrAddAsync(
1,
(k, a) => Task.FromResult(new Scoped<Disposable>(new Disposable(k + a))),
2);

lifetime.Value.State.Should().Be(3);
}
#endif
}
}
Loading