Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue72 Deprecate Add<T> method in favor of Set<T> #136

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
131 changes: 131 additions & 0 deletions LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,137 @@ public void AddWithSlidingThatExpiresReturnsNull()
Assert.IsNull(sut.Get<string>(TestKey));
}

[Test]
public void SetComplexObjectThenGetGenericReturnsCachedObject()
{
testObject.SomeItems.Add("Another");
testObject.SomeMessage = "changed-it-up";
sut.Set(TestKey, testObject);
var actual = sut.Get<ComplexTestObject>(TestKey);
var expected = testObject;
Assert.NotNull(actual);
Assert.AreEqual(expected, actual);
testObject.SomeItems.Should().Contain("Another");
testObject.SomeMessage.Should().Be("changed-it-up");
}

[Test]
public void SetComplexObjectThenGetReturnsCachedObject()
{
sut.Set(TestKey, testObject);
var actual = sut.Get<object>(TestKey) as ComplexTestObject;
var expected = testObject;
Assert.NotNull(actual);
Assert.AreEqual(expected, actual);
}

[Test]
public void SetEmptyKeyThrowsException()
{
Action act = () => sut.Set("", new object());
act.Should().Throw<ArgumentOutOfRangeException>();
}

[Test]
public void SetEmptyKeyThrowsExceptionWithExpiration()
{
Action act = () => sut.Set("", new object(), DateTimeOffset.Now.AddHours(1));
act.Should().Throw<ArgumentOutOfRangeException>();
}

[Test]
public void SetEmptyKeyThrowsExceptionWithPolicy()
{
Action act = () => sut.Set("", new object(), new MemoryCacheEntryOptions());
act.Should().Throw<ArgumentOutOfRangeException>();
}

[Test]
public void SetEmptyKeyThrowsExceptionWithSliding()
{
Action act = () => sut.Set("", new object(), new TimeSpan(1000));
act.Should().Throw<ArgumentOutOfRangeException>();
}

[Test]
public void SetNullKeyThrowsException()
{
Action act = () => sut.Set(null, new object());
act.Should().Throw<ArgumentNullException>();
}

[Test]
public void SetNullKeyThrowsExceptionWithExpiration()
{
Action act = () => sut.Set(null, new object(), DateTimeOffset.Now.AddHours(1));
act.Should().Throw<ArgumentNullException>();
}

[Test]
public void SetNullKeyThrowsExceptionWithPolicy()
{
Action act = () => sut.Set(null, new object(), new MemoryCacheEntryOptions());
act.Should().Throw<ArgumentNullException>();
}

[Test]
public void SetNullKeyThrowsExceptionWithSliding()
{
Action act = () => sut.Set(null, new object(), new TimeSpan(1000));
act.Should().Throw<ArgumentNullException>();
}

[Test]
public void SetNullThrowsException()
{
Action act = () => sut.Add<object>(TestKey, null);
act.Should().Throw<ArgumentNullException>();
}

[Test]
public void SetThenGetReturnsCachedObject()
{
sut.Set(TestKey, "testObject");
Assert.AreEqual("testObject", sut.Get<string>(TestKey));
}

[Test]
public void SetWithOffsetReturnsCachedItem()
{
sut.Set(TestKey, "testObject", DateTimeOffset.Now.AddSeconds(1));
Assert.AreEqual("testObject", sut.Get<string>(TestKey));
}

[Test]
public void SetWithOffsetThatExpiresReturnsNull()
{
sut.Set(TestKey, "testObject", DateTimeOffset.Now.AddSeconds(1));
Thread.Sleep(1500);
Assert.IsNull(sut.Get<string>(TestKey));
}

[Test]
public void SetWithPolicyReturnsCachedItem()
{
sut.Set(TestKey, "testObject", new MemoryCacheEntryOptions());
Assert.AreEqual("testObject", sut.Get<string>(TestKey));
}

[Test]
public void SetWithSlidingReturnsCachedItem()
{
sut.Set(TestKey, "testObject", new TimeSpan(5000));
Assert.AreEqual("testObject", sut.Get<string>(TestKey));
}

[Test]
public void SetWithSlidingThatExpiresReturnsNull()
{
sut.Set(TestKey, "testObject", new TimeSpan(750));
Thread.Sleep(1500);
Assert.IsNull(sut.Get<string>(TestKey));
}

