Files

142 lines
5.3 KiB
C#

using AtomUI.Theme;
using AtomUI.Theme.Compilation;
using AtomUI.Theme.Configuration;
using Shouldly;
using Xunit;
namespace AtomUI.Core.Tests.Theme;
[Collection(ThemeConfigProviderTestCollection.Name)]
public class ThemeScopeGraphTests
{
[Fact]
public void Registration_Ids_Are_Monotonic_And_Are_Not_Reused_After_Reattach()
{
var manager = new ThemeManager(static () => true);
var root = CreateRootContext(manager);
var graph = new ThemeScopeGraph(manager);
var provider = new ThemeConfigProvider();
var first = graph.Register(provider, root, EmptyConfig());
var firstId = first.RegistrationId;
first.Dispose();
var second = graph.Register(provider, root, EmptyConfig());
second.RegistrationId.ShouldBeGreaterThan(firstId);
graph.TopologyRevision.ShouldBe(3);
}
[Fact]
public void Capture_Records_Parent_And_Config_Revisions_In_Topological_Order()
{
var manager = new ThemeManager(static () => true);
var root = CreateRootContext(manager);
var graph = new ThemeScopeGraph(manager);
var parentProvider = new ThemeConfigProvider();
var childProvider = new ThemeConfigProvider();
using var parent = graph.Register(parentProvider, root, EmptyConfig());
using var child = graph.Register(childProvider, parent.Context, EmptyConfig());
graph.ReplaceConfig(child.RegistrationId, ConfigWithToken("ColorPrimary", "#ff0000"));
var capture = graph.CaptureSubtree(parent.RegistrationId);
capture.Nodes.Select(static node => node.Stamp.RegistrationId)
.ShouldBe([parent.RegistrationId, child.RegistrationId]);
capture.Nodes[0].Stamp.ParentRegistrationId.ShouldBe(0);
capture.Nodes[1].Stamp.ParentRegistrationId.ShouldBe(parent.RegistrationId);
capture.Nodes[1].Stamp.ConfigRevision.ShouldBe(1);
graph.IsCurrent(capture).ShouldBeTrue();
}
[Fact]
public void Topology_Changes_Invalidate_Previous_Captures()
{
var manager = new ThemeManager(static () => true);
var root = CreateRootContext(manager);
var graph = new ThemeScopeGraph(manager);
var provider = new ThemeConfigProvider();
var first = graph.Register(provider, root, EmptyConfig());
var capture = graph.CaptureSubtree(first.RegistrationId);
first.Dispose();
using var second = graph.Register(provider, root, EmptyConfig());
graph.IsCurrent(capture).ShouldBeFalse();
second.RegistrationId.ShouldNotBe(capture.Nodes[0].Stamp.RegistrationId);
}
[Fact]
public void Reparent_Replaces_The_Old_Edge_And_Invalidates_The_Stamp()
{
var manager = new ThemeManager(static () => true);
var root = CreateRootContext(manager);
var graph = new ThemeScopeGraph(manager);
using var firstParent = graph.Register(new ThemeConfigProvider(), root, EmptyConfig());
using var secondParent = graph.Register(new ThemeConfigProvider(), root, EmptyConfig());
using var child = graph.Register(new ThemeConfigProvider(), firstParent.Context, EmptyConfig());
var before = graph.CaptureSubtree(child.RegistrationId);
graph.Reparent(child.RegistrationId, secondParent.Context);
var after = graph.CaptureSubtree(child.RegistrationId);
graph.IsCurrent(before).ShouldBeFalse();
after.Nodes[0].Stamp.ParentRegistrationId.ShouldBe(secondParent.RegistrationId);
}
[Fact]
public void Context_Keeps_One_Resource_Provider_While_Snapshots_Change()
{
var manager = new ThemeManager(static () => true);
var first = CreateSnapshot();
var second = CreateSnapshot();
var context = new ThemeContext(manager, first, 0);
var provider = context.ResourceProvider;
var published = 0;
context.Published += (_, _) => published++;
context.Commit(second);
context.Publish();
context.Snapshot.ShouldBeSameAs(second);
context.ResourceProvider.ShouldBeSameAs(provider);
provider.Snapshot.ShouldBeSameAs(second);
published.ShouldBe(1);
}
[Fact]
public void Context_Publish_Continues_After_An_Observer_Fails()
{
var snapshot = ThemeTestSnapshotFactory.Compile();
var context = new ThemeContext(new ThemeManager(static () => true), snapshot, 1);
var laterObserverRan = false;
context.Published += (_, _) => throw new InvalidOperationException("observer failure");
context.Published += (_, _) => laterObserverRan = true;
context.Publish(notifyResources: false);
laterObserverRan.ShouldBeTrue();
}
private static ThemeContext CreateRootContext(ThemeManager manager)
{
return new ThemeContext(manager, CreateSnapshot(), 0);
}
private static ThemeSnapshot CreateSnapshot()
{
var registry = TypedThemeSnapshotCacheTests.CreateRegistry();
var input = TypedThemeSnapshotCacheTests.CreateInput(registry);
return new ThemeCompiler().Compile(input).Snapshot!;
}
private static ThemeConfig EmptyConfig()
{
return new ThemeConfigBuilder().Build();
}
private static ThemeConfig ConfigWithToken(string name, string value)
{
return new ThemeConfigBuilder().WithToken(name, value).Build();
}
}