csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
4.7 KiB
160 lines
4.7 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Avalonia.SourceGenerator;
|
|
using Xunit;
|
|
|
|
namespace Avalonia.Base.UnitTests.SourceGenerators;
|
|
|
|
public class CrossThreadProxyGeneratorTests
|
|
{
|
|
public enum TestPriority { Low, Normal, High }
|
|
|
|
[GenerateCrossThreadProxy(typeof(TestPriority), "Avalonia.Base.UnitTests.SourceGenerators.CrossThreadProxyGeneratorTests.TestPriority.Normal")]
|
|
public interface IBaseProxied
|
|
{
|
|
void BaseFireAndForget(int x);
|
|
}
|
|
|
|
[GenerateCrossThreadProxy(typeof(TestPriority), "Avalonia.Base.UnitTests.SourceGenerators.CrossThreadProxyGeneratorTests.TestPriority.Normal")]
|
|
public interface IDerivedProxied : IBaseProxied
|
|
{
|
|
void Increment();
|
|
int GetValue();
|
|
|
|
[GenerateCrossThreadProxyReturnTask]
|
|
void AsyncFireAndForget(string s);
|
|
}
|
|
|
|
private sealed class Target : IDerivedProxied
|
|
{
|
|
public List<int> BaseCalls { get; } = new();
|
|
public int Counter;
|
|
public List<string> AsyncCalls { get; } = new();
|
|
public Func<int>? GetValueImpl;
|
|
|
|
public void BaseFireAndForget(int x) => BaseCalls.Add(x);
|
|
public void Increment() => Counter++;
|
|
public int GetValue() => GetValueImpl?.Invoke() ?? Counter;
|
|
public void AsyncFireAndForget(string s) => AsyncCalls.Add(s);
|
|
}
|
|
|
|
private sealed class QueueMarshaller
|
|
{
|
|
public readonly Queue<(Action action, TestPriority priority)> Queue = new();
|
|
public void Post(Action a, TestPriority p) => Queue.Enqueue((a, p));
|
|
public void DrainAll()
|
|
{
|
|
while (Queue.Count > 0) Queue.Dequeue().action();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Fire_and_forget_void_routes_through_marshaller_with_default_priority()
|
|
{
|
|
var t = new Target();
|
|
var m = new QueueMarshaller();
|
|
var proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
proxy.Increment();
|
|
|
|
Assert.Single(m.Queue);
|
|
Assert.Equal(TestPriority.Normal, m.Queue.Peek().priority);
|
|
Assert.Equal(0, t.Counter);
|
|
m.DrainAll();
|
|
Assert.Equal(1, t.Counter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Explicit_priority_overload_is_used()
|
|
{
|
|
var t = new Target();
|
|
var m = new QueueMarshaller();
|
|
var proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
proxy.Increment(TestPriority.High);
|
|
|
|
Assert.Equal(TestPriority.High, m.Queue.Peek().priority);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task NonVoid_returns_Task_completed_after_marshaller_runs()
|
|
{
|
|
var t = new Target { Counter = 42 };
|
|
var m = new QueueMarshaller();
|
|
var proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
var task = proxy.GetValue();
|
|
Assert.False(task.IsCompleted);
|
|
m.DrainAll();
|
|
Assert.True(task.IsCompleted);
|
|
Assert.Equal(42, await task);
|
|
}
|
|
|
|
[Fact]
|
|
public void Exception_in_target_propagates_to_Task()
|
|
{
|
|
var t = new Target { GetValueImpl = () => throw new InvalidOperationException("boom") };
|
|
var m = new QueueMarshaller();
|
|
var proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
var task = proxy.GetValue();
|
|
m.DrainAll();
|
|
Assert.True(task.IsFaulted);
|
|
Assert.IsType<InvalidOperationException>(task.Exception!.InnerException);
|
|
}
|
|
|
|
[Fact]
|
|
public void Void_method_with_ReturnTask_attribute_returns_Task()
|
|
{
|
|
var t = new Target();
|
|
var m = new QueueMarshaller();
|
|
var proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
Task task = proxy.AsyncFireAndForget("hi");
|
|
Assert.False(task.IsCompleted);
|
|
m.DrainAll();
|
|
Assert.True(task.IsCompleted);
|
|
Assert.Equal(new[] { "hi" }, t.AsyncCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public void Inherited_method_routes_through_base_proxy()
|
|
{
|
|
var t = new Target();
|
|
var m = new QueueMarshaller();
|
|
var proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
proxy.BaseFireAndForget(7);
|
|
Assert.Single(m.Queue);
|
|
m.DrainAll();
|
|
Assert.Equal(new[] { 7 }, t.BaseCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public void Derived_proxy_is_assignable_to_base_proxy()
|
|
{
|
|
var t = new Target();
|
|
var m = new QueueMarshaller();
|
|
BaseProxiedProxy proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
proxy.BaseFireAndForget(3);
|
|
m.DrainAll();
|
|
Assert.Equal(new[] { 3 }, t.BaseCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public void Marshaller_is_not_invoked_synchronously_during_proxy_call()
|
|
{
|
|
var t = new Target();
|
|
var m = new QueueMarshaller();
|
|
var proxy = new DerivedProxiedProxy(t, m.Post);
|
|
|
|
proxy.Increment();
|
|
proxy.Increment();
|
|
proxy.Increment();
|
|
|
|
Assert.Equal(0, t.Counter);
|
|
Assert.Equal(3, m.Queue.Count);
|
|
}
|
|
}
|
|
|