Files
meeting-assistant/tools/ci/TranscriptFileShareProbe.cs
T
dh 147c544496
PR and Push Build/Test / build-and-test (push) Successful in 18m50s
PR and Push Build/Test / portable-build-and-test (push) Successful in 8m15s
ci: observe transcript append failure and Windows reader sharing
2026-10-03 22:44:31 +02:00

89 lines
4.3 KiB
C#

#:property PublishAot=false
// Local diagnostic only. See TranscriptFileShareProbe.md for the contract and limits.
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
const string original = "---\ntitle: transcript probe\n---\n\n# Meeting Transcript\n";
const string written = original + "[00:00:04] Guest-1: Azure returned ***** here.\n";
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-file-share-probe", Guid.NewGuid().ToString("N"));
if (Directory.Exists(root))
throw new IOException("Probe directory already exists; refusing to reuse it.");
Directory.CreateDirectory(root);
var cases = new List<WriteObservation>();
var cleanupCompleted = false;
try
{
var path = Path.Combine(root, "original-reader.md");
await File.WriteAllTextAsync(path, original);
using (var reader = new StreamReader(path, Encoding.UTF8, detectEncodingFromByteOrderMarks: true))
{
if (reader.ReadToEnd() != original)
throw new InvalidDataException("Original reader did not read the initial fixture.");
cases.Add(await ObserveWriteAsync("held-original-reader", path, written));
}
cases[^1] = cases[^1] with { ContentAfterReaderClosed = await File.ReadAllTextAsync(path) };
cases.Add(await ObserveWriteAsync("original-reader-released", path, written));
cases[^1] = cases[^1] with { ContentAfterReaderClosed = await File.ReadAllTextAsync(path) };
path = Path.Combine(root, "compatible-reader.md");
await File.WriteAllTextAsync(path, original);
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
using (var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true))
{
if (reader.ReadToEnd() != original)
throw new InvalidDataException("Compatible reader did not read the initial fixture.");
cases.Add(await ObserveWriteAsync("held-compatible-reader", path, written));
}
cases[^1] = cases[^1] with { ContentAfterReaderClosed = await File.ReadAllTextAsync(path) };
}
finally
{
Directory.Delete(root, recursive: true);
cleanupCompleted = !Directory.Exists(root);
}
var windowsContractMatched = OperatingSystem.IsWindows()
? !cases[0].Completed && cases[0].Error is { Type: "System.IO.IOException", NativeCode: 32 }
&& cases[0].ContentAfterReaderClosed == original
: (bool?)null;
var compatibleAndReleasedWritesCompleted = cases.Skip(1).All(result =>
result.Completed && result.Error is null && result.ContentAfterReaderClosed == written);
Console.WriteLine(JsonSerializer.Serialize(new
{
Schema = "meeting-assistant-file-share-probe/v1",
Runtime = RuntimeInformation.FrameworkDescription,
OS = RuntimeInformation.OSDescription,
Architecture = RuntimeInformation.ProcessArchitecture.ToString(),
ProbeDirectory = root,
CleanupCompleted = cleanupCompleted,
WindowsContractMatched = windowsContractMatched,
CompatibleAndReleasedWritesCompleted = compatibleAndReleasedWritesCompleted,
Cases = cases,
EvidenceLimit = "Held-reader access-mode probe; it does not reproduce the timing or establish the cause of Run4174."
}, new JsonSerializerOptions { WriteIndented = true }));
return cleanupCompleted && compatibleAndReleasedWritesCompleted && windowsContractMatched != false ? 0 : 1;
static async Task<WriteObservation> ObserveWriteAsync(string name, string path, string content)
{
var elapsed = Stopwatch.StartNew();
Task? write = null;
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
write = File.WriteAllTextAsync(path, content, deadline.Token);
await write;
return new(name, true, write.Status.ToString(), elapsed.ElapsedMilliseconds, null, null);
}
catch (Exception exception)
{
return new(name, false, write?.Status.ToString() ?? "not-returned", elapsed.ElapsedMilliseconds,
new(exception.GetType().FullName!, $"0x{exception.HResult:X8}", exception.HResult & 0xffff, exception.Message), null);
}
}
sealed record WriteObservation(string Name, bool Completed, string TaskStatus, long ElapsedMilliseconds,
WriteError? Error, string? ContentAfterReaderClosed);
sealed record WriteError(string Type, string HResult, int NativeCode, string Message);