mirror of
https://github.com/chylex/.NET-Community-Toolkit.git
synced 2025-04-10 11:15:45 +02:00
Merge pull request #169 from CommunityToolkit/dev/improve-collections
Revamp observable collection APIs
This commit is contained in:
commit
f3bd3d8b72
CommunityToolkit.Mvvm/Collections
IReadOnlyObservableGroup.csIReadOnlyObservableGroup{TKey,TElement}.csIReadOnlyObservableGroup{TKey}.cs
Internals
ObservableGroupedCollectionExtensions.csObservableGroupedCollection{TKey,TElement}.csObservableGroup{TKey,TElement}.csReadOnlyObservableGroupedCollection{TKey,TElement}.csReadOnlyObservableGroupedCollection{TKey,TValue}.csReadOnlyObservableGroup{TKey,TElement}.cstests
CommunityToolkit.Common.UnitTests/Extensions
CommunityToolkit.Mvvm.UnitTests/Collections
IntGroup.csObservableGroupTests.csObservableGroupedCollectionExtensionsTests.csObservableGroupedCollectionTests.csReadOnlyObservableGroupTests.csTest_ObservableGroup.csTest_ObservableGroupedCollection.csTest_ObservableGroupedCollectionExtensions.csTest_ReadOnlyObservableGroup.csTest_ReadOnlyObservableGroupedCollection.cs
@ -2,6 +2,8 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections;
|
||||
@ -9,7 +11,7 @@ namespace CommunityToolkit.Mvvm.Collections;
|
||||
/// <summary>
|
||||
/// An interface for a grouped collection of items.
|
||||
/// </summary>
|
||||
public interface IReadOnlyObservableGroup : INotifyPropertyChanged
|
||||
public interface IReadOnlyObservableGroup : INotifyPropertyChanged, INotifyCollectionChanged, IEnumerable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the key for the current collection.
|
||||
@ -20,4 +22,12 @@ public interface IReadOnlyObservableGroup : INotifyPropertyChanged
|
||||
/// Gets the number of items currently in the grouped collection.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the specified index in the current collection.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the element to get.</param>
|
||||
/// <returns>The element at the specified index in the read-only list.</returns>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">Thrown if the index is out of range.</exception>
|
||||
object? this[int index] { get; }
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// An interface for a grouped collection of items.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the group key.</typeparam>
|
||||
/// <typeparam name="TElement">The type of elements in the group.</typeparam>
|
||||
public interface IReadOnlyObservableGroup<out TKey, out TElement> : IReadOnlyObservableGroup<TKey>, IReadOnlyList<TElement>, IGrouping<TKey, TElement>
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the element at the specified index in the current collection.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the element to get.</param>
|
||||
/// <returns>The element at the specified index in the read-only list.</returns>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">Thrown if the index is out of range.</exception>
|
||||
new TElement this[int index] { get; }
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// An interface for a grouped collection of items.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the group key.</typeparam>
|
||||
public interface IReadOnlyObservableGroup<out TKey> : IReadOnlyObservableGroup
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the key for the current collection.
|
||||
/// </summary>
|
||||
new TKey Key { get; }
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections.Internals;
|
||||
|
||||
/// <summary>
|
||||
/// A helper type for the <see cref="ObservableGroup{TKey, TValue}"/> type.
|
||||
/// </summary>
|
||||
internal static class ObservableGroupHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// The cached <see cref="PropertyChangedEventArgs"/> for <see cref="IReadOnlyObservableGroup.Key"/>
|
||||
/// </summary>
|
||||
public static readonly PropertyChangedEventArgs KeyChangedEventArgs = new(nameof(IReadOnlyObservableGroup.Key));
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -13,9 +13,9 @@ namespace CommunityToolkit.Mvvm.Collections;
|
||||
/// <summary>
|
||||
/// An observable list of observable groups.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the group key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the items in the collection.</typeparam>
|
||||
public sealed class ObservableGroupedCollection<TKey, TValue> : ObservableCollection<ObservableGroup<TKey, TValue>>
|
||||
/// <typeparam name="TKey">The type of the group keys.</typeparam>
|
||||
/// <typeparam name="TElement">The type of elements in the collection.</typeparam>
|
||||
public sealed class ObservableGroupedCollection<TKey, TElement> : ObservableCollection<ObservableGroup<TKey, TElement>>, ILookup<TKey, TElement>
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
@ -29,21 +29,50 @@ public ObservableGroupedCollection()
|
||||
/// Initializes a new instance of the <see cref="ObservableGroupedCollection{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The initial data to add in the grouped collection.</param>
|
||||
public ObservableGroupedCollection(IEnumerable<IGrouping<TKey, TValue>> collection)
|
||||
: base(collection.Select(static c => new ObservableGroup<TKey, TValue>(c)))
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="collection"/> is <see langword="null"/>.</exception>
|
||||
public ObservableGroupedCollection(IEnumerable<IGrouping<TKey, TElement>> collection)
|
||||
: base(collection?.Select(static group => new ObservableGroup<TKey, TElement>(group))!)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerable<TElement> ILookup<TKey, TElement>.this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
IEnumerable<TElement>? result = null;
|
||||
|
||||
if (key is not null)
|
||||
{
|
||||
result = this.FirstGroupByKeyOrDefault(key);
|
||||
}
|
||||
|
||||
return result ?? Enumerable.Empty<TElement>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the underlying <see cref="List{T}"/> instance, if present.
|
||||
/// </summary>
|
||||
/// <param name="list">The resulting <see cref="List{T}"/>, if one was in use.</param>
|
||||
/// <returns>Whether or not a <see cref="List{T}"/> instance has been found.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal bool TryGetList([NotNullWhen(true)] out List<ObservableGroup<TKey, TValue>>? list)
|
||||
internal bool TryGetList([NotNullWhen(true)] out List<ObservableGroup<TKey, TElement>>? list)
|
||||
{
|
||||
list = Items as List<ObservableGroup<TKey, TValue>>;
|
||||
list = Items as List<ObservableGroup<TKey, TElement>>;
|
||||
|
||||
return list is not null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool ILookup<TKey, TElement>.Contains(TKey key)
|
||||
{
|
||||
return key is not null && this.FirstGroupByKey(key) is not null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator<IGrouping<TKey, TElement>> IEnumerable<IGrouping<TKey, TElement>>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
@ -4,9 +4,11 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CommunityToolkit.Mvvm.Collections.Internals;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections;
|
||||
|
||||
@ -15,22 +17,20 @@ namespace CommunityToolkit.Mvvm.Collections;
|
||||
/// It associates a <see cref="Key"/> to an <see cref="ObservableCollection{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the group key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the items in the collection.</typeparam>
|
||||
/// <typeparam name="TElement">The type of elements in the group.</typeparam>
|
||||
[DebuggerDisplay("Key = {Key}, Count = {Count}")]
|
||||
public class ObservableGroup<TKey, TValue> : ObservableCollection<TValue>, IGrouping<TKey, TValue>, IReadOnlyObservableGroup
|
||||
public sealed class ObservableGroup<TKey, TElement> : ObservableCollection<TElement>, IReadOnlyObservableGroup<TKey, TElement>
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// The cached <see cref="PropertyChangedEventArgs"/> for <see cref="Key"/>
|
||||
/// </summary>
|
||||
private static readonly PropertyChangedEventArgs KeyChangedEventArgs = new(nameof(Key));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ObservableGroup{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The key for the group.</param>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="key"/> is <see langword="null"/>.</exception>
|
||||
public ObservableGroup(TKey key)
|
||||
{
|
||||
ArgumentNullException.For<TKey>.ThrowIfNull(key);
|
||||
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@ -38,7 +38,8 @@ public ObservableGroup(TKey key)
|
||||
/// Initializes a new instance of the <see cref="ObservableGroup{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="grouping">The grouping to fill the group.</param>
|
||||
public ObservableGroup(IGrouping<TKey, TValue> grouping)
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="grouping"/> is <see langword="null"/>.</exception>
|
||||
public ObservableGroup(IGrouping<TKey, TElement> grouping)
|
||||
: base(grouping)
|
||||
{
|
||||
this.key = grouping.Key;
|
||||
@ -49,9 +50,12 @@ public ObservableGroup(IGrouping<TKey, TValue> grouping)
|
||||
/// </summary>
|
||||
/// <param name="key">The key for the group.</param>
|
||||
/// <param name="collection">The initial collection of data to add to the group.</param>
|
||||
public ObservableGroup(TKey key, IEnumerable<TValue> collection)
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="key"/> or <paramref name="collection"/> are <see langword="null"/>.</exception>
|
||||
public ObservableGroup(TKey key, IEnumerable<TElement> collection)
|
||||
: base(collection)
|
||||
{
|
||||
ArgumentNullException.For<TKey>.ThrowIfNull(key);
|
||||
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@ -60,20 +64,39 @@ public ObservableGroup(TKey key, IEnumerable<TValue> collection)
|
||||
/// <summary>
|
||||
/// Gets or sets the key of the group.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="value"/> is <see langword="null"/>.</exception>
|
||||
public TKey Key
|
||||
{
|
||||
get => this.key;
|
||||
set
|
||||
{
|
||||
ArgumentNullException.For<TKey>.ThrowIfNull(value);
|
||||
|
||||
if (!EqualityComparer<TKey>.Default.Equals(this.key!, value))
|
||||
{
|
||||
this.key = value;
|
||||
|
||||
OnPropertyChanged(KeyChangedEventArgs);
|
||||
OnPropertyChanged(ObservableGroupHelper.KeyChangedEventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the underlying <see cref="List{T}"/> instance, if present.
|
||||
/// </summary>
|
||||
/// <param name="list">The resulting <see cref="List{T}"/>, if one was in use.</param>
|
||||
/// <returns>Whether or not a <see cref="List{T}"/> instance has been found.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal bool TryGetList([NotNullWhen(true)] out List<TElement>? list)
|
||||
{
|
||||
list = Items as List<TElement>;
|
||||
|
||||
return list is not null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
object IReadOnlyObservableGroup.Key => Key;
|
||||
|
||||
/// <inheritdoc/>
|
||||
object? IReadOnlyObservableGroup.this[int index] => this[index];
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// A read-only list of groups.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the group keys.</typeparam>
|
||||
/// <typeparam name="TElement">The type of elements in the collection.</typeparam>
|
||||
public sealed class ReadOnlyObservableGroupedCollection<TKey, TElement> : ReadOnlyObservableCollection<ReadOnlyObservableGroup<TKey, TElement>>, ILookup<TKey, TElement>
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyObservableGroupedCollection{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The source collection to wrap.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="collection"/> is <see langword="null"/>.</exception>
|
||||
public ReadOnlyObservableGroupedCollection(ObservableCollection<ObservableGroup<TKey, TElement>> collection)
|
||||
: base(new ObservableCollection<ReadOnlyObservableGroup<TKey, TElement>>(collection?.Select(static g => new ReadOnlyObservableGroup<TKey, TElement>(g))!))
|
||||
{
|
||||
collection!.CollectionChanged += OnSourceCollectionChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyObservableGroupedCollection{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The source collection to wrap.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="collection"/> is <see langword="null"/>.</exception>
|
||||
public ReadOnlyObservableGroupedCollection(ObservableCollection<ReadOnlyObservableGroup<TKey, TElement>> collection)
|
||||
: base(collection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerable<TElement> ILookup<TKey, TElement>.this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
IEnumerable<TElement>? result = null;
|
||||
|
||||
if (key is not null)
|
||||
{
|
||||
result = FirstGroupByKeyOrDefault(key);
|
||||
}
|
||||
|
||||
return result ?? Enumerable.Empty<TElement>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool ILookup<TKey, TElement>.Contains(TKey key)
|
||||
{
|
||||
return key is not null && FirstGroupByKeyOrDefault(key) is not null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator<IGrouping<TKey, TElement>> IEnumerable<IGrouping<TKey, TElement>>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forwards the <see cref="INotifyCollectionChanged.CollectionChanged"/> event whenever it is raised by the wrapped collection.
|
||||
/// </summary>
|
||||
/// <param name="sender">The wrapped collection (an <see cref="ObservableCollection{T}"/> of <see cref="ReadOnlyObservableGroup{TKey, TValue}"/> instance).</param>
|
||||
/// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> arguments.</param>
|
||||
/// <exception cref="NotSupportedException">Thrown if a range operation is requested.</exception>
|
||||
private void OnSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
// Even if NotifyCollectionChangedEventArgs allows multiple items, the actual implementation
|
||||
// is only reporting the changes one by one. We consider only this case for now. If this is
|
||||
// added in a new version of .NET, this type will need to be updated accordingly in a new version.
|
||||
[DoesNotReturn]
|
||||
static void ThrowNotSupportedExceptionForRangeOperation()
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"ReadOnlyObservableGroupedCollection<TKey, TValue> doesn't support operations on multiple items at once.\n" +
|
||||
"If this exception was thrown, it likely means support for batched item updates has been added to the " +
|
||||
"underlying ObservableCollection<T> type, and this implementation doesn't support that feature yet.\n" +
|
||||
"Please consider opening an issue in https://aka.ms/toolkit/dotnet to report this.");
|
||||
}
|
||||
|
||||
// The inner Items list is ObservableCollection<ReadOnlyObservableGroup<TKey, TValue>>, so doing a direct cast here will always succeed
|
||||
ObservableCollection<ReadOnlyObservableGroup<TKey, TElement>> items = (ObservableCollection<ReadOnlyObservableGroup<TKey, TElement>>)Items;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
// Insert a single item for an "Add" operation, fail if multiple items are added
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
if (e.NewItems!.Count == 1)
|
||||
{
|
||||
ObservableGroup<TKey, TElement> newItem = (ObservableGroup<TKey, TElement>)e.NewItems![0]!;
|
||||
|
||||
items.Insert(e.NewStartingIndex, new ReadOnlyObservableGroup<TKey, TElement>(newItem));
|
||||
}
|
||||
else if (e.NewItems!.Count > 1)
|
||||
{
|
||||
ThrowNotSupportedExceptionForRangeOperation();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Remove a single item at offset for a "Remove" operation, fail if multiple items are removed
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
if (e.OldItems!.Count == 1)
|
||||
{
|
||||
items.RemoveAt(e.OldStartingIndex);
|
||||
}
|
||||
else if (e.OldItems!.Count > 1)
|
||||
{
|
||||
ThrowNotSupportedExceptionForRangeOperation();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Replace a single item at offset for a "Replace" operation, fail if multiple items are replaced
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
if (e.OldItems!.Count == 1 && e.NewItems!.Count == 1)
|
||||
{
|
||||
ObservableGroup<TKey, TElement> replacedItem = (ObservableGroup<TKey, TElement>)e.NewItems![0]!;
|
||||
|
||||
items[e.OldStartingIndex] = new ReadOnlyObservableGroup<TKey, TElement>(replacedItem);
|
||||
}
|
||||
else if (e.OldItems!.Count > 1 || e.NewItems!.Count > 1)
|
||||
{
|
||||
ThrowNotSupportedExceptionForRangeOperation();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Move a single item between offsets for a "Move" operation, fail if multiple items are moved
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
if (e.OldItems!.Count == 1 && e.NewItems!.Count == 1)
|
||||
{
|
||||
items.Move(e.OldStartingIndex, e.NewStartingIndex);
|
||||
}
|
||||
else if (e.OldItems!.Count > 1 || e.NewItems!.Count > 1)
|
||||
{
|
||||
ThrowNotSupportedExceptionForRangeOperation();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// A "Reset" operation is just forwarded normally
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
items.Clear();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first group with <paramref name="key"/> key or <see langword="null"/> if not found.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the group to query (assumed not to be <see langword="null"/>).</param>
|
||||
/// <returns>The first group matching <paramref name="key"/>.</returns>
|
||||
private IEnumerable<TElement>? FirstGroupByKeyOrDefault(TKey key)
|
||||
{
|
||||
if (Items is List<ReadOnlyObservableGroup<TKey, TElement>> list)
|
||||
{
|
||||
foreach (ReadOnlyObservableGroup<TKey, TElement> group in list)
|
||||
{
|
||||
if (EqualityComparer<TKey>.Default.Equals(group.Key, key))
|
||||
{
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static IEnumerable<TElement>? FirstGroupByKeyOrDefaultFallback(ReadOnlyObservableGroupedCollection<TKey, TElement> source, TKey key)
|
||||
{
|
||||
return Enumerable.FirstOrDefault<ReadOnlyObservableGroup<TKey, TElement>>(source, group => EqualityComparer<TKey>.Default.Equals(group.Key, key));
|
||||
}
|
||||
|
||||
return FirstGroupByKeyOrDefaultFallback(this, key);
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// A read-only list of groups.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the group key.</typeparam>
|
||||
/// <typeparam name = "TValue" > The type of the items in the collection.</typeparam>
|
||||
public sealed class ReadOnlyObservableGroupedCollection<TKey, TValue> : ReadOnlyObservableCollection<ReadOnlyObservableGroup<TKey, TValue>>
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyObservableGroupedCollection{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The source collection to wrap.</param>
|
||||
public ReadOnlyObservableGroupedCollection(ObservableGroupedCollection<TKey, TValue> collection)
|
||||
: this(collection.Select(static g => new ReadOnlyObservableGroup<TKey, TValue>(g)))
|
||||
{
|
||||
((INotifyCollectionChanged)collection).CollectionChanged += OnSourceCollectionChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyObservableGroupedCollection{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The initial data to add in the grouped collection.</param>
|
||||
public ReadOnlyObservableGroupedCollection(IEnumerable<ReadOnlyObservableGroup<TKey, TValue>> collection)
|
||||
: base(new ObservableCollection<ReadOnlyObservableGroup<TKey, TValue>>(collection))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyObservableGroupedCollection{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The initial data to add in the grouped collection.</param>
|
||||
public ReadOnlyObservableGroupedCollection(IEnumerable<IGrouping<TKey, TValue>> collection)
|
||||
: this(collection.Select(static g => new ReadOnlyObservableGroup<TKey, TValue>(g.Key, g)))
|
||||
{
|
||||
}
|
||||
|
||||
private void OnSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
// Even if NotifyCollectionChangedEventArgs allows multiple items, the actual implementation
|
||||
// is only reporting the changes one by one. We consider only this case for now.
|
||||
if (e.OldItems?.Count > 1 || e.NewItems?.Count > 1)
|
||||
{
|
||||
static void ThrowNotSupportedException()
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"ReadOnlyObservableGroupedCollection<TKey, TValue> doesn't support operations on multiple items at once.\n" +
|
||||
"If this exception was thrown, it likely means support for batched item updates has been added to the " +
|
||||
"underlying ObservableCollection<T> type, and this implementation doesn't support that feature yet.\n" +
|
||||
"Please consider opening an issue in https://aka.ms/windowstoolkit to report this.");
|
||||
}
|
||||
|
||||
ThrowNotSupportedException();
|
||||
}
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add or NotifyCollectionChangedAction.Replace:
|
||||
|
||||
// We only need to find the new item if the operation is either add or remove. In this
|
||||
// case we just directly find the first item that was modified, or throw if it's not present.
|
||||
// This normally never happens anyway - add and replace should always have a target element.
|
||||
ObservableGroup<TKey, TValue> newItem = e.NewItems!.Cast<ObservableGroup<TKey, TValue>>().First();
|
||||
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
Items.Insert(e.NewStartingIndex, new ReadOnlyObservableGroup<TKey, TValue>(newItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
Items[e.OldStartingIndex] = new ReadOnlyObservableGroup<TKey, TValue>(newItem);
|
||||
}
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
|
||||
// Our inner Items list is our own ObservableCollection<ReadOnlyObservableGroup<TKey, TValue>> so we can safely cast Items to its concrete type here.
|
||||
((ObservableCollection<ReadOnlyObservableGroup<TKey, TValue>>)Items).Move(e.OldStartingIndex, e.NewStartingIndex);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
Items.RemoveAt(e.OldStartingIndex);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
Items.Clear();
|
||||
break;
|
||||
default:
|
||||
Debug.Fail("unsupported value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,8 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.Collections;
|
||||
|
||||
@ -12,8 +11,9 @@ namespace CommunityToolkit.Mvvm.Collections;
|
||||
/// A read-only observable group. It associates a <see cref="Key"/> to a <see cref="ReadOnlyObservableCollection{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the group key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the items in the collection.</typeparam>
|
||||
public sealed class ReadOnlyObservableGroup<TKey, TValue> : ReadOnlyObservableCollection<TValue>, IGrouping<TKey, TValue>, IReadOnlyObservableGroup
|
||||
/// <typeparam name="TElement">The type of elements in the group.</typeparam>
|
||||
[DebuggerDisplay("Key = {Key}, Count = {Count}")]
|
||||
public sealed class ReadOnlyObservableGroup<TKey, TElement> : ReadOnlyObservableCollection<TElement>, IReadOnlyObservableGroup<TKey, TElement>
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
@ -21,9 +21,12 @@ public sealed class ReadOnlyObservableGroup<TKey, TValue> : ReadOnlyObservableCo
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the group.</param>
|
||||
/// <param name="collection">The collection of items to add in the group.</param>
|
||||
public ReadOnlyObservableGroup(TKey key, ObservableCollection<TValue> collection)
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="key"/> or <paramref name="collection"/> are <see langword="null"/>.</exception>
|
||||
public ReadOnlyObservableGroup(TKey key, ObservableCollection<TElement> collection)
|
||||
: base(collection)
|
||||
{
|
||||
ArgumentNullException.For<TKey>.ThrowIfNull(key);
|
||||
|
||||
Key = key;
|
||||
}
|
||||
|
||||
@ -31,26 +34,19 @@ public ReadOnlyObservableGroup(TKey key, ObservableCollection<TValue> collection
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyObservableGroup{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="group">The <see cref="ObservableGroup{TKey, TValue}"/> to wrap.</param>
|
||||
public ReadOnlyObservableGroup(ObservableGroup<TKey, TValue> group)
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="group"/> is <see langword="null"/>.</exception>
|
||||
public ReadOnlyObservableGroup(ObservableGroup<TKey, TElement> group)
|
||||
: base(group)
|
||||
{
|
||||
Key = group.Key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyObservableGroup{TKey, TValue}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the group.</param>
|
||||
/// <param name="collection">The collection of items to add in the group.</param>
|
||||
public ReadOnlyObservableGroup(TKey key, IEnumerable<TValue> collection)
|
||||
: base(new ObservableCollection<TValue>(collection))
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TKey Key { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
object IReadOnlyObservableGroup.Key => Key;
|
||||
|
||||
/// <inheritdoc/>
|
||||
object? IReadOnlyObservableGroup.this[int index] => this[index];
|
||||
}
|
@ -80,7 +80,7 @@ public void Test_EventHandlerExtensions_UsingDeferralCausesAwait()
|
||||
Assert.IsTrue(handlersTask.IsCompleted);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[TestMethod]
|
||||
[DataRow(0, 1)]
|
||||
[DataRow(1, 0)]
|
||||
public void Test_EventHandlerExtensions_MultipleHandlersCauseAwait(int firstToReleaseDeferral, int lastToReleaseDeferral)
|
||||
|
@ -7,13 +7,22 @@
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests;
|
||||
|
||||
public class IntGroup : List<int>, IGrouping<string, int>
|
||||
/// <summary>
|
||||
/// A simple <see cref="IGrouping{TKey, TElement}"/> implementation for <see cref="string"/> and <see cref="int"/> values.
|
||||
/// </summary>
|
||||
internal sealed class IntGroup : List<int>, IGrouping<string, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IntGroup"/> instance with the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="key">The group key.</param>
|
||||
/// <param name="collection">The group values.</param>
|
||||
public IntGroup(string key, IEnumerable<int> collection)
|
||||
: base(collection)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Key { get; }
|
||||
}
|
||||
|
@ -1,115 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests;
|
||||
|
||||
[TestClass]
|
||||
public class ObservableGroupTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Ctor_ShouldHaveExpectedState()
|
||||
{
|
||||
ObservableGroup<string, int>? group = new("key");
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
Assert.AreEqual(group.Count, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_WithGrouping_ShouldHaveExpectedState()
|
||||
{
|
||||
IntGroup? source = new("key", new[] { 1, 2, 3 });
|
||||
ObservableGroup<string, int>? group = new(source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_WithCollection_ShouldHaveExpectedState()
|
||||
{
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? group = new("key", source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Add_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? group = new("key", source);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
group.Add(4);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3, 4 });
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Update_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? group = new("key", source);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
group[1] = 4;
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 4, 3 });
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Remove_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? group = new("key", source);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
_ = group.Remove(1);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 2, 3 });
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Clear_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? group = new("key", source);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
group.Clear();
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
Assert.AreEqual(group.Count, 0);
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(0)]
|
||||
[DataRow(3)]
|
||||
public void IReadOnlyObservableGroup_ShouldReturnExpectedValues(int count)
|
||||
{
|
||||
ObservableGroup<string, int>? group = new("key", Enumerable.Range(0, count));
|
||||
IReadOnlyObservableGroup? iReadOnlyObservableGroup = (IReadOnlyObservableGroup)group;
|
||||
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Key, "key");
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Count, count);
|
||||
}
|
||||
}
|
@ -1,472 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests;
|
||||
|
||||
[TestClass]
|
||||
public class ObservableGroupedCollectionExtensionsTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void First_WhenGroupExists_ShouldReturnFirstGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
ObservableGroup<string, int>? target = groupedCollection.AddGroup("B", 10);
|
||||
_ = groupedCollection.AddGroup("B", 42);
|
||||
|
||||
ObservableGroup<string, int>? result = groupedCollection.First("B");
|
||||
|
||||
Assert.AreSame(result, target);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void First_WhenGroupDoesNotExist_ShouldThrow()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
|
||||
_ = groupedCollection.First("I do not exist");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FirstOrDefault_WhenGroupExists_ShouldReturnFirstGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
ObservableGroup<string, int>? target = groupedCollection.AddGroup("B", 10);
|
||||
_ = groupedCollection.AddGroup("B", 42);
|
||||
|
||||
ObservableGroup<string, int>? result = groupedCollection.FirstOrDefault("B");
|
||||
|
||||
Assert.AreSame(result, target);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FirstOrDefault_WhenGroupDoesNotExist_ShouldReturnNull()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
|
||||
ObservableGroup<string, int>? result = groupedCollection.FirstOrDefault("I do not exist");
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ElementAt_WhenGroupExistsAndIndexInRange_ShouldReturnFirstGroupValue()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
_ = groupedCollection.AddGroup("B", 10, 11, 12);
|
||||
_ = groupedCollection.AddGroup("B", 42);
|
||||
|
||||
int result = groupedCollection.ElementAt("B", 2);
|
||||
|
||||
Assert.AreEqual(result, 12);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(-1)]
|
||||
[DataRow(3)]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ElementAt_WhenGroupExistsAndIndexOutOfRange_ShouldReturnThrow(int index)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
_ = groupedCollection.AddGroup("B", 10, 11, 12);
|
||||
_ = groupedCollection.AddGroup("B", 42);
|
||||
|
||||
_ = groupedCollection.ElementAt("B", index);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void ElementAt_WhenGroupDoesNotExist_ShouldThrow()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
|
||||
_ = groupedCollection.ElementAt("I do not exist", 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ElementAtOrDefault_WhenGroupExistsAndIndexInRange_ShouldReturnValue()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
_ = groupedCollection.AddGroup("B", 10, 11, 12);
|
||||
_ = groupedCollection.AddGroup("B", 42);
|
||||
|
||||
int result = groupedCollection.ElementAt("B", 2);
|
||||
|
||||
Assert.AreEqual(result, 12);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(-1)]
|
||||
[DataRow(3)]
|
||||
public void ElementAtOrDefault_WhenGroupExistsAndIndexOutOfRange_ShouldReturnDefaultValue(int index)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
_ = groupedCollection.AddGroup("B", 10, 11, 12);
|
||||
_ = groupedCollection.AddGroup("B", 42);
|
||||
|
||||
int result = groupedCollection.ElementAtOrDefault("B", index);
|
||||
|
||||
Assert.AreEqual(result, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ElementAtOrDefault_WhenGroupDoesNotExist_ShouldReturnDefaultValue()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 23);
|
||||
|
||||
int result = groupedCollection.ElementAtOrDefault("I do not exist", 0);
|
||||
|
||||
Assert.AreEqual(result, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddGroup_WithItem_ShouldAddGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int>? addedGroup = groupedCollection.AddGroup("new key", 23);
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddGroup_WithCollection_ShouldAddGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int>? addedGroup = groupedCollection.AddGroup("new key", new[] { 23, 10, 42 });
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23, 10, 42 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddGroup_WithParamsCollection_ShouldAddGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int>? addedGroup = groupedCollection.AddGroup("new key", 23, 10, 42);
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23, 10, 42 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddItem_WhenTargetGroupDoesNotExists_ShouldCreateAndAddNewGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int>? addedGroup = groupedCollection.AddItem("new key", 23);
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddItem_WhenSingleTargetGroupAlreadyExists_ShouldAddItemToExistingGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
ObservableGroup<string, int>? targetGroup = groupedCollection.AddGroup("B", 4, 5, 6);
|
||||
_ = groupedCollection.AddGroup("C", 7, 8);
|
||||
|
||||
ObservableGroup<string, int>? addedGroup = groupedCollection.AddItem("B", 23);
|
||||
|
||||
Assert.AreSame(addedGroup, targetGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "B");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 3);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(2).Key, "C");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(2), new[] { 7, 8 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddItem_WhenSeveralTargetGroupsAlreadyExist_ShouldAddItemToFirstExistingGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
ObservableGroup<string, int>? targetGroup = groupedCollection.AddGroup("B", 4, 5, 6);
|
||||
_ = groupedCollection.AddGroup("B", 7, 8, 9);
|
||||
_ = groupedCollection.AddGroup("C", 10, 11);
|
||||
|
||||
ObservableGroup<string, int>? addedGroup = groupedCollection.AddItem("B", 23);
|
||||
|
||||
Assert.AreSame(addedGroup, targetGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "B");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 4);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(2).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(2), new[] { 7, 8, 9 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(3).Key, "C");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(3), new[] { 10, 11 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void InsertItem_WhenGroupDoesNotExist_ShouldThrow()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
|
||||
_ = groupedCollection.InsertItem("I do not exist", 0, 23);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(-1)]
|
||||
[DataRow(4)]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void InsertItem_WhenIndexOutOfRange_ShouldThrow(int index)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
|
||||
_ = groupedCollection.InsertItem("A", index, 23);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(0, new[] { 23, 1, 2, 3 })]
|
||||
[DataRow(1, new[] { 1, 23, 2, 3 })]
|
||||
[DataRow(3, new[] { 1, 2, 3, 23 })]
|
||||
public void InsertItem_WithValidIndex_WithSeveralGroups_ShoudInsertItemInFirstGroup(int index, int[] expecteGroupValues)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 4, 5);
|
||||
ObservableGroup<string, int>? targetGroup = groupedCollection.AddGroup("B", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 6, 7);
|
||||
|
||||
ObservableGroup<string, int>? group = groupedCollection.InsertItem("B", index, 23);
|
||||
|
||||
Assert.AreSame(group, targetGroup);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 3);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 4, 5 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), expecteGroupValues);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(2).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(2), new[] { 6, 7 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void SetItem_WhenGroupDoesNotExist_ShouldThrow()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
|
||||
_ = groupedCollection.SetItem("I do not exist", 0, 23);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(-1)]
|
||||
[DataRow(3)]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void SetItem_WhenIndexOutOfRange_ShouldThrow(int index)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
|
||||
_ = groupedCollection.SetItem("A", index, 23);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(0, new[] { 23, 2, 3 })]
|
||||
[DataRow(1, new[] { 1, 23, 3 })]
|
||||
[DataRow(2, new[] { 1, 2, 23 })]
|
||||
public void SetItem_WithValidIndex_WithSeveralGroups_ShouldReplaceItemInFirstGroup(int index, int[] expectedGroupValues)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 4, 5);
|
||||
ObservableGroup<string, int>? targetGroup = groupedCollection.AddGroup("B", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 6, 7);
|
||||
|
||||
ObservableGroup<string, int>? group = groupedCollection.SetItem("B", index, 23);
|
||||
|
||||
Assert.AreSame(group, targetGroup);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 3);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 4, 5 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), expectedGroupValues);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(2).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(2), new[] { 6, 7 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveGroup_WhenGroupDoesNotExists_ShouldDoNothing()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
|
||||
groupedCollection.RemoveGroup("I do not exist");
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 1);
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveGroup_WhenSingleGroupExists_ShouldRemoveGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 4, 5, 6);
|
||||
|
||||
groupedCollection.RemoveGroup("B");
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 1);
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveGroup_WhenSeveralGroupsExist_ShouldRemoveFirstGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 4, 5, 6);
|
||||
_ = groupedCollection.AddGroup("B", 7, 8);
|
||||
|
||||
groupedCollection.RemoveGroup("B");
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), new[] { 7, 8 });
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(true)]
|
||||
[DataRow(false)]
|
||||
public void RemoveItem_WhenGroupDoesNotExist_ShouldDoNothing(bool removeGroupIfEmpty)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 4, 5, 6);
|
||||
|
||||
groupedCollection.RemoveItem("I do not exist", 8, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), new[] { 4, 5, 6 });
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(true)]
|
||||
[DataRow(false)]
|
||||
public void RemoveItem_WhenGroupExistsAndItemDoesNotExist_ShouldDoNothing(bool removeGroupIfEmpty)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 4, 5, 6);
|
||||
|
||||
groupedCollection.RemoveItem("B", 8, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), new[] { 4, 5, 6 });
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(true)]
|
||||
[DataRow(false)]
|
||||
public void RemoveItem_WhenGroupAndItemExist_ShouldRemoveItemFromGroup(bool removeGroupIfEmpty)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 4, 5, 6);
|
||||
|
||||
groupedCollection.RemoveItem("B", 5, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(1), new[] { 4, 6 });
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(true, true)]
|
||||
[DataRow(false, false)]
|
||||
public void RemoveItem_WhenRemovingLastItem_ShouldRemoveGroupIfRequired(bool removeGroupIfEmpty, bool expectGroupRemoved)
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupedCollection = new();
|
||||
_ = groupedCollection.AddGroup("A", 1, 2, 3);
|
||||
_ = groupedCollection.AddGroup("B", 4);
|
||||
|
||||
groupedCollection.RemoveItem("B", 4, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, expectGroupRemoved ? 1 : 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection.ElementAt(0), new[] { 1, 2, 3 });
|
||||
|
||||
if (!expectGroupRemoved)
|
||||
{
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Key, "B");
|
||||
Assert.AreEqual(groupedCollection.ElementAt(1).Count, 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests;
|
||||
|
||||
[TestClass]
|
||||
public class ObservableGroupedCollectionTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Ctor_ShouldHaveExpectedValues()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? groupCollection = new();
|
||||
|
||||
Assert.AreEqual(groupCollection.Count, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_WithGroups_ShouldHaveExpectedValues()
|
||||
{
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? groupCollection = new(groups);
|
||||
|
||||
Assert.AreEqual(groupCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupCollection.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(groupCollection.ElementAt(0), new[] { 1, 3, 5 });
|
||||
|
||||
Assert.AreEqual(groupCollection.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(groupCollection.ElementAt(1), new[] { 2, 4, 6 });
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests;
|
||||
|
||||
[TestClass]
|
||||
public class ReadOnlyObservableGroupTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Ctor_WithKeyAndOBservableCollection_ShouldHaveExpectedInitialState()
|
||||
{
|
||||
ObservableCollection<int>? source = new(new[] { 1, 2, 3 });
|
||||
ReadOnlyObservableGroup<string, int>? group = new("key", source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_ObservableGroup_ShouldHaveExpectedInitialState()
|
||||
{
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int>? group = new(sourceGroup);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_WithKeyAndCollection_ShouldHaveExpectedInitialState()
|
||||
{
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ReadOnlyObservableGroup<string, int>? group = new("key", source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Add_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int>? group = new(sourceGroup);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
sourceGroup.Add(4);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3, 4 });
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Update_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int>? group = new(sourceGroup);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
sourceGroup[1] = 4;
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 4, 3 });
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Remove_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int>? group = new(sourceGroup);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
_ = sourceGroup.Remove(1);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 2, 3 });
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Clear_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[]? source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int>? group = new(sourceGroup);
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
sourceGroup.Clear();
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, Array.Empty<int>());
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(0)]
|
||||
[DataRow(3)]
|
||||
public void IReadOnlyObservableGroup_ShouldReturnExpectedValues(int count)
|
||||
{
|
||||
ObservableGroup<string, int>? sourceGroup = new("key", Enumerable.Range(0, count));
|
||||
ReadOnlyObservableGroup<string, int>? group = new(sourceGroup);
|
||||
IReadOnlyObservableGroup? iReadOnlyObservableGroup = (IReadOnlyObservableGroup)group;
|
||||
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Key, "key");
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Count, count);
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests.Collections;
|
||||
|
||||
[TestClass]
|
||||
public class Test_ObservableGroup
|
||||
{
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroup_Ctor_ShouldHaveExpectedState()
|
||||
{
|
||||
ObservableGroup<string, int> group = new("key");
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
Assert.AreEqual(group.Count, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroup_Ctor_WithGrouping_ShouldHaveExpectedState()
|
||||
{
|
||||
IntGroup source = new("key", new[] { 1, 2, 3 });
|
||||
ObservableGroup<string, int> group = new(source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroup_Ctor_WithCollection_ShouldHaveExpectedState()
|
||||
{
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> group = new("key", source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroup_Add_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> group = new("key", source);
|
||||
|
||||
group.CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
group.Add(4);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3, 4 });
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroup_Update_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> group = new("key", source);
|
||||
|
||||
group.CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
group[1] = 4;
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 4, 3 });
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroup_Remove_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? group = new("key", source);
|
||||
|
||||
group.CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
_ = group.Remove(1);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 2, 3 });
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroup_Clear_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int>? group = new("key", source);
|
||||
|
||||
group.CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
group.Clear();
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
Assert.AreEqual(group.Count, 0);
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(0)]
|
||||
[DataRow(3)]
|
||||
public void Test_ObservableGroup_IReadOnlyObservableGroup_ShouldReturnExpectedValues(int count)
|
||||
{
|
||||
ObservableGroup<string, int> group = new("key", Enumerable.Range(0, count));
|
||||
IReadOnlyObservableGroup iReadOnlyObservableGroup = group;
|
||||
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Key, "key");
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Count, count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ObservableGroup_Ctor_NullKey()
|
||||
{
|
||||
_ = new ObservableGroup<string, int>((string)null!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ObservableGroup_Ctor_NullGroup()
|
||||
{
|
||||
_ = new ObservableGroup<string, int>((IGrouping<string, int>)null!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ObservableGroup_Ctor_NullKeyWithNotNullElements()
|
||||
{
|
||||
_ = new ObservableGroup<string, int>(null!, new int[0]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ObservableGroup_Ctor_NotNullKeyWithNullElements()
|
||||
{
|
||||
_ = new ObservableGroup<string, int>("A", null!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ObservableGroup_Ctor_NullKeySetter()
|
||||
{
|
||||
ObservableGroup<string, int> group = new("A");
|
||||
|
||||
group.Key = null!;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests.Collections;
|
||||
|
||||
[TestClass]
|
||||
public class Test_ObservableGroupedCollection
|
||||
{
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollection_Ctor_ShouldHaveExpectedValues()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupCollection = new();
|
||||
|
||||
Assert.AreEqual(groupCollection.Count, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollection_Ctor_WithGroups_ShouldHaveExpectedValues()
|
||||
{
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ObservableGroupedCollection<string, int> groupCollection = new(groups);
|
||||
|
||||
Assert.AreEqual(groupCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupCollection[0], new[] { 1, 3, 5 });
|
||||
|
||||
Assert.AreEqual(groupCollection[1].Key, "B");
|
||||
CollectionAssert.AreEqual(groupCollection[1], new[] { 2, 4, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ObservableGroupedCollection_Ctor_NullCollection()
|
||||
{
|
||||
_ = new ObservableGroupedCollection<string, int>(null!);
|
||||
}
|
||||
}
|
@ -0,0 +1,454 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests.Collections;
|
||||
|
||||
[TestClass]
|
||||
public class Test_ObservableGroupedCollectionExtensions
|
||||
{
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_FirstGroupByKey_WhenGroupExists_ShouldReturnFirstGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 23 });
|
||||
|
||||
ObservableGroup<string, int> target = groupedCollection.AddGroup("B", new[] { 10 });
|
||||
|
||||
_ = groupedCollection.AddGroup("B", new[] { 42 });
|
||||
|
||||
ObservableGroup<string, int> result = groupedCollection.FirstGroupByKey("B");
|
||||
|
||||
Assert.AreSame(result, target);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void Test_ObservableGroupedCollectionExtensions_FirstGroupByKey_WhenGroupDoesNotExist_ShouldThrow()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 23 });
|
||||
|
||||
_ = groupedCollection.FirstGroupByKey("I do not exist");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_FirstGroupByKeyOrDefault_WhenGroupExists_ShouldReturnFirstGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 23 });
|
||||
|
||||
ObservableGroup<string, int> target = groupedCollection.AddGroup("B", new[] { 10 });
|
||||
|
||||
_ = groupedCollection.AddGroup("B", new[] { 42 });
|
||||
|
||||
ObservableGroup<string, int>? result = groupedCollection.FirstGroupByKeyOrDefault("B");
|
||||
|
||||
Assert.AreSame(result, target);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_FirstGroupByKeyOrDefault_WhenGroupDoesNotExist_ShouldReturnNull()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 23 });
|
||||
|
||||
ObservableGroup<string, int>? result = groupedCollection.FirstGroupByKeyOrDefault("I do not exist");
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_FirstGroupByKey_WhenGroupExistsAndIndexInRange_ShouldReturnFirstGroupValue()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 23 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 10, 11, 12 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 42 });
|
||||
|
||||
int result = groupedCollection.FirstGroupByKey("B")[2];
|
||||
|
||||
Assert.AreEqual(result, 12);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(-1)]
|
||||
[DataRow(3)]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void Test_ObservableGroupedCollectionExtensions_FirstGroupByKey_WhenGroupExistsAndIndexOutOfRange_ShouldReturnThrow(int index)
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 23 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 10, 11, 12 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 42 });
|
||||
|
||||
_ = groupedCollection.FirstGroupByKey("B")[index];
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_FirstGroupByKey_WhenGroupExistsAndIndexInRange_ShouldReturnValue()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 23 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 10, 11, 12 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 42 });
|
||||
|
||||
int result = groupedCollection.FirstGroupByKey("B")[2];
|
||||
|
||||
Assert.AreEqual(result, 12);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_AddGroup_WithItem_ShouldAddGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> addedGroup = groupedCollection.AddGroup("new key", new[] { 23 });
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_AddGroup_WithCollection_ShouldAddGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> addedGroup = groupedCollection.AddGroup("new key", new[] { 23, 10, 42 });
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23, 10, 42 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_AddGroup_WithParamsCollection_ShouldAddGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> addedGroup = groupedCollection.AddGroup("new key", new[] { 23, 10, 42 });
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23, 10, 42 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_InsertGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> a = groupedCollection.InsertGroup("A", new[] { 1, 2, 3 });
|
||||
ObservableGroup<string, int> d = groupedCollection.InsertGroup("D", new[] { 1, 2, 3 });
|
||||
ObservableGroup<string, int> e = groupedCollection.InsertGroup("E", new[] { 1, 2, 3 });
|
||||
ObservableGroup<string, int> b = groupedCollection.InsertGroup("B", new[] { 1, 2, 3 });
|
||||
ObservableGroup<string, int> z = groupedCollection.InsertGroup("Z", new[] { 1, 2, 3 });
|
||||
ObservableGroup<string, int> c = groupedCollection.InsertGroup("C", new[] { 1, 2, 3 });
|
||||
|
||||
CollectionAssert.AllItemsAreNotNull(new[] { a, d, e, b, z, c });
|
||||
CollectionAssert.AreEqual(new[] { a, b, c, d, e, z }, groupedCollection);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_InsertGroup_WithGrouping()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> a = groupedCollection.InsertGroup(new IntGroup("A", new[] { 1, 2, 3 }));
|
||||
ObservableGroup<string, int> d = groupedCollection.InsertGroup(new IntGroup("D", new[] { 1, 2, 3 }));
|
||||
ObservableGroup<string, int> e = groupedCollection.InsertGroup(new IntGroup("E", new[] { 1, 2, 3 }));
|
||||
ObservableGroup<string, int> b = groupedCollection.InsertGroup(new IntGroup("B", new[] { 1, 2, 3 }));
|
||||
ObservableGroup<string, int> z = groupedCollection.InsertGroup(new IntGroup("Z", new[] { 1, 2, 3 }));
|
||||
ObservableGroup<string, int> c = groupedCollection.InsertGroup(new IntGroup("C", new[] { 1, 2, 3 }));
|
||||
|
||||
CollectionAssert.AllItemsAreNotNull(new[] { a, d, e, b, z, c });
|
||||
CollectionAssert.AreEqual(new[] { a, b, c, d, e, z }, groupedCollection);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_AddItem_WhenTargetGroupDoesNotExists_ShouldCreateAndAddNewGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> addedGroup = groupedCollection.AddItem("new key", 23);
|
||||
|
||||
Assert.IsNotNull(addedGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "new key");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 23 });
|
||||
CollectionAssert.AreEqual(groupedCollection, new[] { addedGroup });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_AddItem_WhenSingleTargetGroupAlreadyExists_ShouldAddItemToExistingGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
|
||||
ObservableGroup<string, int> targetGroup = groupedCollection.AddGroup("B", new[] { 4, 5, 6 });
|
||||
|
||||
_ = groupedCollection.AddGroup("C", new[] { 7, 8 });
|
||||
|
||||
ObservableGroup<string, int> addedGroup = groupedCollection.AddItem("B", 23);
|
||||
|
||||
Assert.AreSame(addedGroup, targetGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "B");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 3);
|
||||
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[1].Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection[1], new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[2].Key, "C");
|
||||
CollectionAssert.AreEqual(groupedCollection[2], new[] { 7, 8 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_AddItem_WhenSeveralTargetGroupsAlreadyExist_ShouldAddItemToFirstExistingGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
|
||||
ObservableGroup<string, int> targetGroup = groupedCollection.AddGroup("B", new[] { 4, 5, 6 });
|
||||
|
||||
_ = groupedCollection.AddGroup("B", new[] { 7, 8, 9 });
|
||||
_ = groupedCollection.AddGroup("C", new[] { 10, 11 });
|
||||
|
||||
ObservableGroup<string, int> addedGroup = groupedCollection.AddItem("B", 23);
|
||||
|
||||
Assert.AreSame(addedGroup, targetGroup);
|
||||
Assert.AreEqual(addedGroup.Key, "B");
|
||||
CollectionAssert.AreEqual(addedGroup, new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 4);
|
||||
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[1].Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection[1], new[] { 4, 5, 6, 23 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[2].Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection[2], new[] { 7, 8, 9 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[3].Key, "C");
|
||||
CollectionAssert.AreEqual(groupedCollection[3], new[] { 10, 11 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_InsertItem()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> group1 = groupedCollection.InsertItem("A", 1);
|
||||
ObservableGroup<string, int> group2 = groupedCollection.InsertItem("A", 2);
|
||||
ObservableGroup<string, int> group6 = groupedCollection.InsertItem("A", 6);
|
||||
ObservableGroup<string, int> group4 = groupedCollection.InsertItem("A", 4);
|
||||
ObservableGroup<string, int> group3 = groupedCollection.InsertItem("A", 3);
|
||||
ObservableGroup<string, int> group99 = groupedCollection.InsertItem("A", 99);
|
||||
ObservableGroup<string, int> group8 = groupedCollection.InsertItem("B", 8);
|
||||
ObservableGroup<string, int> group7 = groupedCollection.InsertItem("B", 7);
|
||||
|
||||
Assert.AreEqual(2, groupedCollection.Count);
|
||||
CollectionAssert.AllItemsAreNotNull(new[] { group1, group2, group6, group4, group3, group99, group8, group7 });
|
||||
|
||||
Assert.AreSame(group1, group2);
|
||||
Assert.AreSame(group1, group6);
|
||||
Assert.AreSame(group1, group4);
|
||||
Assert.AreSame(group1, group3);
|
||||
Assert.AreSame(group1, group2);
|
||||
Assert.AreSame(group1, group99);
|
||||
Assert.AreNotSame(group1, group8);
|
||||
Assert.AreSame(group8, group7);
|
||||
|
||||
CollectionAssert.AreEqual(new[] { 1, 2, 3, 4, 6, 99 }, group1);
|
||||
CollectionAssert.AreEqual(new[] { 7, 8 }, group8);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_InsertItem_WithComparer()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
ObservableGroup<string, int> group1 = groupedCollection.InsertItem("A", Comparer<string>.Default, 1, Comparer<int>.Default);
|
||||
ObservableGroup<string, int> group2 = groupedCollection.InsertItem("A", Comparer<string>.Default, 2, Comparer<int>.Default);
|
||||
ObservableGroup<string, int> group6 = groupedCollection.InsertItem("A", Comparer<string>.Default, 6, Comparer<int>.Default);
|
||||
ObservableGroup<string, int> group4 = groupedCollection.InsertItem("A", Comparer<string>.Default, 4, Comparer<int>.Default);
|
||||
ObservableGroup<string, int> group3 = groupedCollection.InsertItem("A", Comparer<string>.Default, 3, Comparer<int>.Default);
|
||||
ObservableGroup<string, int> group99 = groupedCollection.InsertItem("A", Comparer<string>.Default, 99, Comparer<int>.Default);
|
||||
ObservableGroup<string, int> group8 = groupedCollection.InsertItem("B", Comparer<string>.Default, 8, Comparer<int>.Default);
|
||||
ObservableGroup<string, int> group7 = groupedCollection.InsertItem("B", Comparer<string>.Default, 7, Comparer<int>.Default);
|
||||
|
||||
Assert.AreEqual(2, groupedCollection.Count);
|
||||
CollectionAssert.AllItemsAreNotNull(new[] { group1, group2, group6, group4, group3, group99, group8, group7 });
|
||||
|
||||
Assert.AreSame(group1, group2);
|
||||
Assert.AreSame(group1, group6);
|
||||
Assert.AreSame(group1, group4);
|
||||
Assert.AreSame(group1, group3);
|
||||
Assert.AreSame(group1, group2);
|
||||
Assert.AreSame(group1, group99);
|
||||
Assert.AreNotSame(group1, group8);
|
||||
Assert.AreSame(group8, group7);
|
||||
|
||||
CollectionAssert.AreEqual(new[] { 1, 2, 3, 4, 6, 99 }, group1);
|
||||
CollectionAssert.AreEqual(new[] { 7, 8 }, group8);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_RemoveGroup_WhenGroupDoesNotExists_ShouldDoNothing()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
|
||||
groupedCollection.RemoveGroup("I do not exist");
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 1);
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_RemoveGroup_WhenSingleGroupExists_ShouldRemoveGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 4, 5, 6 });
|
||||
|
||||
groupedCollection.RemoveGroup("B");
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 1);
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ObservableGroupedCollectionExtensions_RemoveGroup_WhenSeveralGroupsExist_ShouldRemoveFirstGroup()
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 4, 5, 6 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 7, 8 });
|
||||
|
||||
groupedCollection.RemoveGroup("B");
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[1].Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection[1], new[] { 7, 8 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(true)]
|
||||
[DataRow(false)]
|
||||
public void Test_ObservableGroupedCollectionExtensions_RemoveItem_WhenGroupDoesNotExist_ShouldDoNothing(bool removeGroupIfEmpty)
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 4, 5, 6 });
|
||||
|
||||
groupedCollection.RemoveItem("I do not exist", 8, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[1].Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection[1], new[] { 4, 5, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(true)]
|
||||
[DataRow(false)]
|
||||
public void Test_ObservableGroupedCollectionExtensions_RemoveItem_WhenGroupExistsAndItemDoesNotExist_ShouldDoNothing(bool removeGroupIfEmpty)
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 4, 5, 6 });
|
||||
|
||||
groupedCollection.RemoveItem("B", 8, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[1].Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection[1], new[] { 4, 5, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(true)]
|
||||
[DataRow(false)]
|
||||
public void Test_ObservableGroupedCollectionExtensions_RemoveItem_WhenGroupAndItemExist_ShouldRemoveItemFromGroup(bool removeGroupIfEmpty)
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 4, 5, 6 });
|
||||
|
||||
groupedCollection.RemoveItem("B", 5, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
|
||||
Assert.AreEqual(groupedCollection[1].Key, "B");
|
||||
CollectionAssert.AreEqual(groupedCollection[1], new[] { 4, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(true, true)]
|
||||
[DataRow(false, false)]
|
||||
public void Test_ObservableGroupedCollectionExtensions_RemoveItem_WhenRemovingLastItem_ShouldRemoveGroupIfRequired(bool removeGroupIfEmpty, bool expectGroupRemoved)
|
||||
{
|
||||
ObservableGroupedCollection<string, int> groupedCollection = new();
|
||||
|
||||
_ = groupedCollection.AddGroup("A", new[] { 1, 2, 3 });
|
||||
_ = groupedCollection.AddGroup("B", new[] { 4 });
|
||||
|
||||
groupedCollection.RemoveItem("B", 4, removeGroupIfEmpty);
|
||||
|
||||
Assert.AreEqual(groupedCollection.Count, expectGroupRemoved ? 1 : 2);
|
||||
|
||||
Assert.AreEqual(groupedCollection[0].Key, "A");
|
||||
CollectionAssert.AreEqual(groupedCollection[0], new[] { 1, 2, 3 });
|
||||
|
||||
if (!expectGroupRemoved)
|
||||
{
|
||||
Assert.AreEqual(groupedCollection[1].Key, "B");
|
||||
Assert.AreEqual(groupedCollection[1].Count, 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests.Collections;
|
||||
|
||||
[TestClass]
|
||||
public class Test_ReadOnlyObservableGroup
|
||||
{
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroup_Ctor_WithKeyAndOBservableCollection_ShouldHaveExpectedInitialState()
|
||||
{
|
||||
ObservableCollection<int> source = new(new[] { 1, 2, 3 });
|
||||
ReadOnlyObservableGroup<string, int> group = new("key", source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroup_Ctor_ObservableGroup_ShouldHaveExpectedInitialState()
|
||||
{
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int> group = new(sourceGroup);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroup_Ctor_WithKeyAndCollection_ShouldHaveExpectedInitialState()
|
||||
{
|
||||
ObservableCollection<int> source = new() { 1, 2, 3 };
|
||||
ReadOnlyObservableGroup<string, int> group = new("key", source);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroup_Add_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int> group = new(sourceGroup);
|
||||
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
sourceGroup.Add(4);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 2, 3, 4 });
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroup_Update_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int> group = new(sourceGroup);
|
||||
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
sourceGroup[1] = 4;
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 1, 4, 3 });
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroup_Remove_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int> group = new(sourceGroup);
|
||||
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
_ = sourceGroup.Remove(1);
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, new[] { 2, 3 });
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroup_Clear_ShouldRaiseEvent()
|
||||
{
|
||||
bool collectionChangedEventRaised = false;
|
||||
int[] source = new[] { 1, 2, 3 };
|
||||
ObservableGroup<string, int> sourceGroup = new("key", source);
|
||||
ReadOnlyObservableGroup<string, int> group = new(sourceGroup);
|
||||
|
||||
((INotifyCollectionChanged)group).CollectionChanged += (s, e) => collectionChangedEventRaised = true;
|
||||
|
||||
sourceGroup.Clear();
|
||||
|
||||
Assert.AreEqual(group.Key, "key");
|
||||
CollectionAssert.AreEqual(group, Array.Empty<int>());
|
||||
|
||||
Assert.IsTrue(collectionChangedEventRaised);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(0)]
|
||||
[DataRow(3)]
|
||||
public void Test_ReadOnlyObservableGroup_IReadOnlyObservableGroup_ShouldReturnExpectedValues(int count)
|
||||
{
|
||||
ObservableGroup<string, int> sourceGroup = new("key", Enumerable.Range(0, count));
|
||||
ReadOnlyObservableGroup<string, int> group = new(sourceGroup);
|
||||
IReadOnlyObservableGroup iReadOnlyObservableGroup = group;
|
||||
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Key, "key");
|
||||
Assert.AreEqual(iReadOnlyObservableGroup.Count, count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ReadOnlyObservableGroup_Ctor_NullKeyWithNotNullElements()
|
||||
{
|
||||
_ = new ReadOnlyObservableGroup<string, int>(null!, new ObservableCollection<int>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ReadOnlyObservableGroup_Ctor_NotNullKeyWithNullElements()
|
||||
{
|
||||
_ = new ReadOnlyObservableGroup<string, int>("A", null!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ReadOnlyObservableGroup_Ctor_NullGroup()
|
||||
{
|
||||
_ = new ReadOnlyObservableGroup<string, int>(null!);
|
||||
}
|
||||
}
|
@ -12,99 +12,80 @@
|
||||
using CommunityToolkit.Mvvm.Collections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace CommunityToolkit.Mvvm.UnitTests;
|
||||
namespace CommunityToolkit.Mvvm.UnitTests.Collections;
|
||||
|
||||
[TestClass]
|
||||
public class ReadOnlyObservableGroupedCollectionTests
|
||||
public class Test_ReadOnlyObservableGroupedCollection
|
||||
{
|
||||
[TestMethod]
|
||||
public void Ctor_WithEmptySource_ShoudInitializeObject()
|
||||
public void Test_ReadOnlyObservableGroupedCollection_Ctor_WithEmptySource_ShoudInitializeObject()
|
||||
{
|
||||
ObservableGroupedCollection<string, int>? source = new();
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ObservableGroupedCollection<string, int> source = new();
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 0);
|
||||
CollectionAssert.AreEqual(readOnlyGroup, Array.Empty<int>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_WithObservableGroupedCollection_ShoudInitializeObject()
|
||||
public void Test_ReadOnlyObservableGroupedCollection_Ctor_WithObservableGroupedCollection_ShoudInitializeObject()
|
||||
{
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 2);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(0), new[] { 1, 3, 5 });
|
||||
Assert.AreEqual(readOnlyGroup[0].Key, "A");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup[0], new[] { 1, 3, 5 });
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(1), new[] { 2, 4, 6 });
|
||||
Assert.AreEqual(readOnlyGroup[1].Key, "B");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup[1], new[] { 2, 4, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_WithListOfIGroupingSource_ShoudInitializeObject()
|
||||
public void Test_ReadOnlyObservableGroupedCollection_Ctor_WithListOfReadOnlyObservableGroupSource_ShoudInitializeObject()
|
||||
{
|
||||
List<IGrouping<string, int>>? source = new()
|
||||
ObservableCollection<ReadOnlyObservableGroup<string, int>> source = new()
|
||||
{
|
||||
new ReadOnlyObservableGroup<string, int>("A", new ObservableCollection<int> { 1, 3, 5 }),
|
||||
new ReadOnlyObservableGroup<string, int>("B", new ObservableCollection<int> { 2, 4, 6 }),
|
||||
};
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 2);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup[0].Key, "A");
|
||||
CollectionAssert.AreEqual(readOnlyGroup[0], new[] { 1, 3, 5 });
|
||||
|
||||
Assert.AreEqual(readOnlyGroup[1].Key, "B");
|
||||
CollectionAssert.AreEqual(readOnlyGroup[1], new[] { 2, 4, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_ReadOnlyObservableGroupedCollection_IListImplementation_Properties_ShoudReturnExpectedValues()
|
||||
{
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 2);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(0), new[] { 1, 3, 5 });
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(1), new[] { 2, 4, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_WithListOfReadOnlyObservableGroupSource_ShoudInitializeObject()
|
||||
{
|
||||
List<ReadOnlyObservableGroup<string, int>>? source = new()
|
||||
{
|
||||
new ReadOnlyObservableGroup<string, int>("A", new[] { 1, 3, 5 }),
|
||||
new ReadOnlyObservableGroup<string, int>("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 2);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEqual(readOnlyGroup.ElementAt(0), new[] { 1, 3, 5 });
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEqual(readOnlyGroup.ElementAt(1), new[] { 2, 4, 6 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IListImplementation_Properties_ShoudReturnExpectedValues()
|
||||
{
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
IList? list = (IList)readOnlyGroup;
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
IList list = readOnlyGroup;
|
||||
|
||||
Assert.AreEqual(list.Count, 2);
|
||||
|
||||
ReadOnlyObservableGroup<string, int>? group0 = (ReadOnlyObservableGroup<string, int>)list[0]!;
|
||||
ReadOnlyObservableGroup<string, int> group0 = (ReadOnlyObservableGroup<string, int>)list[0]!;
|
||||
|
||||
Assert.AreEqual(group0.Key, "A");
|
||||
CollectionAssert.AreEqual(group0, new[] { 1, 3, 5 });
|
||||
|
||||
ReadOnlyObservableGroup<string, int>? group1 = (ReadOnlyObservableGroup<string, int>)list[1]!;
|
||||
ReadOnlyObservableGroup<string, int> group1 = (ReadOnlyObservableGroup<string, int>)list[1]!;
|
||||
|
||||
Assert.AreEqual(group1.Key, "B");
|
||||
CollectionAssert.AreEqual(group1, new[] { 2, 4, 6 });
|
||||
@ -116,16 +97,16 @@ public void IListImplementation_Properties_ShoudReturnExpectedValues()
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IListImplementation_MutableMethods_ShoudThrow()
|
||||
public void Test_ReadOnlyObservableGroupedCollection_IListImplementation_MutableMethods_ShoudThrow()
|
||||
{
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
IList? list = (IList)readOnlyGroup;
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
IList list = readOnlyGroup;
|
||||
|
||||
ReadOnlyObservableGroup<string, int>? testGroup = new("test", new ObservableCollection<int>());
|
||||
|
||||
@ -142,22 +123,22 @@ public void IListImplementation_MutableMethods_ShoudThrow()
|
||||
list.CopyTo(array, 0);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[TestMethod]
|
||||
[DataRow(-1)]
|
||||
[DataRow(0)]
|
||||
[DataRow(1)]
|
||||
[DataRow(2)]
|
||||
public void IListImplementation_IndexOf_ShoudReturnExpectedValue(int groupIndex)
|
||||
public void Test_ReadOnlyObservableGroupedCollection_IListImplementation_IndexOf_ShoudReturnExpectedValue(int groupIndex)
|
||||
{
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
new IntGroup("C", new[] { 7, 8, 9 }),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
IList? list = (IList)readOnlyGroup;
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
IList list = readOnlyGroup;
|
||||
|
||||
object? groupToSearch = groupIndex >= 0 ? list[groupIndex] : null;
|
||||
|
||||
@ -166,20 +147,20 @@ public void IListImplementation_IndexOf_ShoudReturnExpectedValue(int groupIndex)
|
||||
Assert.AreEqual(index, groupIndex);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[TestMethod]
|
||||
[DataRow(-1, false)]
|
||||
[DataRow(0, true)]
|
||||
[DataRow(1, true)]
|
||||
public void IListImplementation_Contains_ShoudReturnExpectedValue(int groupIndex, bool expectedResult)
|
||||
public void Test_ReadOnlyObservableGroupedCollection_IListImplementation_Contains_ShoudReturnExpectedValue(int groupIndex, bool expectedResult)
|
||||
{
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", new[] { 1, 3, 5 }),
|
||||
new IntGroup("B", new[] { 2, 4, 6 }),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
IList? list = (IList)readOnlyGroup;
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
IList list = readOnlyGroup;
|
||||
|
||||
object? groupToSearch = groupIndex >= 0 ? list[groupIndex] : null;
|
||||
|
||||
@ -188,22 +169,22 @@ public void IListImplementation_Contains_ShoudReturnExpectedValue(int groupIndex
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[TestMethod]
|
||||
[DataRow(0, 0)]
|
||||
[DataRow(3, 3)]
|
||||
public void AddGroupInSource_ShouldAddGroup(int sourceInitialItemsCount, int expectedInsertionIndex)
|
||||
public void Test_ReadOnlyObservableGroupedCollection_AddGroupInSource_ShouldAddGroup(int sourceInitialItemsCount, int expectedInsertionIndex)
|
||||
{
|
||||
NotifyCollectionChangedEventArgs? collectionChangedEventArgs = null;
|
||||
int collectionChangedEventsCount = 0;
|
||||
bool isCountPropertyChangedEventRaised = false;
|
||||
int[]? itemsList = new[] { 1, 2, 3 };
|
||||
ObservableGroupedCollection<string, int>? source = new();
|
||||
int[] itemsList = new[] { 1, 2, 3 };
|
||||
ObservableGroupedCollection<string, int> source = new();
|
||||
for (int i = 0; i < sourceInitialItemsCount; i++)
|
||||
{
|
||||
source.Add(new ObservableGroup<string, int>($"group {i}", Enumerable.Empty<int>()));
|
||||
}
|
||||
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
((INotifyCollectionChanged)readOnlyGroup).CollectionChanged += (s, e) =>
|
||||
{
|
||||
collectionChangedEventArgs = e;
|
||||
@ -216,10 +197,7 @@ public void AddGroupInSource_ShouldAddGroup(int sourceInitialItemsCount, int exp
|
||||
int expectedReadOnlyGroupCount = sourceInitialItemsCount + 1;
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, expectedReadOnlyGroupCount);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Last().Key, "Add");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.Last(), itemsList);
|
||||
|
||||
Assert.AreEqual("Add", readOnlyGroup[readOnlyGroup.Count - 1].Key);
|
||||
Assert.IsTrue(isCountPropertyChangedEventRaised);
|
||||
Assert.IsNotNull(collectionChangedEventArgs);
|
||||
Assert.AreEqual(collectionChangedEventsCount, 1);
|
||||
@ -229,22 +207,22 @@ public void AddGroupInSource_ShouldAddGroup(int sourceInitialItemsCount, int exp
|
||||
Assert.IsTrue(isAddEventValid);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[TestMethod]
|
||||
[DataRow(0)]
|
||||
[DataRow(1)]
|
||||
[DataRow(2)]
|
||||
public void InsertGroupInSource_ShouldAddGroup(int insertionIndex)
|
||||
public void Test_ReadOnlyObservableGroupedCollection_InsertGroupInSource_ShouldAddGroup(int insertionIndex)
|
||||
{
|
||||
NotifyCollectionChangedEventArgs? collectionChangedEventArgs = null;
|
||||
int collectionChangedEventsCount = 0;
|
||||
bool isCountPropertyChangedEventRaised = false;
|
||||
int[]? itemsList = new[] { 1, 2, 3 };
|
||||
ObservableGroupedCollection<string, int>? source = new()
|
||||
int[] itemsList = new[] { 1, 2, 3 };
|
||||
ObservableGroupedCollection<string, int> source = new()
|
||||
{
|
||||
new ObservableGroup<string, int>("Group0", new[] { 10, 20, 30 }),
|
||||
new ObservableGroup<string, int>("Group1", new[] { 40, 50, 60 })
|
||||
};
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
((INotifyCollectionChanged)readOnlyGroup).CollectionChanged += (s, e) =>
|
||||
{
|
||||
collectionChangedEventArgs = e;
|
||||
@ -255,10 +233,7 @@ public void InsertGroupInSource_ShouldAddGroup(int insertionIndex)
|
||||
source.Insert(insertionIndex, new ObservableGroup<string, int>("Add", itemsList));
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 3);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(insertionIndex).Key, "Add");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(insertionIndex), itemsList);
|
||||
|
||||
Assert.AreEqual("Add", readOnlyGroup[insertionIndex].Key);
|
||||
Assert.IsTrue(isCountPropertyChangedEventRaised);
|
||||
Assert.IsNotNull(collectionChangedEventArgs);
|
||||
Assert.AreEqual(collectionChangedEventsCount, 1);
|
||||
@ -269,20 +244,20 @@ public void InsertGroupInSource_ShouldAddGroup(int insertionIndex)
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveGroupInSource_ShoudRemoveGroup()
|
||||
public void Test_ReadOnlyObservableGroupedCollection_RemoveGroupInSource_ShoudRemoveGroup()
|
||||
{
|
||||
NotifyCollectionChangedEventArgs? collectionChangedEventArgs = null;
|
||||
int collectionChangedEventsCount = 0;
|
||||
bool isCountPropertyChangedEventRaised = false;
|
||||
int[]? aItemsList = new[] { 1, 2, 3 };
|
||||
int[]? bItemsList = new[] { 2, 4, 6 };
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
int[] aItemsList = new[] { 1, 2, 3 };
|
||||
int[] bItemsList = new[] { 2, 4, 6 };
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", aItemsList),
|
||||
new IntGroup("B", bItemsList),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
((INotifyCollectionChanged)readOnlyGroup).CollectionChanged += (s, e) =>
|
||||
{
|
||||
collectionChangedEventArgs = e;
|
||||
@ -294,8 +269,8 @@ public void RemoveGroupInSource_ShoudRemoveGroup()
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 1);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(0).Key, "A");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(0), aItemsList);
|
||||
Assert.AreEqual(readOnlyGroup[0].Key, "A");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup[0], aItemsList);
|
||||
|
||||
Assert.IsTrue(isCountPropertyChangedEventRaised);
|
||||
Assert.IsNotNull(collectionChangedEventArgs);
|
||||
@ -306,23 +281,28 @@ public void RemoveGroupInSource_ShoudRemoveGroup()
|
||||
Assert.IsTrue(isRemoveEventValid);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[TestMethod]
|
||||
[DataRow(1, 0)]
|
||||
[DataRow(0, 1)]
|
||||
public void MoveGroupInSource_ShoudMoveGroup(int oldIndex, int newIndex)
|
||||
[DataRow(0, 2)]
|
||||
public void Test_ReadOnlyObservableGroupedCollection_MoveGroupInSource_ShoudMoveGroup(int oldIndex, int newIndex)
|
||||
{
|
||||
NotifyCollectionChangedEventArgs? collectionChangedEventArgs = null;
|
||||
int collectionChangedEventsCount = 0;
|
||||
bool isCountPropertyChangedEventRaised = false;
|
||||
int[]? aItemsList = new[] { 1, 2, 3 };
|
||||
int[]? bItemsList = new[] { 2, 4, 6 };
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
int[] aItemsList = new[] { 1, 2, 3 };
|
||||
int[] bItemsList = new[] { 4, 5, 6 };
|
||||
int[] cItemsList = new[] { 7, 8, 9 };
|
||||
int[] dItemsList = new[] { 10, 11, 12 };
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", aItemsList),
|
||||
new IntGroup("B", bItemsList),
|
||||
new IntGroup("C", cItemsList),
|
||||
new IntGroup("D", dItemsList),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
((INotifyCollectionChanged)readOnlyGroup).CollectionChanged += (s, e) =>
|
||||
{
|
||||
collectionChangedEventArgs = e;
|
||||
@ -332,13 +312,22 @@ public void MoveGroupInSource_ShoudMoveGroup(int oldIndex, int newIndex)
|
||||
|
||||
source.Move(oldIndex, newIndex);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 2);
|
||||
Assert.AreEqual(groups.Count, readOnlyGroup.Count);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(0).Key, "B");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(0), bItemsList);
|
||||
Assert.AreEqual(readOnlyGroup[0].Key, "B");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup[0], bItemsList);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(1).Key, "A");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(1), aItemsList);
|
||||
List<IGrouping<string, int>> tempList = new(groups);
|
||||
IGrouping<string, int> tempItem = tempList[oldIndex];
|
||||
|
||||
tempList.RemoveAt(oldIndex);
|
||||
tempList.Insert(newIndex, tempItem);
|
||||
|
||||
for (int i = 0; i < tempList.Count; i++)
|
||||
{
|
||||
Assert.AreEqual(tempList[i].Key, readOnlyGroup[i].Key);
|
||||
CollectionAssert.AreEqual(tempList[i].ToArray(), readOnlyGroup[i].ToArray());
|
||||
}
|
||||
|
||||
Assert.IsFalse(isCountPropertyChangedEventRaised);
|
||||
Assert.IsNotNull(collectionChangedEventArgs);
|
||||
@ -350,20 +339,20 @@ public void MoveGroupInSource_ShoudMoveGroup(int oldIndex, int newIndex)
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ClearSource_ShoudClear()
|
||||
public void Test_ReadOnlyObservableGroupedCollection_ClearSource_ShoudClear()
|
||||
{
|
||||
NotifyCollectionChangedEventArgs? collectionChangedEventArgs = null;
|
||||
int collectionChangedEventsCount = 0;
|
||||
bool isCountPropertyChangedEventRaised = false;
|
||||
int[]? aItemsList = new[] { 1, 2, 3 };
|
||||
int[]? bItemsList = new[] { 2, 4, 6 };
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
int[] aItemsList = new[] { 1, 2, 3 };
|
||||
int[] bItemsList = new[] { 2, 4, 6 };
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", aItemsList),
|
||||
new IntGroup("B", bItemsList),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
((INotifyCollectionChanged)readOnlyGroup).CollectionChanged += (s, e) =>
|
||||
{
|
||||
collectionChangedEventArgs = e;
|
||||
@ -385,21 +374,21 @@ public void ClearSource_ShoudClear()
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReplaceGroupInSource_ShoudReplaceGroup()
|
||||
public void Test_ReadOnlyObservableGroupedCollection_ReplaceGroupInSource_ShoudReplaceGroup()
|
||||
{
|
||||
NotifyCollectionChangedEventArgs? collectionChangedEventArgs = null;
|
||||
int collectionChangedEventsCount = 0;
|
||||
bool isCountPropertyChangedEventRaised = false;
|
||||
int[]? aItemsList = new[] { 1, 2, 3 };
|
||||
int[]? bItemsList = new[] { 2, 4, 6 };
|
||||
int[]? cItemsList = new[] { 7, 8, 9 };
|
||||
List<IGrouping<string, int>>? groups = new()
|
||||
int[] aItemsList = new[] { 1, 2, 3 };
|
||||
int[] bItemsList = new[] { 2, 4, 6 };
|
||||
int[] cItemsList = new[] { 7, 8, 9 };
|
||||
List<IGrouping<string, int>> groups = new()
|
||||
{
|
||||
new IntGroup("A", aItemsList),
|
||||
new IntGroup("B", bItemsList),
|
||||
};
|
||||
ObservableGroupedCollection<string, int>? source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int>? readOnlyGroup = new(source);
|
||||
ObservableGroupedCollection<string, int> source = new(groups);
|
||||
ReadOnlyObservableGroupedCollection<string, int> readOnlyGroup = new(source);
|
||||
((INotifyCollectionChanged)readOnlyGroup).CollectionChanged += (s, e) =>
|
||||
{
|
||||
collectionChangedEventArgs = e;
|
||||
@ -411,11 +400,11 @@ public void ReplaceGroupInSource_ShoudReplaceGroup()
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.Count, 2);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(0).Key, "C");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(0), cItemsList);
|
||||
Assert.AreEqual(readOnlyGroup[0].Key, "C");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup[0], cItemsList);
|
||||
|
||||
Assert.AreEqual(readOnlyGroup.ElementAt(1).Key, "B");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup.ElementAt(1), bItemsList);
|
||||
Assert.AreEqual(readOnlyGroup[1].Key, "B");
|
||||
CollectionAssert.AreEquivalent(readOnlyGroup[1], bItemsList);
|
||||
|
||||
Assert.IsFalse(isCountPropertyChangedEventRaised);
|
||||
Assert.IsNotNull(collectionChangedEventArgs);
|
||||
@ -479,4 +468,18 @@ private static bool IsReplaceEventValid(NotifyCollectionChangedEventArgs args, I
|
||||
}
|
||||
|
||||
private static bool IsResetEventValid(NotifyCollectionChangedEventArgs args) => args.Action == NotifyCollectionChangedAction.Reset && args.NewItems == null && args.OldItems == null;
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ReadOnlyObservableGroupedCollection_Ctor_NullCollectionWithObservableGroups()
|
||||
{
|
||||
_ = new ReadOnlyObservableGroupedCollection<string, int>((ObservableCollection<ObservableGroup<string, int>>)null!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Test_ReadOnlyObservableGroupedCollection_Ctor_NullCollectionWithReadOnlyObservableGroups()
|
||||
{
|
||||
_ = new ReadOnlyObservableGroupedCollection<string, int>((ObservableCollection<ReadOnlyObservableGroup<string, int>>)null!);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user