Browse Source

Sorter and InvalidationPropertyNames tests

pull/14729/head
Tom Edwards 2 years ago
committed by Tom Edwards
parent
commit
be2893b95a
  1. 6
      src/Avalonia.Controls/ItemsSourceViewLayer.cs
  2. 143
      tests/Avalonia.Controls.UnitTests/ItemsSourceViewTests.cs

6
src/Avalonia.Controls/ItemsSourceViewLayer.cs

@ -129,4 +129,10 @@ public abstract class ItemsSourceViewLayer : AvaloniaObject, INamed
public class InvalidationPropertiesCollection : AvaloniaList<string>
{
// Don't validate items: the PropertyChanged event can be raised with any "PropertyName" string.
public InvalidationPropertiesCollection() { }
public InvalidationPropertiesCollection(IEnumerable<string> names) : base(names) { }
public InvalidationPropertiesCollection(int capacity) : base(capacity) { }
}

143
tests/Avalonia.Controls.UnitTests/ItemsSourceViewTests.cs

@ -2,7 +2,8 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.ComponentModel;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Diagnostics;
using Avalonia.UnitTests;
@ -173,6 +174,146 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(NotifyCollectionChangedAction.Reset, collectionChangeEvents[0].Action);
}
[Fact]
public void ComparableSorter_Sorts_Integers()
{
var random = new Random();
var source = new AvaloniaList<int>(Enumerable.Repeat(0, 100).Select(i => random.Next(int.MinValue + 1, int.MaxValue)));
var target = ItemsSourceView.GetOrCreate(source);
var collectionChangeEvents = new List<NotifyCollectionChangedEventArgs>();
target.CollectionChanged += (s, e) => collectionChangeEvents.Add(e);
target.Sorters.Add(new ComparableSorter());
Assert.Equal(1, collectionChangeEvents.Count);
Assert.Equal(NotifyCollectionChangedAction.Reset, collectionChangeEvents[0].Action);
collectionChangeEvents.Clear();
Assert.Equal(target.Cast<int>().OrderBy(i => i), target);
source.Add(int.MinValue);
Assert.Equal(target[0], int.MinValue);
source.Insert(0, int.MaxValue);
Assert.Equal(target[target.Count - 1], int.MaxValue);
}
[Fact]
public void Disabled_Layers_Update_View_When_Activated()
{
var random = new Random();
var source = new AvaloniaList<int>(Enumerable.Repeat(0, 100));
var target = ItemsSourceView.GetOrCreate(source);
var collectionChangeEvents = new List<NotifyCollectionChangedEventArgs>();
target.CollectionChanged += (s, e) => collectionChangeEvents.Add(e);
var filter = new FunctionItemFilter { IsActive = false, Filter = (s, e) => e.Accept = (int)e.Item % 2 == 0 };
target.Filters.Add(filter);
var sorter = new ComparableSorter() { IsActive = false, SortDirection = ListSortDirection.Descending };
target.Sorters.Add(sorter);
Assert.Equal(0, collectionChangeEvents.Count);
Assert.Equal(source, target);
sorter.IsActive = true;
Assert.Equal(1, collectionChangeEvents.Count);
Assert.Equal(NotifyCollectionChangedAction.Reset, collectionChangeEvents[0].Action);
collectionChangeEvents.Clear();
Assert.Equal(source.Reverse(), target);
filter.IsActive = true;
Assert.Equal(1, collectionChangeEvents.Count);
Assert.Equal(NotifyCollectionChangedAction.Reset, collectionChangeEvents[0].Action);
collectionChangeEvents.Clear();
Assert.Equal(source.Reverse().Where((i, _) => i % 2 == 0), target);
sorter.IsActive = false;
Assert.Equal(1, collectionChangeEvents.Count);
Assert.Equal(NotifyCollectionChangedAction.Reset, collectionChangeEvents[0].Action);
collectionChangeEvents.Clear();
Assert.Equal(source.Where((i, _) => i % 2 == 0), target);
}
[Fact]
public void Layers_Refreshed_When_InvalidationProperty_Changes()
{
var source = new AvaloniaList<ViewModel>(Enumerable.Repeat(0, 5).Select(i => new ViewModel()));
var target = ItemsSourceView.GetOrCreate(source);
var collectionChangeEvents = new List<NotifyCollectionChangedEventArgs>();
target.CollectionChanged += (s, e) => collectionChangeEvents.Add(e);
target.Filters.Add(new FunctionItemFilter
{
Filter = (s, e) => e.Accept = ((ViewModel)e.Item).PassesFilter,
InvalidationPropertyNames = new() { nameof(ViewModel.PassesFilter) },
});
target.Sorters.Add(new ComparableSorter
{
ComparableSelector = (s, e) => e.Comparable = ((ViewModel)e.Item).LastModified,
InvalidationPropertyNames = new() { nameof(ViewModel.LastModified) },
});
foreach (var vm in source)
Assert.Equal(1, vm.PropertyChangedSubscriberCount); // One event subscription should be shared between all layers
Assert.Equal(2, collectionChangeEvents.Count);
Assert.Equal(NotifyCollectionChangedAction.Reset, collectionChangeEvents[0].Action);
Assert.Equal(NotifyCollectionChangedAction.Reset, collectionChangeEvents[1].Action);
collectionChangeEvents.Clear();
source[3].PassesFilter = true;
Assert.Equal(1, collectionChangeEvents.Count);
Assert.Equal(NotifyCollectionChangedAction.Add, collectionChangeEvents[0].Action);
Assert.Equal(0, collectionChangeEvents[0].NewStartingIndex);
Assert.Equal(new[] { source[3] }, collectionChangeEvents[0].NewItems);
Assert.Equal(source[3], target[0]);
collectionChangeEvents.Clear();
source[0].PassesFilter = true;
Assert.Equal(new[] { source[3], source[0] }, target); // source[0] comes last because it was modified more recently
}
private class ViewModel : INotifyPropertyChanged
{
private bool _passesFilter;
public bool PassesFilter
{
get => _passesFilter;
set
{
_passesFilter = value;
PropertyChanged?.Invoke(this, new(nameof(PassesFilter)));
LastModified = DateTimeOffset.Now;
PropertyChanged?.Invoke(this, new(nameof(LastModified)));
}
}
public DateTimeOffset? LastModified { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public int PropertyChangedSubscriberCount => PropertyChanged?.GetInvocationList().Length ?? 0;
}
private class InvalidCollection : INotifyCollectionChanged, IEnumerable<string>
{
public event NotifyCollectionChangedEventHandler CollectionChanged { add { } remove { } }

Loading…
Cancel
Save