A cross-platform UI framework for .NET
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.
 
 
 

599 lines
18 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Avalonia.Collections;
using Xunit;
namespace Avalonia.Base.UnitTests.Collections
{
public class AvaloniaListTests
{
[Fact]
public void Items_Passed_To_Constructor_Should_Appear_In_List()
{
var items = new[] { 1, 2, 3 };
var target = new AvaloniaList<int>(items);
Assert.Equal(items, target);
}
[Fact]
public void AddRange_With_Null_Should_Throw_Exception()
{
var target = new AvaloniaList<int>();
Assert.Throws<ArgumentNullException>(() => target.AddRange(null));
}
[Fact]
public void RemoveAll_With_Null_Should_Throw_Exception()
{
var target = new AvaloniaList<int>();
Assert.Throws<ArgumentNullException>(() => target.RemoveAll(null));
}
[Fact]
public void InsertRange_With_Null_Should_Throw_Exception()
{
var target = new AvaloniaList<int>();
Assert.Throws<ArgumentNullException>(() => target.InsertRange(1, null));
}
[Fact]
public void InsertRange_Past_End_Should_Throw_Exception()
{
var target = new AvaloniaList<int>();
Assert.Throws<ArgumentOutOfRangeException>(() => target.InsertRange(1, new List<int>() { 1 }));
}
[Fact]
public void Move_Should_Move_One_Item()
{
AvaloniaList<int> target = [1, 2, 3];
AssertEvent(target, () => target.Move(0, 1), [
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Move,
new[] { 1 },
1,
0)
]);
Assert.Equal(new[] { 2, 1, 3 }, target);
}
[Fact]
public void Move_Should_Update_Collection()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3 });
target.Move(2, 0);
Assert.Equal(new[] { 3, 1, 2 }, target);
}
[Fact]
public void MoveRange_Should_Update_Collection()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
target.MoveRange(4, 3, 0);
Assert.Equal(new[] { 5, 6, 7, 1, 2, 3, 4, 8, 9, 10 }, target);
}
[Fact]
public void MoveRange_Can_Move_To_End()
{
AvaloniaList<int> target = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
target.MoveRange(0, 5, 9);
Assert.Equal(new[] { 6, 7, 8, 9, 10, 1, 2, 3, 4, 5 }, target);
}
[Fact]
public void MoveRange_Raises_Correct_CollectionChanged_Event()
{
AvaloniaList<int> target = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
AssertEvent(target, () => target.MoveRange(0, 9, 9), [
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Move,
new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
9,
0)
]);
Assert.Equal(new[] { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, target);
}
[Fact]
public void MoveRange_Should_Move_One_Item()
{
AvaloniaList<int> target = [1, 2, 3];
AssertEvent(target, () => target.MoveRange(0, 1, 1), [
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Move,
new[] { 1 },
1,
0)
]);
Assert.Equal(new[] { 2, 1, 3 }, target);
}
[Fact]
public void Adding_Item_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Add, e.Action);
Assert.Equal(new[] { 3 }, e.NewItems.Cast<int>());
Assert.Equal(2, e.NewStartingIndex);
raised = true;
};
target.Add(3);
Assert.True(raised);
}
[Fact]
public void Adding_Items_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Add, e.Action);
Assert.Equal(new[] { 3, 4 }, e.NewItems.Cast<int>());
Assert.Equal(2, e.NewStartingIndex);
raised = true;
};
target.AddRange(new[] { 3, 4 });
Assert.True(raised);
}
[Fact]
public void AddRange_IEnumerable_Should_Raise_Count_PropertyChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3, 4, 5 });
var raised = false;
target.PropertyChanged += (s, e) => {
Assert.Equal(e.PropertyName, nameof(target.Count));
Assert.Equal(target.Count, 7);
raised = true;
};
target.AddRange(Enumerable.Range(6, 2));
Assert.True(raised);
}
[Fact]
public void AddRange_Items_Should_Raise_Correct_CollectionChanged()
{
var target = new AvaloniaList<object>();
var eventItems = new List<object>();
target.CollectionChanged += (sender, args) =>
{
eventItems.AddRange(args.NewItems.Cast<object>());
};
target.AddRange(Enumerable.Range(0,10).Select(i => new object()));
Assert.Equal(eventItems, target);
}
[Fact]
public void Replacing_Item_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Replace, e.Action);
Assert.Equal(new[] { 2 }, e.OldItems.Cast<int>());
Assert.Equal(new[] { 3 }, e.NewItems.Cast<int>());
Assert.Equal(1, e.OldStartingIndex);
Assert.Equal(1, e.NewStartingIndex);
raised = true;
};
target[1] = 3;
Assert.True(raised);
}
[Fact]
public void Inserting_Item_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Add, e.Action);
Assert.Equal(new[] { 3 }, e.NewItems.Cast<int>());
Assert.Equal(1, e.NewStartingIndex);
raised = true;
};
target.Insert(1, 3);
Assert.True(raised);
}
[Fact]
public void Inserting_Items_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Add, e.Action);
Assert.Equal(new[] { 3, 4 }, e.NewItems.Cast<int>());
Assert.Equal(1, e.NewStartingIndex);
raised = true;
};
target.InsertRange(1, new[] { 3, 4 });
Assert.True(raised);
}
[Fact]
public void Removing_Item_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Remove, e.Action);
Assert.Equal(new[] { 3 }, e.OldItems.Cast<int>());
Assert.Equal(2, e.OldStartingIndex);
raised = true;
};
target.Remove(3);
Assert.True(raised);
}
[Fact]
public void Moving_Item_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Move, e.Action);
Assert.Equal(new[] { 3 }, e.OldItems.Cast<int>());
Assert.Equal(new[] { 3 }, e.NewItems.Cast<int>());
Assert.Equal(2, e.OldStartingIndex);
Assert.Equal(0, e.NewStartingIndex);
raised = true;
};
target.Move(2, 0);
Assert.True(raised);
}
[Fact]
public void Moving_Items_Should_Raise_CollectionChanged()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Move, e.Action);
Assert.Equal(new[] { 2, 3 }, e.OldItems.Cast<int>());
Assert.Equal(new[] { 2, 3 }, e.NewItems.Cast<int>());
Assert.Equal(1, e.OldStartingIndex);
Assert.Equal(0, e.NewStartingIndex);
raised = true;
};
target.MoveRange(1, 2, 0);
Assert.True(raised);
}
[Fact]
public void Clearing_Items_Should_Raise_CollectionChanged_Reset()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Reset, e.Action);
raised = true;
};
target.Clear();
Assert.True(raised);
}
[Fact]
public void Clearing_Items_Should_Raise_CollectionChanged_Remove()
{
var target = new AvaloniaList<int>(new[] { 1, 2, 3 });
var raised = false;
target.ResetBehavior = ResetBehavior.Remove;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Remove, e.Action);
Assert.Equal(new[] { 1, 2, 3 }, e.OldItems.Cast<int>());
Assert.Equal(0, e.OldStartingIndex);
raised = true;
};
target.Clear();
Assert.True(raised);
}
[Fact]
public void Can_CopyTo_Array_Of_Same_Type()
{
var target = new AvaloniaList<string> { "foo", "bar", "baz" };
var result = new string[3];
target.CopyTo(result, 0);
Assert.Equal(target, result);
}
[Fact]
public void Can_CopyTo_Array_Of_Base_Type()
{
var target = new AvaloniaList<string> { "foo", "bar", "baz" };
var result = new object[3];
((IList)target).CopyTo(result, 0);
Assert.Equal(target, result);
}
[Fact]
public void RemoveAll_Should_Send_Single_Notification_For_Sequential_Range()
{
var target = new AvaloniaList<string>(Enumerable.Range(0, 10).Select(x => $"Item {x}"));
var toRemove = new[] { "Item 5", "Item 6", "Item 7" };
var raised = 0;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(NotifyCollectionChangedAction.Remove, e.Action);
Assert.Equal(5, e.OldStartingIndex);
Assert.Equal(toRemove, e.OldItems);
++raised;
};
target.RemoveAll(toRemove);
Assert.Equal(1, raised);
}
[Fact]
public void RemoveAll_Should_Send_Single_Notification_For_Sequential_Range_With_Duplicate_Source_Items()
{
var items = Enumerable.Range(0, 20).Select(x => $"Item {x / 2}");
var target = new AvaloniaList<string>(items);
var toRemove = new[] { "Item 5", "Item 6", "Item 7" };
var raised = 0;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(NotifyCollectionChangedAction.Remove, e.Action);
Assert.Equal(10, e.OldStartingIndex);
Assert.Equal(new[]
{
"Item 5",
"Item 5",
"Item 6",
"Item 6",
"Item 7",
"Item 7",
}, e.OldItems);
++raised;
};
target.RemoveAll(toRemove);
Assert.Equal(1, raised);
}
[Fact]
public void RemoveAll_Should_Send_Multiple_Notifications_For_Non_Sequential_Range()
{
var target = new AvaloniaList<string>(Enumerable.Range(0, 10).Select(x => $"Item {x}"));
var raised = 0;
var toRemove = new[]
{
new[] { "Item 2", "Item 3" },
new[] { "Item 5", "Item 6" }
};
target.CollectionChanged += (s, e) =>
{
Assert.Equal(NotifyCollectionChangedAction.Remove, e.Action);
if (raised == 0)
{
Assert.Equal(5, e.OldStartingIndex);
Assert.Equal(toRemove[1], e.OldItems);
}
else
{
Assert.Equal(2, e.OldStartingIndex);
Assert.Equal(toRemove[0], e.OldItems);
}
++raised;
};
target.RemoveAll(toRemove[0].Concat(toRemove[1]));
Assert.Equal(2, raised);
}
[Fact]
public void RemoveAll_Should_Send_Multiple_Notifications_For_Sequential_Range_With_Nonsequential_Duplicate_Source_Items()
{
var items = Enumerable.Range(0, 10).Select(x => $"Item {x}");
var target = new AvaloniaList<string>(items.Concat(items));
var raised = 0;
var toRemove = new[] { "Item 5", "Item 6", "Item 7" };
target.CollectionChanged += (s, e) =>
{
Assert.Equal(NotifyCollectionChangedAction.Remove, e.Action);
if (raised == 0)
{
Assert.Equal(15, e.OldStartingIndex);
Assert.Equal(toRemove, e.OldItems);
}
else
{
Assert.Equal(5, e.OldStartingIndex);
Assert.Equal(toRemove, e.OldItems);
}
++raised;
};
target.RemoveAll(toRemove);
Assert.Equal(2, raised);
}
[Fact]
public void RemoveAll_Should_Not_Send_Notification_For_Items_Not_Present()
{
var target = new AvaloniaList<string>(Enumerable.Range(0, 10).Select(x => $"Item {x}"));
var toRemove = new[] { "Item 5", "Item 6", "Item 7", "Not present" };
var raised = 0;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(NotifyCollectionChangedAction.Remove, e.Action);
Assert.Equal(5, e.OldStartingIndex);
Assert.Equal(toRemove.Take(3).ToArray(), e.OldItems);
++raised;
};
target.RemoveAll(toRemove);
Assert.Equal(1, raised);
}
[Fact]
public void RemoveAll_Should_Handle_Empty_List()
{
var target = new AvaloniaList<string>();
var toRemove = new[] { "Item 5", "Item 6", "Item 7" };
var raised = 0;
target.CollectionChanged += (s, e) =>
{
++raised;
};
target.RemoveAll(toRemove);
Assert.Equal(0, raised);
}
/// <summary>
/// Assert that <paramref name="items"/> emits <paramref name="expectedEvents"/> when performing <paramref name="action"/>.
/// </summary>
/// <param name="items">The event source.</param>
/// <param name="action">The action to perform.</param>
/// <param name="expectedEvents">The expected events.</param>
private static void AssertEvent(INotifyCollectionChanged items, Action action, NotifyCollectionChangedEventArgs[] expectedEvents)
{
var callCount = 0;
items.CollectionChanged += OnCollectionChanged;
Assert.Multiple(() =>
{
try
{
action();
}
finally
{
items.CollectionChanged -= OnCollectionChanged;
}
Assert.Equal(expectedEvents.Length, callCount);
});
return;
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs actualEvent)
{
Assert.Multiple(() =>
{
Assert.True(callCount < expectedEvents.Length);
Assert.Equal(expectedEvents[callCount].Action, actualEvent.Action);
Assert.Equal(expectedEvents[callCount].NewItems, actualEvent.NewItems);
Assert.Equal(expectedEvents[callCount].NewStartingIndex, actualEvent.NewStartingIndex);
Assert.Equal(expectedEvents[callCount].OldItems, actualEvent.OldItems);
Assert.Equal(expectedEvents[callCount].OldStartingIndex, actualEvent.OldStartingIndex);
});
++callCount;
}
}
}
}