using System.Diagnostics; using XFEExtension.NetCore.XUnit.Assertions; using XFEExtension.NetCore.XUnit.Execution; namespace XFEExtension.NetCore.XUnit.Compatibility; /// /// 提供 3.x 源代码迁移所需的旧测试基类和辅助方法;新代码应直接使用 、 /// 。 /// [Obsolete("Use XFERunner, Assert and BenchmarkAttribute. XFECode will be removed in XUnit 5.0.")] public abstract class XFECode { /// /// 使用当前进程命令行参数运行生成注册表中的测试,并把结果写入 。 /// /// 表示整个运行完成的任务。 [Obsolete("Use generated XFERunner.RunAsync entry points.")] public static async Task RunTest() => Environment.ExitCode = await XFERunner.RunAsync(Environment.GetCommandLineArgs().Skip(1).ToArray()).ConfigureAwait(false); /// /// 对委托执行一次墙钟计时;该兼容方法不会校准、预热或统计采样,不能替代正式基准。 /// /// 要执行一次的同步委托;为空时仅测量空调用路径。 /// 是否把格式化时间写入控制台。 /// 控制台输出中使用的计时器名称。 /// 该次调用测得的墙钟时间。 [Obsolete("One-shot timing is not a benchmark. Use BenchmarkAttribute and --benchmarks.")] public static TimeSpan CTime(Action? action, bool autoOutPut = true, string timerName = "unnamed") { var elapsed = Measure(action); if (autoOutPut) Console.WriteLine($"Name: {timerName}\tElapsed: {Format(elapsed)} (one-shot measurement; not a statistical benchmark)"); return elapsed; } /// /// 完整等待异步委托并执行一次墙钟计时;该兼容方法不能替代正式基准。 /// /// 要执行并等待一次的异步委托;为空时只测量计时器路径。 /// 是否把格式化时间写入控制台。 /// 控制台输出中使用的计时器名称。 /// 结果为该次异步调用墙钟时间的任务。 [Obsolete("One-shot timing is not a benchmark. Use BenchmarkAttribute and --benchmarks.")] public static async Task CTimeAsync(Func? action, bool autoOutPut = true, string timerName = "unnamed") { var stopwatch = Stopwatch.StartNew(); if (action is not null) await action().ConfigureAwait(false); stopwatch.Stop(); if (autoOutPut) Console.WriteLine($"Name: {timerName}\tElapsed: {Format(stopwatch.Elapsed)} (one-shot measurement; not a statistical benchmark)"); return stopwatch.Elapsed; } /// /// 兼容旧代码并验证条件为真。 /// /// 要验证的条件。 /// 失败时使用的可选消息。 /// 断言成功时始终返回 /// [Obsolete("Use Assert.True.")] public static bool Assert(bool condition, string? message = null) { global::XFEExtension.NetCore.XUnit.Assertions.Assert.True(condition, message); return true; } /// /// 兼容旧代码并验证条件为假。 /// /// 要验证的条件。 /// 失败时使用的可选消息。 /// 断言成功时始终返回 /// [Obsolete("Use Assert.False.")] public static bool AssertF(bool condition, string? message = null) { global::XFEExtension.NetCore.XUnit.Assertions.Assert.False(condition, message); return true; } /// /// 兼容旧代码并验证期望值与实际值相等。 /// /// 参与比较的值类型。 /// 期望值。 /// 实际值。 /// 失败时使用的可选消息。 /// 断言成功时始终返回 /// 两个值不相等。 [Obsolete("Use Assert.Equal.")] public static bool AssertE(T expected, T actual, string? message = null) { global::XFEExtension.NetCore.XUnit.Assertions.Assert.Equal(expected, actual, message); return true; } /// /// 在线程池中并行启动指定次数的同步操作,并可选择等待全部任务。 /// /// 每个任务执行的同步操作。 /// 要启动的任务数。 /// 是否在返回前等待全部任务完成。 /// 包含全部已启动任务的列表。 protected static async Task> Circle(Action action, int count, bool autoWaitAll = false) { var tasks = Enumerable.Range(0, count).Select(_ => Task.Run(action)).ToList(); if (autoWaitAll) await Task.WhenAll(tasks).ConfigureAwait(false); return tasks; } /// /// 按顺序在线程池中执行指定次数的同步操作。 /// /// 每次执行的同步操作。 /// 顺序执行次数。 /// 表示全部操作完成的任务。 protected static async Task CircleOrderly(Action action, int count) { for (var i = 0; i < count; i++) await Task.Run(action).ConfigureAwait(false); } /// /// 可选显示默认提示,然后等待用户按下任意键。 /// /// 是否显示默认提示文本。 protected static void Pause(bool showText = true) { if (showText) Console.WriteLine("Press any key to continue..."); Console.ReadKey(); } /// /// 显示指定提示并等待用户按下任意键。 /// /// 等待按键前显示的文本。 protected static void Pause(string showText) { Console.WriteLine(showText); Console.ReadKey(); } /// /// 等待用户按下指定控制台按键。 /// /// 用于继续执行的按键。 protected static void Pause(ConsoleKey consoleKey) { Console.WriteLine($"Press {consoleKey} to continue..."); while (Console.ReadKey().Key != consoleKey) { } } /// /// 显示指定提示并等待用户按下指定控制台按键。 /// /// 用于继续执行的按键。 /// 等待按键前显示的文本。 protected static void Pause(ConsoleKey consoleKey, string showText) { Console.WriteLine(showText); while (Console.ReadKey().Key != consoleKey) { } } private static TimeSpan Measure(Action? action) { var stopwatch = Stopwatch.StartNew(); action?.Invoke(); stopwatch.Stop(); return stopwatch.Elapsed; } private static string Format(TimeSpan elapsed) => elapsed.TotalNanoseconds switch { >= 1_000_000_000 => $"{elapsed.TotalSeconds:F3} s", >= 1_000_000 => $"{elapsed.TotalMilliseconds:F3} ms", >= 1_000 => $"{elapsed.TotalMicroseconds:F3} us", _ => $"{elapsed.TotalNanoseconds:F3} ns" }; }