Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added the start of a test framework

  • Loading branch information...
commit 2864ff61299a2c0c4dc468dbb798440c10ee28b8 1 parent 8a7e74f
Karl Seguin authored
View
24 Metsys.Redis.Tests/Commands/BaseCommandTests.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+
+namespace Metsys.Redis.Tests.Commands
+{
+ public class BaseCommandTests
+ {
+ protected IRedis Redis;
+
+ [TestFixtureSetUp]
+ public void FixtureSetUp()
+ {
+ Redis = RedisManager.Configure(c => c.ConnectTo("127.0.0.1", 6379).UsingDatabase(5)).Redis();
+ }
+
+ [SetUp]
+ public void SetUp()
+ {
+ Redis.FlushDb();
+ BeforeEachTest();
+ }
+ protected virtual void BeforeEachTest() {}
+
+ }
+}
View
29 Metsys.Redis.Tests/Commands/IncrTests.cs
@@ -0,0 +1,29 @@
+using NUnit.Framework;
+
+namespace Metsys.Redis.Tests.Commands
+{
+ public class IncrTests : BaseCommandTests
+ {
+ [Test]
+ public void IncrementsAKey()
+ {
+ for (var i = 0; i < 5; ++i)
+ {
+ IncrementAndAssert("test:1", i+1);
+ }
+ IncrementAndAssert("test:2", 1);
+ AssertKeyValue("test:1", 5);
+ }
+
+ private void IncrementAndAssert(string key, int expected)
+ {
+ Assert.AreEqual(expected, Redis.Incr(key));
+ AssertKeyValue(key, expected);
+ }
+
+ private void AssertKeyValue(string key, int expected)
+ {
+ Assert.AreEqual(expected, Redis.Get<long>(key));
+ }
+ }
+}
View
60 Metsys.Redis.Tests/Metsys.Redis.Tests.csproj
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{83A9D87F-E7AA-40D7-8403-4A8BDF09357C}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Metsys.Redis.Tests</RootNamespace>
+ <AssemblyName>Metsys.Redis.Tests</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="nunit.framework">
+ <HintPath>..\References\nunit.framework.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Commands\BaseCommandTests.cs" />
+ <Compile Include="Commands\IncrTests.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Metsys.Redis\Metsys.Redis.csproj">
+ <Project>{C050F51C-8CAC-4E10-A215-AE155F67EA02}</Project>
+ <Name>Metsys.Redis</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
15 Metsys.Redis.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,15 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Metsys.Redis.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Metsys.Redis.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+[assembly: Guid("d40ddc47-9e1d-4aec-8d4e-18e131021474")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
12 Metsys.Redis.sln
@@ -5,6 +5,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Metsys.Redis", "Metsys.Redi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Metsys.Redis.Console", "Metsys.Redis.Console\Metsys.Redis.Console.csproj", "{B28FB5AB-98D9-4B5B-A8F6-E39BBCB64EA4}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Metsys.Redis.Tests", "Metsys.Redis.Tests\Metsys.Redis.Tests.csproj", "{83A9D87F-E7AA-40D7-8403-4A8BDF09357C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -35,6 +37,16 @@ Global
{B28FB5AB-98D9-4B5B-A8F6-E39BBCB64EA4}.Release|Mixed Platforms.Build.0 = Release|x86
{B28FB5AB-98D9-4B5B-A8F6-E39BBCB64EA4}.Release|x86.ActiveCfg = Release|x86
{B28FB5AB-98D9-4B5B-A8F6-E39BBCB64EA4}.Release|x86.Build.0 = Release|x86
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {83A9D87F-E7AA-40D7-8403-4A8BDF09357C}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
View
7 Metsys.Redis/Configuration.cs
@@ -8,11 +8,18 @@ public interface IConfiguration
public class Configuration : IConfiguration
{
public ConnectionInfo Server { get; private set; }
+ public int Database;
public Configuration ConnectTo(string host, int port)
{
Server = new ConnectionInfo(host, port);
return this;
}
+
+ public Configuration UsingDatabase(int database)
+ {
+ Database = database;
+ return this;
+ }
}
}
View
6 Metsys.Redis/Connections/Connection.cs
@@ -65,11 +65,15 @@ public bool IsAlive()
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) { return; }
- _client.Close();
+ if (disposing)
+ {
+ _client.Close();
+ }
_disposed = true;
}
View
14 Metsys.Redis/Connections/ConnectionPool.cs
@@ -21,30 +21,30 @@ public ConnectionPool(ConnectionInfo connectionInfo)
_timer = new Timer(o => Cleanup(), null, 30000, 30000);
}
- public IConnection CheckOut()
+ public bool CheckOut(out IConnection connection)
{
- IConnection connection;
if (_freeConnections.TryDequeue(out connection))
{
Interlocked.Increment(ref _connectionsInUse);
- return connection;
+ return false;
}
if (_connectionsInUse < _maximumPoolSize)
{
Interlocked.Increment(ref _connectionsInUse);
- return new Connection(_connectionInfo);
+ connection = new Connection(_connectionInfo);
+ return true;
}
if (!_notifier.WaitOne(10000))
{
throw new RedisException("Connection timeout trying to get connection from connection pool");
}
- return CheckOut();
+ return CheckOut(out connection);
}
- public void CheckIn(IConnection connection)
+ public void CheckIn(IConnection connection, bool error)
{
- if (!IsAlive(connection))
+ if (error || !IsAlive(connection))
{
_invalidConnections.Enqueue(connection);
Interlocked.Decrement(ref _connectionsInUse);
View
5 Metsys.Redis/IRedis.cs
@@ -4,8 +4,11 @@ namespace Metsys.Redis
{
public interface IRedis : IDisposable
{
+ T Get<T>(string key);
long Incr(string key);
long IncrBy(string key, int value);
- long Del(string key);
+ long Del(params string[] key);
+ void FlushDb();
+ void Select(int database);
}
}
View
1  Metsys.Redis/Metsys.Redis.csproj
@@ -46,6 +46,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Protocol\Encoding.cs" />
<Compile Include="Protocol\Reader.cs" />
+ <Compile Include="Protocol\Serializer.cs" />
<Compile Include="Protocol\WriteContext.cs" />
<Compile Include="Protocol\Writer.cs" />
<Compile Include="Redis.cs" />
View
5 Metsys.Redis/Protocol/Encoding.cs
@@ -13,5 +13,10 @@ public static byte[] GetBytes(string value)
{
return _encoder.GetBytes(value);
}
+
+ public static string GetString(byte[] data)
+ {
+ return _encoder.GetString(data);
+ }
}
}
View
80 Metsys.Redis/Protocol/Reader.cs
@@ -5,13 +5,69 @@ namespace Metsys.Redis
{
public class Reader
{
- private const byte _integerReply = (byte)':';
- private const byte _errorReply = (byte)'-';
+ private const byte _integerMarker = (byte)':';
+ private const byte _lineMarker = (byte)'+';
+ private const byte _errorMarker = (byte)'-';
+ private const byte _bulkMarker = (byte)'$';
+ private const byte _OReply = (byte)'O';
+ private const byte _KReply = (byte)'K';
+ private const byte _CRReply = (byte)'\r';
+ private const byte _LFReply = (byte)'\n';
public static long Integer(Stream stream)
{
- AssertReplyKind(_integerReply, stream);
+ AssertReplyKind(_integerMarker, stream);
+ return ReadNumber(stream);
+ }
+
+ public static bool Status(Stream stream)
+ {
+ AssertReplyKind(_lineMarker, stream);
+ AssertNextByteIs(stream, _OReply);
+ AssertNextByteIs(stream, _KReply);
+ ReadCrLf(stream);
+ return true;
+ }
+
+ public static byte[] Bulk(Stream stream)
+ {
+ AssertReplyKind(_bulkMarker, stream);
+ var length = (int)ReadNumber(stream);
+ if (length == -1)
+ {
+ return null;
+ }
+ var buffer = new byte[length];
+ var read = 0;
+ while (read < length)
+ {
+ read += stream.Read(buffer, read, length);
+ }
+ ReadCrLf(stream);
+ return buffer;
+ }
+
+ private static void AssertNextByteIs(Stream stream, byte expected)
+ {
+ var b = stream.ReadByte();
+ if (b == expected) { return; }
+ throw new RedisException(string.Format("Expecting '{0}' but got '{1}'", (char)expected, (char)b));
+ }
+
+ private static void AssertReplyKind(byte expected, Stream stream)
+ {
+ var b = stream.ReadByte();
+ if (b == expected) { return; }
+
+ if (b == _errorMarker)
+ {
+ throw new RedisException(ReadLine(stream));
+ }
+ throw new RedisException(string.Format("Expecting a reply of type '{0}' but got '{1}'", (char)expected, (char)b));
+ }
+ private static long ReadNumber(Stream stream)
+ {
const char zero = '0';
var negative = false;
var value = 0L;
@@ -22,36 +78,28 @@ public static long Integer(Stream stream)
while ((b = stream.ReadByte()) != -1 && b != '\r')
{
- value = value*10 + (b - zero);
+ value = value * 10 + (b - zero);
}
stream.ReadByte(); // \n
return negative ? -value : value;
}
- private static void AssertReplyKind(byte type, Stream stream)
+ private static void ReadCrLf(Stream stream)
{
- var b = stream.ReadByte();
- if (b == type) { return; }
-
- if (b == _errorReply)
- {
- throw new RedisException(ReadLine(stream));
- }
- throw new RedisException(string.Format("Expecting a reply of type '{0}' but got '{1}'", (char)type, (char)b));
+ AssertNextByteIs(stream, _CRReply);
+ AssertNextByteIs(stream, _LFReply);
}
private static string ReadLine(Stream stream)
{
int b;
var sb = new StringBuilder(100);
- while ((b = stream.ReadByte()) != -1 && b != '\r')
+ while ((b = stream.ReadByte()) != -1 && b != _CRReply)
{
sb.Append((char)b);
}
stream.ReadByte(); // \n
return sb.ToString();
}
-
-
}
}
View
27 Metsys.Redis/Protocol/Serializer.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+
+namespace Metsys.Redis
+{
+ public class Serializer
+ {
+ private static readonly IDictionary<Type, Func<byte[], object>> _deserializationTypeLookup = new Dictionary<Type, Func<byte[], object>>
+ {
+ {typeof (bool), d => d[0] == 1},
+ {typeof (int), d => int.Parse(Encoding.GetString(d))},
+ {typeof (long), d => long.Parse(Encoding.GetString(d))},
+
+ };
+
+ public static T Deserialize<T>(byte[] data)
+ {
+ Func<byte[], object> reader;
+ var type = typeof (T);
+ if (_deserializationTypeLookup.TryGetValue(type, out reader))
+ {
+ return (T) reader(data);
+ }
+ return default(T);
+ }
+ }
+}
View
15 Metsys.Redis/Protocol/WriteContext.cs
@@ -5,11 +5,11 @@ namespace Metsys.Redis
public class WriteContext : IDisposable
{
private static readonly Pool<byte[]> _smallBuffer = new Pool<byte[]>(1000, p => new byte[250]);
+ private readonly Pool<WriteContext> _parentPool;
private byte[] _buffer;
private bool _fromPool;
private int _length;
- private readonly Pool<WriteContext> _parentPool;
-
+
public byte[] Buffer
{
get { return _buffer; }
@@ -43,13 +43,16 @@ public void SetLength(int length)
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
- if (!disposing || _buffer == null || !_fromPool) { return; }
- _smallBuffer.CheckIn(_buffer);
- _buffer = null;
- _parentPool.CheckIn(this);
+ if (disposing)
+ {
+ if (_fromPool) { _smallBuffer.CheckIn(_buffer); }
+ _buffer = null;
+ _parentPool.CheckIn(this);
+ }
}
~WriteContext()
View
83 Metsys.Redis/Redis.cs
@@ -6,12 +6,27 @@ namespace Metsys.Redis
public class Redis : IRedis
{
private readonly RedisManager _manager;
+ private readonly Configuration _configuration;
+ private IConnection _connection;
+ private bool _selectedADatabase;
+ private bool _error;
- public IConnection Connection { get; private set; }
+ public IConnection Connection
+ {
+ get { return _connection; }
+ }
internal Redis(RedisManager manager)
{
_manager = manager;
+ _configuration = manager.Configuration;
+ }
+
+ private static readonly byte[] _getCommand = Encoding.GetBytes("GET");
+ public T Get<T>(string key)
+ {
+ var data = Send(Writer.Serialize(_getCommand, key), Reader.Bulk);
+ return data == null ? default(T) : Serializer.Deserialize<T>(data);
}
private static readonly byte[] _incrCommand = Encoding.GetBytes("INCR");
@@ -27,28 +42,78 @@ public long IncrBy(string key, int value)
}
private static readonly byte[] _delCommand = Encoding.GetBytes("DEL");
- public long Del(string key)
+ public long Del(params string[] key)
{
return Send(Writer.Serialize(_delCommand, key), Reader.Integer);
}
- public T Send<T>(WriteContext context, Func<Stream, T> callback)
+ private static readonly byte[] _flushDbCommand = Encoding.GetBytes("FLUSHDB");
+ public void FlushDb()
+ {
+ Send(Writer.Serialize(_flushDbCommand), Reader.Status);
+ }
+
+
+ public void Select(int database)
+ {
+ Select(database, true);
+ }
+
+ private static readonly byte[] _selectCommand = Encoding.GetBytes("SELECT");
+ private void Select(int database, bool flagAsDifferent)
{
- if (Connection == null)
+ if (flagAsDifferent) { _selectedADatabase = true; }
+ Send(Writer.Serialize(_selectCommand, database.ToString()), Reader.Status);
+ }
+
+ private T Send<T>(WriteContext context, Func<Stream, T> callback)
+ {
+ if (_connection == null)
{
- Connection = _manager.GetConnection();
+ _error = false;
+ if (_manager.GetConnection(out _connection) && _configuration.Database != 0)
+ {
+ Select(_configuration.Database, false);
+ }
}
using (context)
{
- Connection.Send(context.Buffer, context.Length);
- return callback(Connection.GetStream());
+ try
+ {
+ Connection.Send(context.Buffer, context.Length);
+ return callback(Connection.GetStream());
+ }
+ catch
+ {
+ _error = true;
+ throw;
+ }
}
}
public void Dispose()
{
- _manager.CheckIn(this);
- Connection = null;
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (!_error && _selectedADatabase)
+ {
+ Select(_configuration.Database, false);
+ }
+ _manager.CheckIn(this, _error);
+ _error = false;
+ _connection = null;
+ }
+ }
+
+ ~Redis()
+ {
+ Dispose(false);
+ }
+
}
}
View
16 Metsys.Redis/RedisManager.cs
@@ -13,6 +13,11 @@ public class RedisManager : IRedisManager
private Pool<Redis> _redisPool;
private readonly Configuration _configuration = new Configuration();
+ public Configuration Configuration
+ {
+ get { return _configuration; }
+ }
+
public static IRedisManager Configure(Action<IConfiguration> action)
{
var manager = new RedisManager();
@@ -27,15 +32,18 @@ public IRedis Redis()
return _redisPool.CheckOut();
}
- public IConnection GetConnection()
+ public bool GetConnection(out IConnection connection)
{
- return _connectionPool.CheckOut();
+ return _connectionPool.CheckOut(out connection);
}
- public void CheckIn(Redis redis)
+ public void CheckIn(Redis redis, bool error)
{
var connection = redis.Connection;
- if (connection != null) {_connectionPool.CheckIn(connection);}
+ if (connection != null)
+ {
+ _connectionPool.CheckIn(connection, error);
+ }
_redisPool.CheckIn(redis);
}
}
View
BIN  References/nunit.framework.dll
Binary file not shown
Please sign in to comment.
Something went wrong with that request. Please try again.