Skip to content

Commit

Permalink
Workspace Icon Test update (#815)
Browse files Browse the repository at this point in the history
* Add Verify

* Update tests

* Use WorkspaceGridState
  • Loading branch information
erri120 authored Dec 14, 2023
1 parent d7db8c7 commit 8c8ce9e
Show file tree
Hide file tree
Showing 21 changed files with 166 additions and 50 deletions.
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,13 @@ indent_size = 2

[*.csproj]
indent_size = 4

# Verify settings
[*.{received,verified}.{txt,xml,json}]
charset = "utf-8-bom"
end_of_line = lf
indent_size = unset
indent_style = unset
insert_final_newline = false
tab_width = unset
trim_trailing_whitespace = false
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,8 @@
.gitattributes export-ignore
.gitignore export-ignore
.gitkeep export-ignore

# Verify
*.verified.txt text eol=lf working-tree-encoding=UTF-8
*.verified.xml text eol=lf working-tree-encoding=UTF-8
*.verified.json text eol=lf working-tree-encoding=UTF-8
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ Thumbs.db
Desktop.ini
.DS_Store

coverage.json
coverage.json

# Verify
*.received.*
14 changes: 8 additions & 6 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
</ItemGroup>
<ItemGroup>
<!-- Testing -->
<PackageVersion Include="AutoFixture" Version="4.18.0" />
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.0" />
<PackageVersion Include="AutoFixture" Version="4.18.1" />
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -80,12 +80,14 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit" Version="2.6.1" />
<PackageVersion Include="Xunit.DependencyInjection" Version="8.9.0" />
<PackageVersion Include="Verify.Xunit" Version="22.8.0" />
<PackageVersion Include="Verify.ImageMagick" Version="3.2.2" />
<PackageVersion Include="xunit" Version="2.6.3" />
<PackageVersion Include="Xunit.DependencyInjection" Version="8.9.1" />
<PackageVersion Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
<PackageVersion Include="Xunit.DependencyInjection.SkippableFact" Version="8.1.0" />
<PackageVersion Include="xunit.extensibility.core" Version="2.6.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3">
<PackageVersion Include="xunit.extensibility.core" Version="2.6.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
Expand Down
2 changes: 2 additions & 0 deletions NexusMods.App.sln
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solutionItems", ".solution
NuGet.Build.props = NuGet.Build.props
README.md = README.md
Directory.Packages.props = Directory.Packages.props
.gitignore = .gitignore
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NexusMods.Games.FOMOD", "src\Games\NexusMods.Games.FOMOD\NexusMods.Games.FOMOD.csproj", "{B43A31B2-1F08-4D8E-9C45-48015FBA983E}"
Expand Down Expand Up @@ -129,6 +130,7 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Games.BladeAndSorcery", "src\Games\NexusMods.Games.BladeAndSorcery\NexusMods.Games.BladeAndSorcery.csproj", "{81F23A27-F517-41AD-B86E-6DCE7B4CCE93}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Games.BladeAndSorcery.Tests", "tests\Games\NexusMods.Games.BladeAndSorcery.Tests\NexusMods.Games.BladeAndSorcery.Tests.csproj", "{C2F6C9E5-CC53-44B7-994C-5B9287408263}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Games.AdvancedInstaller.UI", "src\Games\NexusMods.Games.AdvancedInstaller.UI\NexusMods.Games.AdvancedInstaller.UI.csproj", "{07B8ACA6-CE4B-496D-B183-63A57C5F08E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Games.AdvancedInstaller.UI.Tests", "tests\Games\NexusMods.Games.AdvancedInstaller.UI.Tests\NexusMods.Games.AdvancedInstaller.UI.Tests.csproj", "{2BFAAE53-AFFF-4F0B-AD76-67918665F298}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ public class AddPanelButtonDesignViewModel : AddPanelButtonViewModel
{
public AddPanelButtonDesignViewModel() : base(DummyState, IconUtils.StateToBitmap(DummyState)) { }

private static readonly IReadOnlyDictionary<PanelId, Rect> DummyState = new Dictionary<PanelId, Rect>
{
{ PanelId.NewId(), new Rect(0, 0, 0.5, 0.5) },
{ PanelId.DefaultValue, new Rect(0.5, 0, 0.5, 0.5) },
{ PanelId.NewId(), new Rect(0, 0.5, 0.5, 0.5) },
{ PanelId.NewId(), new Rect(0.5, 0.5, 0.5, 0.5) },
};
private static readonly WorkspaceGridState DummyState = WorkspaceGridState.From(
isHorizontal: true,
new PanelGridState(PanelId.NewId(), new Rect(0, 0, 0.5, 0.5)),
new PanelGridState(PanelId.NewId(), new Rect(0, 0.5, 0.5, 0.5)),
new PanelGridState(PanelId.NewId(), new Rect(0.5, 0, 0.5, 0.5)),
new PanelGridState(PanelId.DefaultValue, new Rect(0.5, 0.5, 0.5, 0.5))
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ namespace NexusMods.App.UI.WorkspaceSystem;

public class AddPanelButtonViewModel : AViewModel<IAddPanelButtonViewModel>, IAddPanelButtonViewModel
{
public IReadOnlyDictionary<PanelId, Rect> NewLayoutState { get; }
public WorkspaceGridState NewLayoutState { get; }
public IImage ButtonImage { get; }
public ReactiveCommand<Unit, IReadOnlyDictionary<PanelId, Rect>> AddPanelCommand { get; }
public ReactiveCommand<Unit, WorkspaceGridState> AddPanelCommand { get; }

public AddPanelButtonViewModel(
IReadOnlyDictionary<PanelId, Rect> newLayoutState,
WorkspaceGridState newLayoutState,
IImage buttonImage)
{
NewLayoutState = newLayoutState;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using System.Reactive;
using Avalonia;
using Avalonia.Media;
using ReactiveUI;

namespace NexusMods.App.UI.WorkspaceSystem;

public interface IAddPanelButtonViewModel : IViewModelInterface
{
public IReadOnlyDictionary<PanelId, Rect> NewLayoutState { get; }
public WorkspaceGridState NewLayoutState { get; }

public IImage ButtonImage { get; }

public ReactiveCommand<Unit, IReadOnlyDictionary<PanelId, Rect>> AddPanelCommand { get; }
public ReactiveCommand<Unit, WorkspaceGridState> AddPanelCommand { get; }
}
8 changes: 4 additions & 4 deletions src/NexusMods.App.UI/WorkspaceSystem/IconUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal static class IconUtils
/// <summary>
/// Generates a <see cref="Bitmap"/> for the given state.
/// </summary>
internal static Bitmap StateToBitmap(IReadOnlyDictionary<PanelId, Rect> state)
internal static Bitmap StateToBitmap(WorkspaceGridState state)
{
using var skPicture = GeneratePicture(state);
using var skBitmap = skPicture.ToBitmap(
Expand All @@ -39,7 +39,7 @@ internal static Bitmap StateToBitmap(IReadOnlyDictionary<PanelId, Rect> state)
return skBitmap.ToAvaloniaImage();
}

private static SKPicture GeneratePicture(IReadOnlyDictionary<PanelId, Rect> state)
private static SKPicture GeneratePicture(WorkspaceGridState state)
{
using var skPictureRecorder = new SKPictureRecorder();
using var skCanvas = skPictureRecorder.BeginRecording(new SKRect(0f, 0f, IconSize, IconSize));
Expand All @@ -52,9 +52,9 @@ private static SKPicture GeneratePicture(IReadOnlyDictionary<PanelId, Rect> stat
using var skPathFilled = new SKPath();
using var skPathHollow = new SKPath();

foreach (var kv in state)
foreach (var panel in state)
{
var (panelId, rect) = kv;
var (panelId, rect) = panel;
DrawRect(skPathFilled, skPathHollow, rect, isHollow: panelId != PanelId.DefaultValue);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public interface IWorkspaceViewModel : IViewModelInterface

public ReadOnlyObservableCollection<IAddPanelButtonViewModel> AddPanelButtonViewModels { get; }

public bool IsHorizontal { get; }

/// <summary>
/// Called by the View to notify the VM about the new size of the control.
/// </summary>
Expand All @@ -20,7 +22,7 @@ public interface IWorkspaceViewModel : IViewModelInterface
/// Add a new panel to the workspace.
/// </summary>
/// <returns>The newly created <see cref="IPanelViewModel"/>.</returns>
public IPanelViewModel AddPanel(IReadOnlyDictionary<PanelId, Rect> state);
public IPanelViewModel AddPanel(WorkspaceGridState state);

/// <summary>
/// Transforms the current state of the workspace into a serializable data format.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text.Json;
using Avalonia;
using Microsoft.Extensions.DependencyInjection;
using NexusMods.App.UI.RightContent.LoadoutGrid;
using NexusMods.Common;
using NexusMods.DataModel.Loadouts;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;

Expand Down Expand Up @@ -63,10 +59,10 @@ public WorkspacePlaygroundViewModel()

this.WhenActivated(disposables =>
{
WorkspaceViewModel.AddPanel(new Dictionary<PanelId, Rect>
WorkspaceViewModel.AddPanel(WorkspaceGridState.From(new[]
{
{ PanelId.DefaultValue, MathUtils.One }
});
new PanelGridState(PanelId.DefaultValue, MathUtils.One)
}, isHorizontal: WorkspaceViewModel.IsHorizontal));

Disposable.Create(() => { }).DisposeWith(disposables);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public WorkspaceViewModel(PageFactoryController factoryController)

private Size _lastWorkspaceSize;

[Reactive] private bool IsHorizontal { get; set; }
[Reactive] public bool IsHorizontal { get; private set; }

/// <inheritdoc/>
public void Arrange(Size workspaceSize)
Expand Down Expand Up @@ -244,10 +244,8 @@ private void UpdateStates()

foreach (var state in newStates)
{
var dict = state.ToDictionary();

var image = IconUtils.StateToBitmap(dict);
updater.Add(new AddPanelButtonViewModel(dict, image));
var image = IconUtils.StateToBitmap(state);
updater.Add(new AddPanelButtonViewModel(state, image));
}
});
}
Expand All @@ -271,14 +269,14 @@ private void UpdateResizers()
}

/// <inheritdoc/>
public IPanelViewModel AddPanel(IReadOnlyDictionary<PanelId, Rect> state)
public IPanelViewModel AddPanel(WorkspaceGridState state)
{
IPanelViewModel panelViewModel = null!;
_panelSource.Edit(updater =>
{
foreach (var kv in state)
foreach (var panel in state)
{
var (panelId, logicalBounds) = kv;
var (panelId, logicalBounds) = panel;
if (panelId == PanelId.DefaultValue)
{
panelViewModel = new PanelViewModel(_factoryController)
Expand Down
10 changes: 10 additions & 0 deletions src/NexusMods.App.UI/WorkspaceSystem/WorkspaceGridState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ public static WorkspaceGridState From(IEnumerable<PanelGridState> panels, bool i
);
}

public static WorkspaceGridState From(bool isHorizontal, params PanelGridState[] panels) => From(panels, isHorizontal);

public static WorkspaceGridState From(IReadOnlyDictionary<PanelId, Rect> panels, bool isHorizontal)
{
return new WorkspaceGridState(
inner: panels.Select(kv => new PanelGridState(kv.Key, kv.Value)).ToImmutableSortedSet(PanelGridStateComparer.Instance),
isHorizontal
);
}

public static WorkspaceGridState Empty(bool isHorizontal) => new(ImmutableSortedSet<PanelGridState>.Empty, isHorizontal);

private WorkspaceGridState WithInner(ImmutableSortedSet<PanelGridState> inner)
Expand Down
20 changes: 20 additions & 0 deletions tests/NexusMods.UI.Tests/ModuleInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Runtime.CompilerServices;
using ImageMagick;

namespace NexusMods.UI.Tests;

public static class ModuleInitializer
{
[ModuleInitializer]
public static void Init()
{
VerifyImageMagick.Initialize();
VerifyImageMagick.RegisterComparers(threshold: 0.005D, metric: ErrorMetric.Fuzz);
}

[ModuleInitializer]
public static void InitOther()
{
VerifierSettings.InitializePlugins();
}
}
6 changes: 6 additions & 0 deletions tests/NexusMods.UI.Tests/NexusMods.UI.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
<ItemGroup>
<PackageReference Include="ReactiveUI.Fody" />
<PackageReference Include="Avalonia.Headless" />
<PackageReference Include="Verify.Xunit" />
<PackageReference Include="Verify.ImageMagick"/>
</ItemGroup>

<ItemGroup>
Expand All @@ -23,5 +25,9 @@
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="WorkspaceSystem\IconUtilsTests.Test_StateToBitmap.verified.png">
<ParentFile>IconUtilsTests</ParentFile>
<DependentUpon>IconUtilsTests.cs</DependentUpon>
</None>
</ItemGroup>
</Project>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
83 changes: 73 additions & 10 deletions tests/NexusMods.UI.Tests/WorkspaceSystem/IconUtilsTests.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,86 @@
using System.Runtime.CompilerServices;
using Avalonia;
using FluentAssertions;
using NexusMods.App.UI.WorkspaceSystem;

namespace NexusMods.UI.Tests.WorkspaceSystem;

public class IconUtilsTests : AUiTest
[UsesVerify]
public class IconUtilsTests(IServiceProvider provider) : AUiTest(provider)
{
public IconUtilsTests(IServiceProvider provider) : base(provider) { }
[Fact]
public Task Test_StateToBitmap_TwoColumns()
{
var state = WorkspaceGridState.From(
isHorizontal: true,
new PanelGridState(PanelId.NewId(),new Rect(0, 0, 0.5, 1)),
new PanelGridState(PanelId.DefaultValue, new Rect(0.5, 0, 0.5, 1))
);

return RunVerify(state);
}

[Fact]
public Task Test_StateToBitmap_TwoRows()
{
var state = WorkspaceGridState.From(
isHorizontal: true,
new PanelGridState(PanelId.NewId(), new Rect(0, 0, 1, 0.5)),
new PanelGridState(PanelId.DefaultValue, new Rect(0, 0.5, 1, 0.5))
);

return RunVerify(state);
}

[Fact]
public void Test_StateToBitmap()
public Task Test_StateToBitmap_ThreePanels_OneLargeColumn()
{
var state = WorkspaceGridState.From(
isHorizontal: true,
new PanelGridState(PanelId.NewId(),new Rect(0, 0, 0.5, 0.5)),
new PanelGridState(PanelId.DefaultValue, new Rect(0.5, 0, 0.5, 1)),
new PanelGridState(PanelId.NewId(), new Rect(0, 0.5, 0.5, 0.5))
);

return RunVerify(state);
}

[Fact]
public Task Test_StateToBitmap_ThreePanels_OneLargeRow()
{
var state = WorkspaceGridState.From(
isHorizontal: true,
new PanelGridState(PanelId.NewId(), new Rect(0, 0, 0.5, 0.5)),
new PanelGridState(PanelId.NewId(), new Rect(0.5, 0, 0.5, 0.5)),
new PanelGridState(PanelId.DefaultValue,new Rect(0, 0.5, 1, 0.5))
);

return RunVerify(state);
}

[Fact]
public Task Test_StateToBitmap_FourPanels()
{
var state = WorkspaceGridState.From(
isHorizontal: true,
new PanelGridState(PanelId.NewId(), new Rect(0, 0, 0.5, 0.5)),
new PanelGridState(PanelId.NewId(), new Rect(0, 0.5, 0.5, 0.5)),
new PanelGridState(PanelId.NewId(), new Rect(0.5, 0, 0.5, 0.5)),
new PanelGridState(PanelId.DefaultValue, new Rect(0.5, 0.5, 0.5, 0.5))
);

return RunVerify(state);
}

private static Task RunVerify(WorkspaceGridState state, [CallerFilePath] string sourceFile = "")
{
var state = new Dictionary<PanelId, Rect>
using var stream = new MemoryStream();
using (var bitmap = IconUtils.StateToBitmap(state))
{
{ PanelId.NewId(), new Rect(0, 0, 0.5, 1) },
{ PanelId.DefaultValue, new Rect(0.5, 0, 0.5, 1) }
};
bitmap.Save(stream);
stream.Position = 0;
}

var bitmap = IconUtils.StateToBitmap(state);
bitmap.Size.Should().Be(new Size(150, 150));
// ReSharper disable once ExplicitCallerInfoArgument
return Verify(stream, extension: "png", sourceFile: sourceFile).DisableDiff();
}
}

0 comments on commit 8c8ce9e

Please sign in to comment.