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.
249 lines
7.2 KiB
249 lines
7.2 KiB
#nullable enable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Reactive.Subjects;
|
|
using System.Runtime.CompilerServices;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Data;
|
|
using Avalonia.Data.Core;
|
|
using Avalonia.Markup.Xaml.MarkupExtensions;
|
|
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
|
|
using Avalonia.Threading;
|
|
using JetBrains.dotMemoryUnit;
|
|
using Xunit;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace Avalonia.LeakTests
|
|
{
|
|
[DotMemoryUnit(FailIfRunWithoutSupport = false)]
|
|
public class AvaloniaObjectTests
|
|
{
|
|
public AvaloniaObjectTests(ITestOutputHelper atr)
|
|
{
|
|
DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine);
|
|
}
|
|
|
|
[Fact]
|
|
public void Binding_To_Direct_Property_Does_Not_Get_Collected()
|
|
{
|
|
var target = new Class1();
|
|
|
|
Func<WeakReference> setupBinding = () =>
|
|
{
|
|
var source = new Subject<string>();
|
|
var sub = target.Bind((AvaloniaProperty)Class1.FooProperty, source);
|
|
source.OnNext("foo");
|
|
return new WeakReference(source);
|
|
};
|
|
|
|
var weakSource = setupBinding();
|
|
|
|
CollectGarbage();
|
|
|
|
Assert.Equal("foo", target.Foo);
|
|
Assert.True(weakSource.IsAlive);
|
|
}
|
|
|
|
[Fact]
|
|
public void Binding_To_Direct_Property_Gets_Collected_When_Completed()
|
|
{
|
|
var target = new Class1();
|
|
|
|
Func<WeakReference> setupBinding = () =>
|
|
{
|
|
var source = new Subject<string>();
|
|
var sub = target.Bind((AvaloniaProperty)Class1.FooProperty, source);
|
|
return new WeakReference(source);
|
|
};
|
|
|
|
var weakSource = setupBinding();
|
|
|
|
Action completeSource = () =>
|
|
{
|
|
((ISubject<string>)weakSource.Target).OnCompleted();
|
|
};
|
|
|
|
completeSource();
|
|
CollectGarbage();
|
|
Assert.False(weakSource.IsAlive);
|
|
}
|
|
|
|
[Fact]
|
|
public void CompiledBinding_To_InpcProperty_With_Alive_Source_Does_Not_Keep_Target_Alive()
|
|
{
|
|
var source = new Class2 { Foo = "foo" };
|
|
|
|
WeakReference SetupBinding()
|
|
{
|
|
var path = new CompiledBindingPathBuilder()
|
|
.Property(
|
|
new ClrPropertyInfo(
|
|
nameof(Class2.Foo),
|
|
target => ((Class2)target).Foo,
|
|
(target, value) => ((Class2)target).Foo = (string?)value,
|
|
typeof(string)),
|
|
PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
|
|
.Build();
|
|
|
|
var target = new TextBlock();
|
|
|
|
target.Bind(TextBlock.TextProperty, new CompiledBindingExtension
|
|
{
|
|
Source = source,
|
|
Path = path
|
|
});
|
|
|
|
return new WeakReference(target);
|
|
}
|
|
|
|
var weakTarget = SetupBinding();
|
|
|
|
CollectGarbage();
|
|
Assert.False(weakTarget.IsAlive);
|
|
}
|
|
|
|
[Fact]
|
|
public void CompiledBinding_To_AvaloniaProperty_With_Alive_Source_Does_Not_Keep_Target_Alive()
|
|
{
|
|
var source = new StyledElement { Name = "foo" };
|
|
|
|
WeakReference SetupBinding()
|
|
{
|
|
var path = new CompiledBindingPathBuilder()
|
|
.Property(StyledElement.NameProperty, PropertyInfoAccessorFactory.CreateAvaloniaPropertyAccessor)
|
|
.Build();
|
|
|
|
var target = new TextBlock();
|
|
|
|
target.Bind(TextBlock.TextProperty, new CompiledBindingExtension
|
|
{
|
|
Source = source,
|
|
Path = path
|
|
});
|
|
|
|
return new WeakReference(target);
|
|
}
|
|
|
|
var weakTarget = SetupBinding();
|
|
|
|
CollectGarbage();
|
|
Assert.False(weakTarget.IsAlive);
|
|
}
|
|
|
|
[Fact]
|
|
public void CompiledBinding_To_Method_With_Alive_Source_Does_Not_Keep_Target_Alive()
|
|
{
|
|
var source = new Class1();
|
|
|
|
WeakReference SetupBinding()
|
|
{
|
|
var path = new CompiledBindingPathBuilder()
|
|
.Command(
|
|
nameof(Class1.DoSomething),
|
|
(o, _) => ((Class1) o).DoSomething(),
|
|
(_, _) => true,
|
|
[])
|
|
.Build();
|
|
|
|
var target = new Button();
|
|
|
|
target.Bind(Button.CommandProperty, new CompiledBindingExtension
|
|
{
|
|
Source = source,
|
|
Path = path
|
|
});
|
|
|
|
return new WeakReference(target);
|
|
}
|
|
|
|
var weakTarget = SetupBinding();
|
|
|
|
CollectGarbage();
|
|
Assert.False(weakTarget.IsAlive);
|
|
}
|
|
|
|
[Fact]
|
|
public void Binding_To_AttachedProperty_With_Alive_Source_Does_Not_Keep_Target_Alive()
|
|
{
|
|
var source = new StyledElement { Name = "foo" };
|
|
|
|
WeakReference SetupBinding()
|
|
{
|
|
var target = new TextBlock();
|
|
|
|
target.Bind(TextBlock.TextProperty, new Binding
|
|
{
|
|
Source = source,
|
|
Path = "(Grid.Row)",
|
|
TypeResolver = (_, name) => name == "Grid" ? typeof(Grid) : throw new NotSupportedException()
|
|
});
|
|
|
|
return new WeakReference(target);
|
|
}
|
|
|
|
var weakTarget = SetupBinding();
|
|
|
|
CollectGarbage();
|
|
Assert.False(weakTarget.IsAlive);
|
|
}
|
|
|
|
private static void CollectGarbage()
|
|
{
|
|
GC.Collect();
|
|
// Forces WeakEvent compact
|
|
Dispatcher.UIThread.RunJobs();
|
|
GC.Collect();
|
|
}
|
|
|
|
private class Class1 : AvaloniaObject
|
|
{
|
|
public static readonly DirectProperty<Class1, string> FooProperty =
|
|
AvaloniaProperty.RegisterDirect<Class1, string>(
|
|
"Foo",
|
|
o => o.Foo,
|
|
(o, v) => o.Foo = v,
|
|
unsetValue: "unset");
|
|
|
|
private string _foo = "initial2";
|
|
|
|
static Class1()
|
|
{
|
|
}
|
|
|
|
public string Foo
|
|
{
|
|
get { return _foo; }
|
|
set { SetAndRaise(FooProperty, ref _foo, value); }
|
|
}
|
|
|
|
public void DoSomething()
|
|
{
|
|
}
|
|
}
|
|
|
|
private sealed class Class2 : INotifyPropertyChanged
|
|
{
|
|
private string? _foo;
|
|
|
|
public string? Foo
|
|
{
|
|
get => _foo;
|
|
set
|
|
{
|
|
if (_foo != value)
|
|
{
|
|
_foo = value;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
|
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
}
|
|
|