using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Themes.Fluent; using Avalonia.Threading; using Avalonia.UnitTests; using BenchmarkDotNet.Attributes; namespace Avalonia.Benchmarks.Navigation { /// /// Measures the cost of creating a NavigationPage and applying its template. /// [MemoryDiagnoser] public class NavigationPageCreationBenchmark : IDisposable { private readonly IDisposable _app; private readonly TestRoot _root; public NavigationPageCreationBenchmark() { AssetLoader.RegisterResUriParsers(); _app = UnitTestApplication.Start(TestServices.StyledWindow.With( theme: () => new Styles { new FluentTheme() })); _root = new TestRoot(true, null) { Renderer = new NullRenderer() }; _root.LayoutManager.ExecuteInitialLayoutPass(); } [Benchmark] [MethodImpl(MethodImplOptions.NoInlining)] public void Create() { _root.Child = new NavigationPage { PageTransition = null }; _root.LayoutManager.ExecuteLayoutPass(); Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); } public void Dispose() => _app.Dispose(); } /// /// Measures push and pop on a NavigationPage that already has two pages on its stack. /// Transitions are disabled so only the stack-management and layout cost is captured. /// [MemoryDiagnoser] public class NavigationPageStackBenchmark : IDisposable { private readonly IDisposable _app; private readonly TestRoot _root; private NavigationPage _nav = null!; public NavigationPageStackBenchmark() { AssetLoader.RegisterResUriParsers(); _app = UnitTestApplication.Start(TestServices.StyledWindow.With( theme: () => new Styles { new FluentTheme() })); _root = new TestRoot(true, null) { Renderer = new NullRenderer() }; _root.LayoutManager.ExecuteInitialLayoutPass(); } [IterationSetup] public void SetupIteration() { _nav = new NavigationPage { PageTransition = null }; _root.Child = _nav; _root.LayoutManager.ExecuteLayoutPass(); _nav.PushAsync(new ContentPage { Header = "Root" }).GetAwaiter().GetResult(); _nav.PushAsync(new ContentPage { Header = "Page 2" }).GetAwaiter().GetResult(); _root.LayoutManager.ExecuteLayoutPass(); Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); } /// /// Push a page onto a stack that already has two entries (depth 2 → 3). /// [Benchmark] [MethodImpl(MethodImplOptions.NoInlining)] public async Task Push() { await _nav.PushAsync(new ContentPage { Header = "Page 3" }); _root.LayoutManager.ExecuteLayoutPass(); } /// /// Pop the top page from a stack with two entries (depth 2 → 1). /// [Benchmark] [MethodImpl(MethodImplOptions.NoInlining)] public async Task Pop() { await _nav.PopAsync(); _root.LayoutManager.ExecuteLayoutPass(); } public void Dispose() => _app.Dispose(); } }