Skip to content

Commit

Permalink
Implemented SetTimeout, SetInterval and Debounce, added unit tests, z…
Browse files Browse the repository at this point in the history
  • Loading branch information
yallie committed Feb 20, 2019
1 parent 9e61ef6 commit 4457dae
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 0 deletions.
92 changes: 92 additions & 0 deletions source/Zyan.Communication/Toolbox/Debouncer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using SysTimer = System.Timers.Timer;

namespace Zyan.Communication.Toolbox
{
internal static class Debouncer
{
/// <summary>
/// Default debounce interval, milliseconds.
/// </summary>
public const int DefaultDebounceInterval = 300;

/// <summary>
/// Creates a new debounced version of the passed action.
/// </summary>
/// <param name="action">Action to debounce.</param>
/// <param name="delayMs">Debounce interval in milliseconds.</param>
/// <returns>The debounced version of the given action.</returns>
/// <remarks>
/// Based on these gists:
/// https://gist.github.com/ca0v/73a31f57b397606c9813472f7493a940
/// https://gist.github.com/fr-ser/ded7690b245223094cd876069456ed6c
/// </remarks>
public static Action Debounce(this Action action, int delayMs = DefaultDebounceInterval)
{
var timer = default(IDisposable);

return () =>
{
timer?.Dispose();
timer = SetTimeout(action, delayMs);
};
}

/// <summary>
/// Executes an action at specified intervals (in milliseconds), like setInterval in Javascript.
/// </summary>
/// <param name="action">The action to schedule.</param>
/// <param name="delayMs">The delay in milliseconds.</param>
/// <returns>
/// The value that can be disposed to stop the timer.
/// </returns>
/// <remarks>
/// Based on this gist:
/// https://gist.github.com/CipherLab/10a40f7032be04f0aa6f
/// </remarks>
public static IDisposable SetInterval(Action action, int delayMs)
{
var timer = new SysTimer(delayMs)
{
AutoReset = true
};

timer.Elapsed += (s, e) =>
{
action();
};

timer.Start();
return timer;
}

/// <summary>
/// Executes an action after a specified number of milliseconds.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="delayMs">The delay in milliseconds.</param>
/// <returns>
/// The value that can be disposed to cancel the execution.
/// </returns>
/// <remarks>
/// Based on this gist:
/// https://gist.github.com/CipherLab/10a40f7032be04f0aa6f
/// </remarks>
public static IDisposable SetTimeout(Action action, int delayMs)
{
var timer = new SysTimer(delayMs)
{
AutoReset = false
};

timer.Elapsed += (s, e) =>
{
action();
timer.Dispose();
};

timer.Start();
return timer;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@
<Compile Include="Toolbox\Compression\CompressionHelper.cs" />
<Compile Include="Toolbox\Compression\LZF.cs" />
<Compile Include="Toolbox\Crc32Calculator.cs" />
<Compile Include="Toolbox\Debouncer.cs" />
<Compile Include="Toolbox\Diagnostics\Trace.cs" />
<Compile Include="Toolbox\Disposable.cs" />
<Compile Include="Toolbox\DisposableMarshalByRefObject.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Communication/Zyan.Communication.Fx3.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@
<Compile Include="Toolbox\ActionFuncDelegatesFx3.cs" />
<Compile Include="Toolbox\ConcurrentQueueFx3.cs" />
<Compile Include="Toolbox\Crc32Calculator.cs" />
<Compile Include="Toolbox\Debouncer.cs" />
<Compile Include="Toolbox\Diagnostics\Trace.cs" />
<Compile Include="Toolbox\Disposable.cs" />
<Compile Include="Toolbox\DisposableMarshalByRefObject.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Communication/Zyan.Communication.Fx4.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
<Compile Include="Toolbox\ConcurrentCollection.cs" />
<Compile Include="Toolbox\AssemblyLocator.cs" />
<Compile Include="Toolbox\Crc32Calculator.cs" />
<Compile Include="Toolbox\Debouncer.cs" />
<Compile Include="Toolbox\LocalCallContextData.cs" />
<Compile Include="Toolbox\Compression\CompressionHelper.cs" />
<Compile Include="Toolbox\Compression\LZF.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Communication/Zyan.Communication.Fx45.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@
<Compile Include="Toolbox\ChecksumHelper.cs" />
<Compile Include="Toolbox\ConcurrentCollection.cs" />
<Compile Include="Toolbox\Crc32Calculator.cs" />
<Compile Include="Toolbox\Debouncer.cs" />
<Compile Include="Toolbox\LocalCallContextData.cs" />
<Compile Include="Toolbox\Compression\CompressionHelper.cs" />
<Compile Include="Toolbox\Compression\LZF.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Communication/Zyan.Communication.Mono.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@
<Compile Include="Toolbox\Compression\CompressionHelper.cs" />
<Compile Include="Toolbox\Compression\LZF.cs" />
<Compile Include="Toolbox\Crc32Calculator.cs" />
<Compile Include="Toolbox\Debouncer.cs" />
<Compile Include="Toolbox\Diagnostics\Trace.cs" />
<Compile Include="Toolbox\Disposable.cs" />
<Compile Include="Toolbox\DisposableMarshalByRefObject.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Communication/Zyan.Communication.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@
<Compile Include="Toolbox\ChecksumHelper.cs" />
<Compile Include="Toolbox\ConcurrentCollection.cs" />
<Compile Include="Toolbox\Crc32Calculator.cs" />
<Compile Include="Toolbox\Debouncer.cs" />
<Compile Include="Toolbox\LocalCallContextData.cs" />
<Compile Include="Toolbox\Compression\CompressionHelper.cs" />
<Compile Include="Toolbox\Compression\LZF.cs" />
Expand Down
116 changes: 116 additions & 0 deletions source/Zyan.Tests/DebouncerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Zyan.Communication.Toolbox;

namespace Zyan.Tests
{
#region Unit testing platform abstraction layer
#if NUNIT
using NUnit.Framework;
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using ClassInitializeNonStatic = NUnit.Framework.TestFixtureSetUpAttribute;
using ClassInitialize = DummyAttribute;
using ClassCleanupNonStatic = NUnit.Framework.TestFixtureTearDownAttribute;
using ClassCleanup = DummyAttribute;
using TestContext = System.Object;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ClassCleanupNonStatic = DummyAttribute;
using ClassInitializeNonStatic = DummyAttribute;
#endif
#endregion

/// <summary>
/// Test class for the debouncer.
///</summary>
[TestClass]
public class DebouncerTests
{
[TestMethod]
public void SetTimeoutExecutesTheGivenActionAfterAnInterval()
{
// target function
var counter = 0;
Action inc = () => counter++;

Debouncer.SetTimeout(inc, 10);
Assert.AreEqual(0, counter);

Thread.Sleep(50);
Assert.AreEqual(1, counter);
}

[TestMethod]
public void SetTimeoutCanBeCancelled()
{
// target function
var counter = 0;
Action inc = () => counter++;

var timer = Debouncer.SetTimeout(inc, 10);
Assert.AreEqual(0, counter);
timer.Dispose();

Thread.Sleep(50);
Assert.AreEqual(0, counter);
}

[TestMethod]
public void SetIntervalExecutesTheGivenActionAtAGivenInterval()
{
// target function
var counter = 0;
Action inc = () => counter++;

Debouncer.SetInterval(inc, 10);
Assert.AreEqual(0, counter);

Thread.Sleep(50);
Assert.IsTrue(counter > 1);
}

[TestMethod]
public void SetIntervalCanBeCancelled()
{
// target function
var counter = 0;
Action inc = () => counter++;

var timer = Debouncer.SetInterval(inc, 10);
Assert.AreEqual(0, counter);

Thread.Sleep(50);
Assert.IsTrue(counter > 1);
timer.Dispose();

var lastCounter = counter;
Thread.Sleep(50);
Assert.AreEqual(lastCounter, counter);
}

[TestMethod]
public void DebouncedActionIsCalledOnce()
{
// target function
var counter = 0;
Action inc = () => counter++;

// debounce the given function
var debounced = inc.Debounce(10);
Assert.IsNotNull(debounced);

// try to call the debounced version and make sure the target is not yet called
debounced();
debounced();
debounced();
debounced();
Assert.AreEqual(0, counter);

Thread.Sleep(50);
Assert.AreEqual(1, counter);
}
}
}
1 change: 1 addition & 0 deletions source/Zyan.Tests/Zyan.Tests.Fx3.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
<Compile Include="CompressionTests.cs" />
<Compile Include="CryptoToolsTests.cs" />
<Compile Include="CustomAuthenticationTests.cs" />
<Compile Include="DebouncerTests.cs" />
<Compile Include="DefaultProtocolTests.cs" />
<Compile Include="DiscoveryMetadataTests.cs" />
<Compile Include="DuckTypingTests.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Tests/Zyan.Tests.Mono.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<Compile Include="CleanupTests.cs" />
<Compile Include="CryptoToolsTests.cs" />
<Compile Include="CustomAuthenticationTests.cs" />
<Compile Include="DebouncerTests.cs" />
<Compile Include="DefaultProtocolTests.cs" />
<Compile Include="DiscoveryMetadataTests.cs" />
<Compile Include="EmptyDelegateFactoryTests.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Tests/Zyan.Tests.NUnit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<Compile Include="CompressionTests.cs" />
<Compile Include="CryptoToolsTests.cs" />
<Compile Include="CustomAuthenticationTests.cs" />
<Compile Include="DebouncerTests.cs" />
<Compile Include="DefaultProtocolTests.cs" />
<Compile Include="DiscoveryMetadataTests.cs" />
<Compile Include="DuckTypingTests.cs" />
Expand Down
1 change: 1 addition & 0 deletions source/Zyan.Tests/Zyan.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
<Compile Include="CompressionTests.cs" />
<Compile Include="CryptoToolsTests.cs" />
<Compile Include="CustomAuthenticationTests.cs" />
<Compile Include="DebouncerTests.cs" />
<Compile Include="DefaultProtocolTests.cs" />
<Compile Include="DiscoveryMetadataTests.cs" />
<Compile Include="DuckTypingTests.cs" />
Expand Down

0 comments on commit 4457dae

Please sign in to comment.