Add a few more OcrRequestQueue tests

This commit is contained in:
Ben Olden-Cooligan 2022-06-17 17:40:31 -07:00
parent 6c64d1cf20
commit 9ab618acee
2 changed files with 100 additions and 3 deletions

View File

@ -234,6 +234,52 @@ public class OcrRequestQueueTests : ContextualTexts
_mockEngine.VerifyNoOtherCalls();
}
[Fact]
public async Task CancelOnceWithTwoReferences()
{
var tempPath1 = CreateTempFile();
var tempPath2 = CreateTempFile();
_mockEngine.Setup(x => x.ProcessImage(tempPath1, _ocrParams, It.IsAny<CancellationToken>()))
.Returns(() => Task.Run(async () =>
{
await Task.Delay(100);
return _expectedResult;
}));
// Delay the workers so we can cancel before processing starts
_ocrRequestQueue.WorkerAddedLatency = 50;
var cts = new CancellationTokenSource();
var ocrResult1Task = DoEnqueueForeground(_image, tempPath1, _ocrParams, cts.Token);
var ocrResult2Task = DoEnqueueForeground(_image, tempPath2, _ocrParams);
cts.Cancel();
var ocrResult1 = await ocrResult1Task;
var ocrResult2 = await ocrResult2Task;
Assert.Null(ocrResult1);
Assert.Equal(_expectedResult, ocrResult2);
_mockEngine.Verify(x => x.ProcessImage(tempPath1, _ocrParams, It.IsAny<CancellationToken>()));
_mockEngine.VerifyNoOtherCalls();
}
[Fact]
public async Task CancelTwiceWithTwoReferences()
{
// Delay the workers so we can cancel before processing starts
_ocrRequestQueue.WorkerAddedLatency = 50;
var cts = new CancellationTokenSource();
var ocrResult1Task = DoEnqueueForeground(_image, _tempPath, _ocrParams, cts.Token);
var ocrResult2Task = DoEnqueueForeground(_image, _tempPath, _ocrParams, cts.Token);
cts.Cancel();
var ocrResult1 = await ocrResult1Task;
var ocrResult2 = await ocrResult2Task;
Assert.Null(ocrResult1);
Assert.Null(ocrResult2);
await Task.Delay(100);
_mockEngine.VerifyNoOtherCalls();
}
[Fact]
public async Task ForegroundPrioritized()
{
@ -264,6 +310,47 @@ public class OcrRequestQueueTests : ContextualTexts
int completedCount = tasks.Count(x => x.IsCompleted);
Assert.Equal(1000, completedCount);
}
[Fact]
public async Task HasCachedResult()
{
_mockEngine.Setup(x => x.ProcessImage(_tempPath, _ocrParams, It.IsAny<CancellationToken>()))
.Returns(_expectedResultTask);
await DoEnqueueForeground(_image, _tempPath, _ocrParams);
var ocrParams2 = new OcrParams("fra", OcrMode.Fast, 10);
Assert.True(_ocrRequestQueue.HasCachedResult(_mockEngine.Object, _image, _ocrParams));
Assert.False(_ocrRequestQueue.HasCachedResult(_mockEngine.Object, _image, ocrParams2));
Assert.False(_ocrRequestQueue.HasCachedResult(_mockEngine.Object, CreateScannedImage(), _ocrParams));
Assert.False(_ocrRequestQueue.HasCachedResult(new Mock<IOcrEngine>().Object, _image, _ocrParams));
}
[Fact]
public async Task HasCachedResult_WhileProcessing()
{
_mockEngine.Setup(x => x.ProcessImage(_tempPath, _ocrParams, It.IsAny<CancellationToken>()))
.Returns(_expectedResultTask);
_ocrRequestQueue.WorkerAddedLatency = 50;
DoEnqueueForeground(_image, _tempPath, _ocrParams).AssertNoAwait();
Assert.False(_ocrRequestQueue.HasCachedResult(_mockEngine.Object, _image, _ocrParams));
await Task.Delay(100);
Assert.True(_ocrRequestQueue.HasCachedResult(_mockEngine.Object, _image, _ocrParams));
}
[Fact]
public async Task HasCachedResult_WithError()
{
_mockEngine.Setup(x => x.ProcessImage(_tempPath, _ocrParams, It.IsAny<CancellationToken>()))
.Throws<Exception>();
await DoEnqueueForeground(_image, _tempPath, _ocrParams);
Assert.False(_ocrRequestQueue.HasCachedResult(_mockEngine.Object, _image, _ocrParams));
}
private List<Task<OcrResult>> EnqueueMany(OcrPriority priority, int count)
{

View File

@ -2,6 +2,7 @@
namespace NAPS2.Ocr;
// TODO: Might need to use configureawait(false) everywhere, or at least on non-worker threads...
/// <summary>
/// Allows OCR requests to be queued and prioritized. Results are cached so that requests with the same set of
/// parameters (image, engine, language code, etc.) don't do duplicate work.
@ -18,6 +19,11 @@ public class OcrRequestQueue
/// in parallel.
/// </summary>
public int WorkerCount { get; init; } = Environment.ProcessorCount;
/// <summary>
/// For testing. Adds a delay to the worker tasks to process requests.
/// </summary>
public int WorkerAddedLatency { get; set; }
/// <summary>
/// Returns true if a previous queued request with the provided parameters has already completed and produced a
@ -63,11 +69,11 @@ public class OcrRequestQueue
_queueWaitHandle.Release();
// If no worker threads are running, start them
EnsureWorkerThreads();
var result = await req.CompletedTask;
await Task.WhenAny(req.CompletedTask, cancelToken.WaitHandle.WaitOneAsync());
// If no requests are pending, stop the worker threads
EnsureWorkerThreads();
// May return null if cancelled
return result;
// Return null if canceled
return req.CompletedTask.IsCompleted ? req.CompletedTask.Result : null;
}
private void EnsureWorkerThreads()
@ -96,6 +102,10 @@ public class OcrRequestQueue
{
try
{
if (WorkerAddedLatency > 0)
{
await Task.Delay(WorkerAddedLatency);
}
while (true)
{
// Wait for a queued ocr request to become available