Merge branch 'feature-6-timers' into feature-3-lodestone-linkup

This commit is contained in:
Daniel_I_Am 2020-06-05 17:46:42 +02:00
commit 20a0c41d1e
No known key found for this signature in database
GPG Key ID: 80C428FCC9743E84
5 changed files with 233 additions and 0 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ obj/
.idea/
/ChaosBot/appsettings.json
/ChaosBot/ChaosBotSQL.db
*.sln.DotSettings.user

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChaosBot\ChaosBot.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,90 @@
using System;
using Antlr4.Runtime;
using ChaosBot.Services;
using NUnit.Framework;
namespace ChaosBot.UnitTests
{
public class TimerTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void RunAt_RunningTimerAtTimestamp_True()
{
DateTime current = DateTime.Now;
DateTime runAt = current + new TimeSpan(TimeSpan.TicksPerSecond * 10);
DateTime testAt = current + new TimeSpan(TimeSpan.TicksPerSecond * 2);
int totalRuns = 0;
void ToRun()
{
totalRuns++;
}
int timerId = Timer.RunAt(ToRun, runAt);
Assert.AreEqual(totalRuns, 0);
// Wait for the timer to finish
Timer.Join(timerId);
Assert.AreEqual(totalRuns, 1);
Assert.GreaterOrEqual(DateTime.Now, testAt);
}
[Test]
public void RunIn_RunningTimerInTimeSpan_True()
{
DateTime current = DateTime.Now;
TimeSpan runIn = new TimeSpan(TimeSpan.TicksPerSecond * 10);
DateTime testAt = current + new TimeSpan(TimeSpan.TicksPerSecond * 2);
int totalRuns = 0;
void ToRun()
{
totalRuns++;
}
int timerId = Timer.RunIn(ToRun, runIn);
Assert.AreEqual(totalRuns, 0);
// Wait for the timer to finish
Timer.Join(timerId);
Assert.AreEqual(totalRuns, 1);
Assert.GreaterOrEqual(DateTime.Now, testAt);
}
[Test]
public void RunTimer_RunningTimerCountingExecutions_True()
{
DateTime current = DateTime.Now;
TimeSpan interval = new TimeSpan(TimeSpan.TicksPerSecond * 2);
DateTime testAt = current + new TimeSpan(TimeSpan.TicksPerSecond * 11);
int totalRuns = 0;
void ToRun()
{
totalRuns++;
}
int timerId = Timer.RunTimer(ToRun, interval, offset: interval);
void CancelCallback()
{
Timer.CancelTimer(timerId);
}
int cancelTimerId = Timer.RunAt(CancelCallback, testAt);
Assert.AreEqual(totalRuns, 0);
// Wait for the cancel timer to finish
Timer.Join(cancelTimerId);
Assert.AreEqual(totalRuns, 5);
}
}
}

View File

@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChaosBot", "ChaosBot\ChaosBot.csproj", "{0222079F-84D7-4EEF-A2A6-D5AD67546D61}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChaosBot.UnitTests", "ChaosBot.UnitTests\ChaosBot.UnitTests.csproj", "{A4678BAA-93AF-4A10-8548-1921236BC57E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -12,5 +14,9 @@ Global
{0222079F-84D7-4EEF-A2A6-D5AD67546D61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0222079F-84D7-4EEF-A2A6-D5AD67546D61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0222079F-84D7-4EEF-A2A6-D5AD67546D61}.Release|Any CPU.Build.0 = Release|Any CPU
{A4678BAA-93AF-4A10-8548-1921236BC57E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4678BAA-93AF-4A10-8548-1921236BC57E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4678BAA-93AF-4A10-8548-1921236BC57E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4678BAA-93AF-4A10-8548-1921236BC57E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

117
ChaosBot/Services/Timer.cs Normal file
View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ChaosBot.Services
{
public static class Timer
{
private static Dictionary<int, System.Threading.Timer> _timers = new Dictionary<int, System.Threading.Timer>();
private static int _nextTimerIndex = 0;
private static int NextTimerIndex
{
get
{
_nextTimerIndex++;
return _nextTimerIndex;
}
}
public static int RunAt(Action toRun, DateTime deadlineTime)
{
DateTime current = DateTime.Now;
TimeSpan timeToGo = deadlineTime - current;
if (timeToGo < TimeSpan.Zero)
{
return -1; //time already passed
}
int timerIndex = NextTimerIndex;
_timers.Add(timerIndex, new System.Threading.Timer(x =>
{
toRun();
_timers.Remove(timerIndex);
}, null, timeToGo, Timeout.InfiniteTimeSpan));
return timerIndex;
}
public static int RunIn(Action toRun, TimeSpan dueTime)
{
DateTime current = DateTime.Now;
if (dueTime < TimeSpan.Zero)
{
return -1; //time already passed
}
int timerIndex = NextTimerIndex;
_timers.Add(timerIndex, new System.Threading.Timer(x =>
{
toRun();
_timers.Remove(timerIndex);
}, null, dueTime, Timeout.InfiniteTimeSpan));
return timerIndex;
}
public static int RunTimer(Action toRun, TimeSpan interval)
{
return RunTimer(toRun, interval, TimeSpan.Zero);
}
public static int RunTimer(Action toRun, TimeSpan interval, TimeSpan offset)
{
DateTime current = DateTime.Now;
if (offset < TimeSpan.Zero)
{
return -1; //time already passed
}
int timerIndex = NextTimerIndex;
void Callback()
{
toRun();
if (_timers.ContainsKey(timerIndex))
_timers.Remove(timerIndex);
_timers.Add(timerIndex, new System.Threading.Timer(x => { Callback(); }, null, interval, Timeout.InfiniteTimeSpan));
}
if (offset > TimeSpan.Zero)
{
_timers.Add(timerIndex, new System.Threading.Timer(x => { Callback(); }, null, offset, Timeout.InfiniteTimeSpan));
}
else
{
Callback();
}
return timerIndex;
}
public static void CancelTimer(int id)
{
if (id < 0) return;
if (!_timers.ContainsKey(id)) return;
_timers.GetValueOrDefault(id)?.Dispose();
if (_timers.ContainsKey(id))
_timers.Remove(id);
}
public static void Join(int id)
{
if (id < 0) return;
while (_timers.ContainsKey(id))
{
Thread.Sleep(500);
}
}
}
}