[Test]
public void CacheProviderIsNotNull()
{
Expand Down
106 changes: 106 additions & 0 deletions LazyCache.UnitTests/DeprecationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace LazyCache.UnitTests
{
/// <summary>
/// Tests to confirm that methods intended to be deprecated have been adequately marked as such
/// </summary>
public class DeprecationTests
{
[Test]
public void AllImplementationsOfIAppCache_HaveTheAddMethod_MarkedAsObsolete()
{
var classesWithoutObsoleteTag = new List<Type>();

foreach (var type in GetTypesWithIAppCache())
{
if (MethodDoesNotHaveObsoleteAttribute(type))
{
classesWithoutObsoleteTag.Add(type);
}
}
CollectionAssert.IsEmpty(classesWithoutObsoleteTag, "Types which do not have the Add() method marked as Obsolete");
}

[Test]
public void AllImplementationsOfIAppCache_HaveTheAddMethod_MarkedAsNeverBrowsable()
{
var classesWhereAddAppearsBrowsable = new List<Type>();

foreach (var type in GetTypesWithIAppCache())
{
var editorBrowsableState = GetAddMethodsEditorBrowsableState(type);
if (editorBrowsableState != EditorBrowsableState.Never)
{
classesWhereAddAppearsBrowsable.Add(type);
}
}
CollectionAssert.IsEmpty(classesWhereAddAppearsBrowsable, "Types which have the Add() method not marked as Never browsable");
}

[Test]
public void AppCacheExtensions_AddMethods_AreObsolete()
{
var addMethodsWithoutObsoleteAttribute =
typeof(AppCacheExtensions).GetMethods()
.Where(m => m.Name == nameof(AppCacheExtensions.Add)
&& m.GetCustomAttribute(typeof(ObsoleteAttribute), true) == null);

CollectionAssert.IsEmpty(addMethodsWithoutObsoleteAttribute, "Add methods not marked as obsolete");
}

[Test]
public void AppCacheExtensions_AddMethods_AreNotBrowsable()
{
var addMethodsThatAreBrowsable =
typeof(AppCacheExtensions)
.GetMethods()
.Where(m => m.Name == nameof(AppCacheExtensions.Add))
.Select(m => (Method: m, Attribute: GetEditorBrowsableAttribute(m)))
.Where(ma => ma.Attribute.State != EditorBrowsableState.Never);

CollectionAssert.IsEmpty(addMethodsThatAreBrowsable.Select(ma => ma.Method), "Add methods not marked as Never browsable");

static EditorBrowsableAttribute GetEditorBrowsableAttribute(MethodInfo m)
{
return m.GetCustomAttributes(typeof(EditorBrowsableAttribute), true).Single() as EditorBrowsableAttribute;
}
}

private IEnumerable<Type> GetTypesWithIAppCache()
{
var iAppCache = typeof(IAppCache);
return Assembly.GetAssembly(iAppCache)
.GetTypes()
.Where(iAppCache.IsAssignableFrom);
}

private bool MethodDoesNotHaveObsoleteAttribute(Type type)
{
var method = type.GetMethods()
.SingleOrDefault(m => m.Name == nameof(IAppCache.Add));
var attribute = method?.GetCustomAttributes(typeof(ObsoleteAttribute), true)
.SingleOrDefault() as ObsoleteAttribute;
return attribute == null;
}

private EditorBrowsableState GetAddMethodsEditorBrowsableState(Type type)
{
var addMethod = nameof(IAppCache.Add);
var method = type.GetMethods()
.SingleOrDefault(m => m.Name == addMethod);
var attribute = method?.GetCustomAttributes(typeof(EditorBrowsableAttribute), true)
.SingleOrDefault() as EditorBrowsableAttribute;
if (attribute == null)
{
throw new InvalidOperationException($"{type.Name}'s {addMethod} method does not have {nameof(EditorBrowsableAttribute)}");
}
return attribute.State;
}
}
}
40 changes: 31 additions & 9 deletions LazyCache/AppCacheExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
Expand All @@ -8,25 +9,25 @@ namespace LazyCache
{
public static class AppCacheExtensions
{
[Obsolete("This method has been deprecated. Use Set<T> instead.", false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static void Add<T>(this IAppCache cache, string key, T item)
{
if (cache == null) throw new ArgumentNullException(nameof(cache));

cache.Add(key, item, cache.DefaultCachePolicy.BuildOptions());
Set(cache, key, item);
}

[Obsolete("This method has been deprecated. Use Set<T> instead.", false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static void Add<T>(this IAppCache cache, string key, T item, DateTimeOffset expires)
{
if (cache == null) throw new ArgumentNullException(nameof(cache));

cache.Add(key, item, new MemoryCacheEntryOptions {AbsoluteExpiration = expires});
Set(cache, key, item, expires);
}

[Obsolete("This method has been deprecated. Use Set<T> instead.", false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static void Add<T>(this IAppCache cache, string key, T item, TimeSpan slidingExpiration)
{
if (cache == null) throw new ArgumentNullException(nameof(cache));

cache.Add(key, item, new MemoryCacheEntryOptions {SlidingExpiration = slidingExpiration});
Set(cache, key, item, slidingExpiration);
}

public static T GetOrAdd<T>(this IAppCache cache, string key, Func<T> addItemFactory)
Expand Down Expand Up @@ -116,5 +117,26 @@ public static Task<T> GetOrAddAsync<T>(this IAppCache cache, string key, Func<Ta

return cache.GetOrAddAsync(key, _=> addItemFactory(), policy);
}

public static void Set<T>(this IAppCache cache, string key, T item)
{
if (cache == null) throw new ArgumentNullException(nameof(cache));

cache.Set(key, item, cache.DefaultCachePolicy.BuildOptions());
}

public static void Set<T>(this IAppCache cache, string key, T item, DateTimeOffset expires)
{
if (cache == null) throw new ArgumentNullException(nameof(cache));

cache.Set(key, item, new MemoryCacheEntryOptions { AbsoluteExpiration = expires });
}

public static void Set<T>(this IAppCache cache, string key, T item, TimeSpan slidingExpiration)
{
if (cache == null) throw new ArgumentNullException(nameof(cache));

cache.Set(key, item, new MemoryCacheEntryOptions { SlidingExpiration = slidingExpiration });
}
}
}
Loading