using XFEToolBox.Core.Model; using System.Diagnostics; using System.Net; using System.Security.Cryptography; using XFEToolBox.Client.Core.Console; using XFEToolBox.Core.Tools; namespace XFEToolBox.Test; public class Program { [SMTest] public static void PathTest() { Console.WriteLine($""" 应用同步文件夹:{AppPath.AppSynData} 应用本地文件夹:{AppPath.AppLocalData} 应用本地版本文件夹:{AppPath.AppLocalVersionData} 应用缓存文件夹:{AppPath.AppCache} """); } [SMTest] public static void ConsoleOutputBufferPreservesWriteSemantics() { var buffer = new ConsoleOutputBuffer(); buffer.Enqueue("first", 1, false); buffer.Enqueue(startsNewLine => startsNewLine ? "unexpected" : " continuation", 2, true); buffer.Enqueue("second", 3, true); buffer.Enqueue("forced", 4, false, true); Ensure(buffer.TryDequeue(out var first) && first.StartsNewLine, "首条输出应开始新行。"); Ensure(buffer.TryDequeue(out var continuation) && !continuation.StartsNewLine, "Write 后的内容应续写当前行。"); Ensure(continuation.Text == " continuation", "续写消息工厂获得了错误的行状态。"); Ensure(buffer.TryDequeue(out var second) && second.StartsNewLine, "WriteLine 后的内容应开始新行。"); Ensure(buffer.TryDequeue(out var forced) && forced.StartsNewLine, "强制换行消息没有开始新行。"); Ensure(buffer.IsEmpty, "输出缓冲区没有被完全读取。"); buffer.Enqueue("discarded", 5, false); var discarded = buffer.Clear(); Ensure(discarded.Count == 1 && buffer.IsEmpty, "清空没有丢弃全部待渲染输出。"); Ensure(!buffer.IsCurrent(discarded[0]), "清空前的输出仍属于当前渲染代次。"); var afterClear = buffer.Enqueue("after clear", 5, true); Ensure(afterClear.StartsNewLine, "清空后第一条输出应开始新行。"); } [SMTest] public static void ConsoleOutputBufferHandlesHighThroughput() { const int outputCount = 200_000; var buffer = new ConsoleOutputBuffer(); var stopwatch = Stopwatch.StartNew(); Parallel.For(0, outputCount, index => buffer.Enqueue(index.ToString(), index, true)); var drainedCount = 0; while (buffer.TryDequeue(out _)) drainedCount++; stopwatch.Stop(); Ensure(drainedCount == outputCount, $"高并发输出发生丢失:期望 {outputCount},实际 {drainedCount}。"); Ensure(stopwatch.Elapsed < TimeSpan.FromSeconds(5), $"输出缓冲吞吐不足:{stopwatch.Elapsed}。"); var throughput = outputCount / stopwatch.Elapsed.TotalSeconds; Console.WriteLine($"控制台输出缓冲吞吐:{throughput:N0} 条/秒({outputCount:N0} 条,共 {stopwatch.Elapsed.TotalMilliseconds:N1} ms)"); } [SMTest] public static void ToolPackageDownloadReportsByteProgress() { var payload = Enumerable.Range(0, 240_000).Select(index => (byte)(index % 251)).ToArray(); var expectedHash = Convert.ToHexString(SHA256.HashData(payload)).ToLowerInvariant(); using var httpClient = new HttpClient(new PackageDownloadHandler(payload)) { BaseAddress = new Uri("https://toolbox.test/") }; var client = new ToolCatalogClient(httpClient); var reports = new List(); var progress = new InlineProgress(reports.Add); var package = new ToolPackageVersionInfo { ToolId = "test.progress", Version = "1.0.0", Sha256 = expectedHash, PackageSize = payload.Length, UploadedAtUtc = DateTimeOffset.UtcNow, Published = true, DownloadUrl = "api/v1/tools/download" }; using var destination = new MemoryStream(); client.DownloadPackageAsync(package, destination, progress).GetAwaiter().GetResult(); Ensure(destination.ToArray().SequenceEqual(payload), "工具包下载内容与服务器响应不一致。"); Ensure(reports.Count >= 2 && reports[0].BytesReceived == 0, "工具包下载没有上报初始进度。"); var completed = reports[^1]; Ensure(completed.BytesReceived == payload.Length && completed.TotalBytes == payload.Length, "工具包下载没有上报最终字节数。"); Ensure(Math.Abs(completed.Percentage.GetValueOrDefault() - 100) < 0.001, "工具包下载完成进度不是 100%。"); } private static void Ensure(bool condition, string message) { if (!condition) throw new InvalidOperationException(message); } private sealed class PackageDownloadHandler(byte[] payload) : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(payload), RequestMessage = request }; return Task.FromResult(response); } } private sealed class InlineProgress(Action report) : IProgress { public void Report(T value) => report(value); } }