Public Access
Stabilize recording coordinator timing tests
PR and Push Build/Test / build-and-test (push) Failing after 9m40s
PR and Push Build/Test / build-and-test (push) Failing after 9m40s
This commit is contained in:
@@ -15,7 +15,7 @@ namespace MeetingAssistant.Tests;
|
|||||||
|
|
||||||
public sealed class RecordingCoordinatorTests
|
public sealed class RecordingCoordinatorTests
|
||||||
{
|
{
|
||||||
private static async Task WaitUntilAsync(Func<bool> condition)
|
private static async Task WaitUntilAsync(Func<bool> condition, string? timeoutMessage = null)
|
||||||
{
|
{
|
||||||
var deadline = DateTimeOffset.UtcNow.AddSeconds(15);
|
var deadline = DateTimeOffset.UtcNow.AddSeconds(15);
|
||||||
while (DateTimeOffset.UtcNow < deadline)
|
while (DateTimeOffset.UtcNow < deadline)
|
||||||
@@ -28,7 +28,7 @@ public sealed class RecordingCoordinatorTests
|
|||||||
await Task.Delay(25);
|
await Task.Delay(25);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TimeoutException("Condition was not met.");
|
throw new TimeoutException(timeoutMessage ?? "Condition was not met.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task AppendTextWithRetryAsync(string path, string text)
|
private static async Task AppendTextWithRetryAsync(string path, string text)
|
||||||
@@ -365,6 +365,7 @@ public sealed class RecordingCoordinatorTests
|
|||||||
await WaitUntilAsync(() => transcriptStore.Segments.Any(segment =>
|
await WaitUntilAsync(() => transcriptStore.Segments.Any(segment =>
|
||||||
segment.Text.Contains("latest transcript text", StringComparison.Ordinal)));
|
segment.Text.Contains("latest transcript text", StringComparison.Ordinal)));
|
||||||
|
|
||||||
|
await WaitUntilAsync(() => clock.PendingDelayCount > 0);
|
||||||
clock.Advance(TimeSpan.FromSeconds(2));
|
clock.Advance(TimeSpan.FromSeconds(2));
|
||||||
await promptService.WaitForPromptAsync();
|
await promptService.WaitForPromptAsync();
|
||||||
await WaitUntilAsync(() => summaryPipeline.WasRun);
|
await WaitUntilAsync(() => summaryPipeline.WasRun);
|
||||||
@@ -414,8 +415,10 @@ public sealed class RecordingCoordinatorTests
|
|||||||
|
|
||||||
await coordinator.StartAsync(CancellationToken.None);
|
await coordinator.StartAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
await WaitUntilAsync(() => clock.PendingDelayCount > 0);
|
||||||
clock.Advance(TimeSpan.FromSeconds(5));
|
clock.Advance(TimeSpan.FromSeconds(5));
|
||||||
await WaitUntilAsync(() => !coordinator.CurrentStatus.IsRecording);
|
await WaitUntilAsync(() => !coordinator.CurrentStatus.IsRecording);
|
||||||
|
await WaitUntilAsync(() => summaryPipeline.WasRun);
|
||||||
|
|
||||||
Assert.Empty(promptService.Requests);
|
Assert.Empty(promptService.Requests);
|
||||||
Assert.False(coordinator.CurrentStatus.IsRecording);
|
Assert.False(coordinator.CurrentStatus.IsRecording);
|
||||||
@@ -459,6 +462,7 @@ public sealed class RecordingCoordinatorTests
|
|||||||
inactivityClock: clock);
|
inactivityClock: clock);
|
||||||
|
|
||||||
await coordinator.StartAsync(CancellationToken.None);
|
await coordinator.StartAsync(CancellationToken.None);
|
||||||
|
await WaitUntilAsync(() => clock.PendingDelayCount > 0);
|
||||||
clock.Advance(TimeSpan.FromSeconds(2));
|
clock.Advance(TimeSpan.FromSeconds(2));
|
||||||
await promptService.WaitForPromptAsync();
|
await promptService.WaitForPromptAsync();
|
||||||
await WaitUntilAsync(() => clock.PendingDelayCount > 0);
|
await WaitUntilAsync(() => clock.PendingDelayCount > 0);
|
||||||
@@ -509,12 +513,14 @@ public sealed class RecordingCoordinatorTests
|
|||||||
await audioSource.WriteAsync(new AudioChunk([1, 0], 16000, 1), CancellationToken.None);
|
await audioSource.WriteAsync(new AudioChunk([1, 0], 16000, 1), CancellationToken.None);
|
||||||
await WaitUntilAsync(() => transcriptStore.Segments.Any(segment =>
|
await WaitUntilAsync(() => transcriptStore.Segments.Any(segment =>
|
||||||
segment.Text.Contains("chunk:2", StringComparison.Ordinal)));
|
segment.Text.Contains("chunk:2", StringComparison.Ordinal)));
|
||||||
|
await WaitUntilAsync(() => clock.PendingDelayCount > 0);
|
||||||
clock.Advance(TimeSpan.FromSeconds(2));
|
clock.Advance(TimeSpan.FromSeconds(2));
|
||||||
await WaitUntilAsync(() => promptService.Requests.Count == 1);
|
await WaitUntilAsync(() => promptService.Requests.Count == 1);
|
||||||
|
|
||||||
await audioSource.WriteAsync(new AudioChunk([1, 0, 2, 0], 16000, 1), CancellationToken.None);
|
await audioSource.WriteAsync(new AudioChunk([1, 0, 2, 0], 16000, 1), CancellationToken.None);
|
||||||
await WaitUntilAsync(() => transcriptStore.Segments.Any(segment =>
|
await WaitUntilAsync(() => transcriptStore.Segments.Any(segment =>
|
||||||
segment.Text.Contains("chunk:4", StringComparison.Ordinal)));
|
segment.Text.Contains("chunk:4", StringComparison.Ordinal)));
|
||||||
|
await WaitUntilAsync(() => clock.PendingDelayCount > 0);
|
||||||
clock.Advance(TimeSpan.FromSeconds(2));
|
clock.Advance(TimeSpan.FromSeconds(2));
|
||||||
await WaitUntilAsync(() => promptService.Requests.Count == 2);
|
await WaitUntilAsync(() => promptService.Requests.Count == 2);
|
||||||
|
|
||||||
@@ -2167,8 +2173,8 @@ public sealed class RecordingCoordinatorTests
|
|||||||
|
|
||||||
private sealed class InMemoryTranscriptStore : ITranscriptStore
|
private sealed class InMemoryTranscriptStore : ITranscriptStore
|
||||||
{
|
{
|
||||||
|
private readonly object gate = new();
|
||||||
private readonly List<TranscriptionSegment> segments = [];
|
private readonly List<TranscriptionSegment> segments = [];
|
||||||
private readonly TaskCompletionSource segmentWritten = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
private readonly string transcriptPath;
|
private readonly string transcriptPath;
|
||||||
|
|
||||||
public InMemoryTranscriptStore(string transcriptPath = "memory-transcript.md")
|
public InMemoryTranscriptStore(string transcriptPath = "memory-transcript.md")
|
||||||
@@ -2190,31 +2196,34 @@ public sealed class RecordingCoordinatorTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task AppendLineAsync(TranscriptSession session, string line, CancellationToken cancellationToken)
|
public Task AppendLineAsync(TranscriptSession session, string line, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
{
|
{
|
||||||
segments.Add(ParseTranscriptLine(line));
|
segments.Add(ParseTranscriptLine(line));
|
||||||
segmentWritten.TrySetResult();
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WaitForTextAsync(string text)
|
public Task WaitForTextAsync(string text)
|
||||||
{
|
{
|
||||||
var deadline = DateTimeOffset.UtcNow.AddSeconds(5);
|
return WaitUntilAsync(
|
||||||
while (DateTimeOffset.UtcNow < deadline)
|
() => Segments.Any(segment => segment?.Text?.Contains(text, StringComparison.Ordinal) == true),
|
||||||
{
|
$"Segment containing '{text}' was not written.");
|
||||||
if (segments.Any(segment => segment?.Text?.Contains(text, StringComparison.Ordinal) == true))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await segmentWritten.Task.WaitAsync(TimeSpan.FromMilliseconds(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TimeoutException($"Segment containing '{text}' was not written.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<TranscriptionSegment> ReplacedSegments { get; private set; } = [];
|
public IReadOnlyList<TranscriptionSegment> ReplacedSegments { get; private set; } = [];
|
||||||
|
|
||||||
public IReadOnlyList<TranscriptionSegment> Segments => segments;
|
public IReadOnlyList<TranscriptionSegment> Segments
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
return segments.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public MeetingNote? MetadataMeetingNote { get; private set; }
|
public MeetingNote? MetadataMeetingNote { get; private set; }
|
||||||
|
|
||||||
@@ -2242,7 +2251,6 @@ public sealed class RecordingCoordinatorTests
|
|||||||
{
|
{
|
||||||
private int created;
|
private int created;
|
||||||
private int appendCount;
|
private int appendCount;
|
||||||
private TaskCompletionSource appendObserved = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
|
|
||||||
public List<TranscriptWrite> AppendHistory { get; } = [];
|
public List<TranscriptWrite> AppendHistory { get; } = [];
|
||||||
|
|
||||||
@@ -2268,29 +2276,14 @@ public sealed class RecordingCoordinatorTests
|
|||||||
{
|
{
|
||||||
AppendHistory.Add(new TranscriptWrite(session, ParseTranscriptLine(line)));
|
AppendHistory.Add(new TranscriptWrite(session, ParseTranscriptLine(line)));
|
||||||
Interlocked.Increment(ref appendCount);
|
Interlocked.Increment(ref appendCount);
|
||||||
appendObserved.TrySetResult();
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WaitForAppendCountAsync(int expectedCount)
|
public Task WaitForAppendCountAsync(int expectedCount)
|
||||||
{
|
{
|
||||||
var deadline = DateTimeOffset.UtcNow.AddSeconds(5);
|
return WaitUntilAsync(
|
||||||
while (DateTimeOffset.UtcNow < deadline)
|
() => Volatile.Read(ref appendCount) >= expectedCount,
|
||||||
{
|
$"Expected {expectedCount} transcript append(s).");
|
||||||
if (Volatile.Read(ref appendCount) >= expectedCount)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var observed = appendObserved;
|
|
||||||
await observed.Task.WaitAsync(TimeSpan.FromMilliseconds(100));
|
|
||||||
if (ReferenceEquals(observed, appendObserved))
|
|
||||||
{
|
|
||||||
appendObserved = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TimeoutException($"Expected {expectedCount} transcript append(s).");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ReplaceLinesAsync(
|
public Task ReplaceLinesAsync(
|
||||||
|
|||||||
Reference in New Issue
Block a user