| Name | Message | Date |
|---|---|---|
| 📄 InteractShould.cs | 5 days ago | |
| 📄 RenderShould.cs | 5 days ago |
📄
ui/tests/MainWindowTests/RenderShould.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using System; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Headless; using Avalonia.Headless.XUnit; using Avalonia.Input; using Avalonia.Media.Imaging; using Avalonia.Threading; using Cfa.Ui.Services; using Cfa.Ui.ViewModels; using Cfa.Ui.Views; using Nodify; using Shouldly; namespace Cfa.Ui.Tests.MainWindowTests; public sealed class RenderShould { [AvaloniaFact] public async Task MatchThePreviousBitmapAfterFittingTheRealSolutionGraph() { // Arrange var directory = new DirectoryInfo(AppContext.BaseDirectory); while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "code-flow-analysis.slnx"))) directory = directory.Parent; var root = directory.ShouldNotBeNull().FullName; using var model = new MainViewModel(new Desktop(root), new ScanSessionFactory()); var window = new MainWindow { DataContext = model, Width = 1400, Height = 900, }; window.Show(); try { await model.OpenSolutionCommand.ExecuteAsync(null); model.Address = "core/tests/AnalyzerTests/AnalyzeShould.cs:15"; await model.AnalyzeCommand.ExecuteAsync(null); Dispatcher.UIThread.RunJobs(); window.UpdateLayout(); var editor = window.FindControl<NodifyEditor>("Editor")!; editor.ViewportZoom = 0.2; editor.ViewportLocation = new Point(-500, -500); var fit = window.FindControl<Button>("FitGraphButton")!; var point = fit.TranslatePoint(new Point(fit.Bounds.Width / 2, fit.Bounds.Height / 2), window)!.Value; // Act window.MouseDown(point, MouseButton.Left); window.MouseUp(point, MouseButton.Left); window.MouseMove(new Point(5, 5)); Dispatcher.UIThread.RunJobs(); window.UpdateLayout(); // Repaint the entire window rather than reusing incremental frame damage. using var actual = new RenderTargetBitmap(new PixelSize(1400, 900), new Vector(96, 96)); actual.Render(window); // Assert var scene = model.Scene.ShouldNotBeNull(); var bounds = scene.Nodes.Select(node => node.Bounds).Aggregate((first, next) => first.Union(next)); var center = (bounds.Center - editor.ViewportLocation) * editor.ViewportZoom; center.X.ShouldBe(editor.Bounds.Width / 2, 0.01); center.Y.ShouldBe(editor.Bounds.Height / 2, 0.01); var available = new Rect(editor.Bounds.Size); editor.ViewportZoom.ShouldBe( Math.Min( editor.MaxViewportZoom, Math.Min(editor.Bounds.Width / bounds.Width, editor.Bounds.Height / bounds.Height) ), 0.000001 ); foreach (var node in scene.Nodes) { var location = (node.Location - editor.ViewportLocation) * editor.ViewportZoom; available .Inflate(0.000001) .Contains(new Rect(location, new Size(node.Width, node.Height) * editor.ViewportZoom)) .ShouldBeTrue(); } if (Environment.GetEnvironmentVariable("CFA_UI_ARTIFACTS") is { } artifacts) { Directory.CreateDirectory(artifacts); actual.Save(Path.Combine(artifacts, "analyzer-fit.png")); } if (Environment.GetEnvironmentVariable("CFA_UPDATE_SNAPSHOTS") == "1") actual.Save(Path.Combine(root, "ui/tests/Snapshots/analyzer-fit.png")); using var expected = new Bitmap(Path.Combine(root, "ui/tests/Snapshots/analyzer-fit.png")); actual.PixelSize.ShouldBe(expected.PixelSize); // Decode both images identically to normalize their native pixel formats. using var encoded = new MemoryStream(); actual.Save(encoded); encoded.Position = 0; using var normalized = new Bitmap(encoded); var actualPixels = Pixels(normalized); var expectedPixels = Pixels(expected); actualPixels .SequenceEqual(expectedPixels) .ShouldBeTrue("The rendered bitmap must match every baseline pixel."); } finally { window.Close(); } } private static byte[] Pixels(Bitmap bitmap) { var stride = bitmap.PixelSize.Width * 4; var pixels = new byte[stride * bitmap.PixelSize.Height]; var buffer = Marshal.AllocHGlobal(pixels.Length); try { bitmap.CopyPixels(new PixelRect(bitmap.PixelSize), buffer, pixels.Length, stride); Marshal.Copy(buffer, pixels, 0, pixels.Length); return pixels; } finally { Marshal.FreeHGlobal(buffer); } } private sealed class Desktop(string root) : IDesktopServices { public Task<FileInfo?> PickSolutionAsync() => Task.FromResult<FileInfo?>(new FileInfo(Path.Combine(root, "code-flow-analysis.slnx"))); public Task ShowErrorAsync(string message) => throw new InvalidOperationException(message); public Task OpenSourceAsync(string link) => Task.CompletedTask; } }