forked from ddrilling/AsbCloudServer
Add Tests
This commit is contained in:
parent
7f92f07423
commit
68d3d2724c
@ -52,17 +52,6 @@ public class BackgroundWorker : BackgroundService
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавить в очередь
|
||||
/// </summary>
|
||||
/// <param name="work"></param>
|
||||
public void Enqueue(Work work)
|
||||
{
|
||||
works.Enqueue(work);
|
||||
if (ExecuteTask is null || ExecuteTask.IsCompleted)
|
||||
StartAsync(CancellationToken.None).Wait();
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested && works.TryDequeue(out CurrentWork))
|
||||
@ -94,12 +83,27 @@ public class BackgroundWorker : BackgroundService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавить в очередь
|
||||
/// <para>
|
||||
/// work.Id может быть не уникальным,
|
||||
/// при этом метод TryRemoveFromQueue удалит все работы с совпадающими id
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="work"></param>
|
||||
public void Enqueue(Work work)
|
||||
{
|
||||
works.Enqueue(work);
|
||||
if (ExecuteTask is null || ExecuteTask.IsCompleted)
|
||||
StartAsync(CancellationToken.None).Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаление работы по ID из одноразовой очереди
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryRemoveFromRunOnceQueue(string id)
|
||||
public bool TryRemoveFromQueue(string id)
|
||||
{
|
||||
var work = Works.FirstOrDefault(w => w.Id == id);
|
||||
if (work is not null)
|
||||
|
@ -14,8 +14,8 @@ namespace AsbCloudInfrastructure.Background;
|
||||
/// </summary>
|
||||
public class PeriodicBackgroundWorker : BackgroundService
|
||||
{
|
||||
private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10);
|
||||
private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(1);
|
||||
private readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10);
|
||||
private readonly TimeSpan minDelay = TimeSpan.FromSeconds(1);
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
private readonly List<WorkPeriodic> works = new(8);
|
||||
@ -97,6 +97,8 @@ public class PeriodicBackgroundWorker : BackgroundService
|
||||
{
|
||||
var periodic = new WorkPeriodic(work, period);
|
||||
works.Add(periodic);
|
||||
if (ExecuteTask is null || ExecuteTask.IsCompleted)
|
||||
StartAsync(CancellationToken.None).Wait();
|
||||
}
|
||||
|
||||
private WorkPeriodic? GetNext()
|
||||
|
@ -165,6 +165,7 @@ namespace AsbCloudInfrastructure
|
||||
services.AddSingleton<ITelemetryDataCache<TelemetryDataSaubDto>>(provider => TelemetryDataCache<TelemetryDataSaubDto>.GetInstance<TelemetryDataSaub>(provider));
|
||||
services.AddSingleton<ITelemetryDataCache<TelemetryDataSpinDto>>(provider => TelemetryDataCache<TelemetryDataSpinDto>.GetInstance<TelemetryDataSpin>(provider));
|
||||
services.AddSingleton<IRequerstTrackerService, RequestTrackerService>();
|
||||
services.AddSingleton<PeriodicBackgroundWorker>();
|
||||
services.AddSingleton<BackgroundWorker>();
|
||||
services.AddSingleton<NotificationBackgroundWorker>();
|
||||
services.AddSingleton<IReduceSamplingService>(provider => ReduceSamplingService.GetInstance(configuration));
|
||||
|
@ -556,7 +556,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
|
||||
private async Task<int> RemoveDrillingProgramAsync(int idWell, CancellationToken token)
|
||||
{
|
||||
var workId = MakeWorkId(idWell);
|
||||
backgroundWorker.TryRemoveFromRunOnceQueue(workId);
|
||||
backgroundWorker.TryRemoveFromQueue(workId);
|
||||
|
||||
var filesIds = await context.Files
|
||||
.Where(f => f.IdWell == idWell &&
|
||||
|
@ -1,16 +1,7 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.SAUB;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using AsbCloudInfrastructure.Services;
|
||||
using AsbCloudInfrastructure.Services.SAUB;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -18,12 +9,12 @@ using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services;
|
||||
|
||||
public class BackgroundWorkertest
|
||||
public class BackgroundWorkerTest
|
||||
{
|
||||
private IServiceProvider provider;
|
||||
private BackgroundWorker service;
|
||||
|
||||
public BackgroundWorkertest()
|
||||
public BackgroundWorkerTest()
|
||||
{
|
||||
provider = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
var serviceScope = Substitute.For<IServiceScope>();
|
||||
@ -33,7 +24,7 @@ public class BackgroundWorkertest
|
||||
|
||||
service = new BackgroundWorker(provider);
|
||||
typeof(BackgroundWorker)
|
||||
.GetField("minDelay", BindingFlags.NonPublic | BindingFlags.Instance)?
|
||||
.GetField("minDelay", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.SetValue(service, TimeSpan.FromMilliseconds(1));
|
||||
}
|
||||
|
||||
@ -55,11 +46,8 @@ public class BackgroundWorkertest
|
||||
service.Enqueue(work);
|
||||
}
|
||||
|
||||
var waitI = workCount;
|
||||
await Task.Delay(1_000);
|
||||
//while (waitI-- > 0 && service.ExecuteTask is not null && service.ExecuteTask.IsCompleted)
|
||||
// await Task.Delay(4);
|
||||
|
||||
await service.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.Equal(workCount, result);
|
||||
}
|
||||
@ -69,15 +57,16 @@ public class BackgroundWorkertest
|
||||
{
|
||||
var expectadResult = 42;
|
||||
var result = 0;
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result = expectadResult;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
var goodWork = Work.CreateByDelegate("", workAction);
|
||||
|
||||
Task failAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> throw new Exception();
|
||||
var goodWork = Work.CreateByDelegate("", workAction);
|
||||
|
||||
var badWork = Work.CreateByDelegate("", failAction);
|
||||
badWork.OnErrorAsync = (id, exception, token) => throw new Exception();
|
||||
@ -85,9 +74,40 @@ public class BackgroundWorkertest
|
||||
//act
|
||||
service.Enqueue(badWork);
|
||||
service.Enqueue(goodWork);
|
||||
await Task.Delay(1200);
|
||||
|
||||
await service.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.Equal(expectadResult, result);
|
||||
Assert.Equal(1, service.Felled.Count);
|
||||
Assert.Equal(1, service.Done.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryRemove()
|
||||
{
|
||||
var workCount = 5;
|
||||
var result = 0;
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result++;
|
||||
return Task.Delay(10);
|
||||
}
|
||||
|
||||
//act
|
||||
for (int i = 0; i < workCount; i++)
|
||||
{
|
||||
var work = Work.CreateByDelegate(i.ToString(), workAction);
|
||||
service.Enqueue(work);
|
||||
}
|
||||
|
||||
var removed = service.TryRemoveFromQueue((workCount - 1).ToString());
|
||||
|
||||
await service.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.True(removed);
|
||||
Assert.Equal(workCount - 1, result);
|
||||
Assert.Equal(workCount - 1, service.Done.Count);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using DocumentFormat.OpenXml.Drawing.Charts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services;
|
||||
|
||||
public class PeriodicBackgroundWorkerTest
|
||||
{
|
||||
private IServiceProvider provider;
|
||||
private PeriodicBackgroundWorker service;
|
||||
|
||||
public PeriodicBackgroundWorkerTest()
|
||||
{
|
||||
provider = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
var serviceScope = Substitute.For<IServiceScope>();
|
||||
var serviceScopeFactory = Substitute.For<IServiceScopeFactory>();
|
||||
serviceScopeFactory.CreateScope().Returns(serviceScope);
|
||||
((ISupportRequiredService)provider).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactory);
|
||||
|
||||
service = new PeriodicBackgroundWorker(provider);
|
||||
typeof(PeriodicBackgroundWorker)
|
||||
.GetField("minDelay", BindingFlags.NonPublic | BindingFlags.Instance)?
|
||||
.SetValue(service, TimeSpan.FromMilliseconds(1));
|
||||
|
||||
typeof(PeriodicBackgroundWorker)
|
||||
.GetField("executePeriod", BindingFlags.NonPublic | BindingFlags.Instance)?
|
||||
.SetValue(service, TimeSpan.FromMilliseconds(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WorkRunsTwice()
|
||||
{
|
||||
var workCount = 2;
|
||||
var periodMs = 100d;
|
||||
|
||||
var period = TimeSpan.FromMilliseconds(periodMs);
|
||||
|
||||
var result = 0;
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result++;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
//act
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
service.Add(work, period);
|
||||
|
||||
var delay = (periodMs / 20) + (periodMs * workCount) - stopwatch.ElapsedMilliseconds;
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(delay));
|
||||
|
||||
//assert
|
||||
Assert.Equal(workCount, result);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Enqueue_continues_after_exceptions()
|
||||
{
|
||||
var expectadResult = 42;
|
||||
var result = 0;
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result = expectadResult;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
var goodWork = Work.CreateByDelegate("", workAction);
|
||||
|
||||
Task failAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> throw new Exception();
|
||||
|
||||
var badWork = Work.CreateByDelegate("", failAction);
|
||||
badWork.OnErrorAsync = (id, exception, token) => throw new Exception();
|
||||
|
||||
//act
|
||||
service.Add(badWork, TimeSpan.FromSeconds(2));
|
||||
service.Add(goodWork, TimeSpan.FromSeconds(2));
|
||||
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(20));
|
||||
|
||||
//assert
|
||||
Assert.Equal(expectadResult, result);
|
||||
Assert.Equal(1, badWork.CountErrors);
|
||||
Assert.Equal(1, goodWork.CountComplete);
|
||||
Assert.Equal(1, goodWork.CountStart);
|
||||
}
|
||||
}
|
126
AsbCloudWebApi.Tests/Services/WorkTest.cs
Normal file
126
AsbCloudWebApi.Tests/Services/WorkTest.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services
|
||||
{
|
||||
public class WorkTest
|
||||
{
|
||||
private IServiceProvider provider;
|
||||
|
||||
public WorkTest()
|
||||
{
|
||||
provider = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
var serviceScope = Substitute.For<IServiceScope>();
|
||||
var serviceScopeFactory = Substitute.For<IServiceScopeFactory>();
|
||||
serviceScopeFactory.CreateScope().Returns(serviceScope);
|
||||
((ISupportRequiredService)provider).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Work_done_with_success()
|
||||
{
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
await work.Start(provider, CancellationToken.None);
|
||||
var done = DateTime.Now;
|
||||
var executionTime = done - begin;
|
||||
|
||||
//assert
|
||||
Assert.Equal(1, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.Null(work.CurrentState);
|
||||
Assert.Null(work.LastError);
|
||||
|
||||
var lastState = work.LastComplete;
|
||||
Assert.NotNull(lastState);
|
||||
Assert.InRange(lastState.Start, begin, done - 0.5 * executionTime);
|
||||
Assert.InRange(lastState.End, done - 0.5 * executionTime, done);
|
||||
Assert.InRange(lastState.ExecutionTime, TimeSpan.Zero, executionTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Work_calls_callback()
|
||||
{
|
||||
var expectedState = "42";
|
||||
var expectedProgress = 42d;
|
||||
|
||||
var timeout = TimeSpan.FromMilliseconds(40);
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
callback.Invoke(expectedState, expectedProgress);
|
||||
return Task.Delay(timeout);
|
||||
}
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
_ = work.Start(provider, CancellationToken.None);
|
||||
await Task.Delay(timeout/3);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.NotNull(work.CurrentState);
|
||||
Assert.Null(work.LastComplete);
|
||||
Assert.Null(work.LastError);
|
||||
|
||||
var currentState = work.CurrentState;
|
||||
Assert.NotNull(currentState);
|
||||
Assert.InRange(currentState.Start, begin, begin + timeout);
|
||||
Assert.InRange(currentState.StateUpdate, begin, begin + timeout);
|
||||
Assert.Equal(expectedState, currentState.State);
|
||||
Assert.Equal(expectedProgress, currentState.Progress);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Work_fails_with_info()
|
||||
{
|
||||
var expectedState = "41";
|
||||
var expectedErrorText = "42";
|
||||
var minWorkTime = TimeSpan.FromMilliseconds(10);
|
||||
|
||||
async Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
await Task.Delay(minWorkTime);
|
||||
callback(expectedState, 0);
|
||||
throw new Exception(expectedErrorText);
|
||||
}
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
await work.Start(provider, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(1, work.CountErrors);
|
||||
Assert.Null(work.CurrentState);
|
||||
Assert.Null(work.LastComplete);
|
||||
|
||||
var error = work.LastError;
|
||||
Assert.NotNull(error);
|
||||
Assert.InRange(error.Start, begin, DateTime.Now);
|
||||
Assert.InRange(error.End, begin, DateTime.Now);
|
||||
Assert.InRange(error.ExecutionTime, minWorkTime, DateTime.Now - begin);
|
||||
Assert.Contains(expectedErrorText, error.ErrorText, StringComparison.InvariantCultureIgnoreCase);
|
||||
Assert.Equal(expectedState, error.State);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user