Files
game-engine/managed/Engine.EditorApp.Tests/Program.cs
T
BillyOutlastandhermes-agent 09d48dc301 [P3-13] Zero-copy viewport: shareable image + control + real-GPU numbers (#212)
* parity: 138 survey Avalonia 12.1.4 GPU interop OS matrix and share paths

* parity: 138 shareable Vulkan viewport image with fd export and timing harness

* parity: 138 GPU-shared SceneViewportView control with CPU fallback and env toggle

* parity: 138 real-GPU 1080p timing win (0.002ms < 1168.955ms) + headless goldens green

* parity: 138 correct viewport-share header to print-only timing semantics

* parity: 138 fix viewport-share unused-variable on non-Linux (fail-closed asserts)

* parity: 138 fix PresentFrame throw contract + CodeRabbit native threads + off-Linux CI

---------

Co-authored-by: hermes-agent <hermes-agent@local>
2026-11-04 12:10:04 -05:00

116 lines
4.6 KiB
C#

// Program.cs — P3-01: editor shell acceptance: menu titles, panel names,
// window size, title, golden screenshot. Prints [PASS] lines + the golden
// line, exits 0 only when all pass. Usage: Engine.EditorApp.Tests <out-dir>.
using Avalonia;
using Avalonia.Controls;
using Avalonia.Headless;
using Avalonia.Media.Imaging;
using Avalonia.VisualTree;
using Engine.EditorApp;
namespace Engine.EditorApp.Tests;
internal static class Program
{
public static int Main(string[] args)
{
var failures = 0;
failures += Check(MainWindow.MenuTitles.Length == 5, "shell: five menus");
failures += Check(MainWindow.MenuTitles[0] == "File", "shell: menu File first");
failures += Check(MainWindow.MenuTitles[2] == "GameObject", "shell: menu GameObject present");
failures += Check(MainWindow.MenuTitles[4] == "Help", "shell: menu Help last");
failures += Check(MainWindow.PanelNames.Length == 7, "shell: seven panels (P3-09 adds Profiler)");
failures += Check(MainWindow.PanelNames.Contains("Console"), "shell: console panel present");
failures += Check(MainWindow.PanelNames.Contains("Profiler"), "shell: profiler panel present");
var builder = AppBuilder.Configure<Engine.EditorApp.App>()
.UseSkia()
.UseHeadless(new AvaloniaHeadlessPlatformOptions { UseHeadlessDrawing = false })
.LogToTrace();
builder.SetupWithoutStarting();
var window = new MainWindow();
window.Show();
failures += Check(window.Title == "Engine Editor", "shell: window title");
failures += Check(window.ClientSize is { Width: 1280, Height: 720 }, "shell: default size 1280x720");
var names = CollectNames(window);
var missing = MainWindow.PanelNames.Where(p => !names.Contains(p)).ToArray();
failures += Check(missing.Length == 0, "shell: all panels in visual tree");
var outDir = args.Length > 0 ? args[0] : Path.GetTempPath();
Directory.CreateDirectory(outDir);
var frame = window.CaptureRenderedFrame();
if (frame is null)
{
Console.WriteLine("[FAIL] golden: editor_shell_1280x720 (no frame captured)");
return 1;
}
var shotPath = Path.Combine(outDir, "editor_shell_1280x720.png");
frame.Save(shotPath, PngBitmapEncoderOptions.Default);
Console.WriteLine($"[PASS] shell: screenshot saved to {shotPath}");
// The goldens were rendered on Linux. Font smoothing differs per OS
// (~1% of pixels on macOS/Windows), so only Linux compares pixels;
// the structural checks run everywhere.
var comparePixels = OperatingSystem.IsLinux();
var goldenDir = GoldenDir();
if (comparePixels)
{
failures += Golden.Check("editor_shell_1280x720", frame, goldenDir);
}
else
{
Console.WriteLine("[SKIP] golden: editor_shell_1280x720 (Linux-rendered golden; font rasterization differs on this OS)");
}
// P3-03: hierarchy panel checks + golden (3 nested entities) —
// captured from the SAME window session (Avalonia allows one Setup
// per process, so the panel renders into the shell window).
failures += HierarchyPanelGolden.Check(window, outDir, goldenDir, comparePixels);
// P3-13: viewport control headless checks (GPU toggle + CPU fallback).
// Runs in the same session; the control is never added to the shell
// window, so the editor_shell_1280x720 golden is untouched.
failures += ViewportPanelGolden.Check(window);
window.Close();
return failures;
}
private static HashSet<string> CollectNames(Visual visual)
{
var names = new HashSet<string>(StringComparer.Ordinal);
foreach (var descendant in visual.GetVisualDescendants())
{
if (descendant is Control control && !string.IsNullOrEmpty(control.Name))
{
names.Add(control.Name);
}
}
return names;
}
private static string GoldenDir()
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir is not null)
{
var candidate = Path.Combine(dir.FullName, "managed", "Engine.EditorApp.Tests", "golden");
if (Directory.Exists(candidate))
{
return candidate;
}
dir = dir.Parent;
}
return Path.Combine(AppContext.BaseDirectory, "golden");
}
private static int Check(bool ok, string name)
{
Console.WriteLine($"[{(ok ? "PASS" : "FAIL")}] {name}");
return ok ? 0 : 1;
}
}