File: Collections\List\SegmentedList.Generic.Tests.AddRange.cs
Web Access
Project: src\src\Compilers\Core\CodeAnalysisTest\Microsoft.CodeAnalysis.UnitTests.csproj (Microsoft.CodeAnalysis.UnitTests)
// 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.
 
// NOTE: This code is derived from an implementation originally in dotnet/runtime:
// https://github.com/dotnet/runtime/blob/v8.0.3/src/libraries/System.Collections/tests/Generic/List/List.Generic.Tests.AddRange.cs
//
// See the commentary in https://github.com/dotnet/roslyn/pull/50156 for notes on incorporating changes made to the
// reference implementation.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.UnitTests.Collections
{
    /// <summary>
    /// Contains tests that ensure the correctness of the List class.
    /// </summary>
    public abstract partial class SegmentedList_Generic_Tests<T> : IList_Generic_Tests<T>
    {
        // Has tests that pass a variably sized TestCollection and MyEnumerable to the AddRange function
 
        [Theory]
        [MemberData(nameof(EnumerableTestData))]
        public void AddRange(EnumerableType enumerableType, int listLength, int enumerableLength, int numberOfMatchingElements, int numberOfDuplicateElements)
        {
            SegmentedList<T> list = GenericListFactory(listLength);
            SegmentedList<T> listBeforeAdd = list.ToSegmentedList();
            IEnumerable<T> enumerable = CreateEnumerable(enumerableType, list, enumerableLength, numberOfMatchingElements, numberOfDuplicateElements);
            list.AddRange(enumerable);
 
            // Check that the first section of the List is unchanged
            Assert.All(Enumerable.Range(0, listLength), index =>
            {
                Assert.Equal(listBeforeAdd[index], list[index]);
            });
 
            // Check that the added elements are correct
            Assert.All(Enumerable.Range(0, enumerableLength), index =>
            {
                Assert.Equal(enumerable.ElementAt(index), list[index + listLength]);
            });
        }
 
#if true
        [Fact]
        public void NoAddRangeExtension()
        {
            foreach (var type in typeof(SegmentedList<>).Assembly.DefinedTypes)
            {
                foreach (var method in type.DeclaredMethods)
                {
                    if (!method.IsStatic)
                        continue;
 
                    if (method.Name is not "AddRange")
                        continue;
 
                    if (method.GetParameters() is not [var firstParameter, var spanParameter])
                        continue;
 
                    // AddRange(SegmentedList<int>, ReadOnlySpan<int>)
                    if (firstParameter.ParameterType.GetGenericTypeDefinition() != typeof(SegmentedList<>))
                        continue;
 
                    if (spanParameter.ParameterType.GetGenericTypeDefinition() != typeof(ReadOnlySpan<>))
                        continue;
 
                    // If this fails, it means the extension necessary for the following tests has now been implemented
                    throw ExceptionUtilities.UnexpectedValue(method);
                }
            }
        }
#else
        [Theory]
        [MemberData(nameof(ListTestData))]
        public void AddRange_Span(EnumerableType enumerableType, int listLength, int enumerableLength, int numberOfMatchingElements, int numberOfDuplicateElements)
        {
            SegmentedList<T> list = GenericListFactory(listLength);
            SegmentedList<T> listBeforeAdd = list.ToSegmentedList();
            Span<T> span = CreateEnumerable(enumerableType, list, enumerableLength, numberOfMatchingElements, numberOfDuplicateElements).ToArray();
            list.AddRange(span);
 
            // Check that the first section of the List is unchanged
            Assert.All(Enumerable.Range(0, listLength), index =>
            {
                Assert.Equal(listBeforeAdd[index], list[index]);
            });
 
            // Check that the added elements are correct
            for (int i = 0; i < enumerableLength; i++)
            {
                Assert.Equal(span[i], list[i + listLength]);
            };
        }
 
        [Fact]
        public void AddRange_NullList_ThrowsArgumentNullException()
        {
            Assert.Throws<ArgumentNullException>("list", () => SegmentedCollectionExtensions.AddRange<int>(null!, default));
            Assert.Throws<ArgumentNullException>("list", () => SegmentedCollectionExtensions.AddRange<int>(null!, new int[1]));
        }
#endif
 
        [Theory]
        [MemberData(nameof(ValidCollectionSizes))]
        public void AddRange_NullEnumerable_ThrowsArgumentNullException(int count)
        {
            SegmentedList<T> list = GenericListFactory(count);
            SegmentedList<T> listBeforeAdd = list.ToSegmentedList();
            Assert.Throws<ArgumentNullException>(() => list.AddRange(null!));
            Assert.Equal(listBeforeAdd, list);
        }
 
        [Fact]
        public void AddRange_AddSelfAsEnumerable_ThrowsExceptionWhenNotEmpty()
        {
            SegmentedList<T?> list = GenericListFactory(0)!;
 
            // Succeeds when list is empty.
            list.AddRange(list);
            list.AddRange(list.Where(_ => true));
 
            // Succeeds when list has elements and is added as collection.
            list.Add(default);
            Assert.Equal(1, list.Count);
            list.AddRange(list);
            Assert.Equal(2, list.Count);
            list.AddRange(list);
            Assert.Equal(4, list.Count);
 
            // Fails version check when list has elements and is added as non-collection.
            Assert.Throws<InvalidOperationException>(() => list.AddRange(list.Where(_ => true)));
            Assert.Equal(5, list.Count);
            Assert.Throws<InvalidOperationException>(() => list.AddRange(list.Where(_ => true)));
            Assert.Equal(6, list.Count);
        }
 
        [Fact]
        public void AddRange_CollectionWithLargeCount_ThrowsOverflowException()
        {
            SegmentedList<T> list = GenericListFactory(count: 1);
            ICollection<T> collection = new CollectionWithLargeCount();
 
            Assert.Throws<OverflowException>(() => list.AddRange(collection));
        }
 
        private class CollectionWithLargeCount : ICollection<T>
        {
            public int Count => int.MaxValue;
 
            public bool IsReadOnly => throw new NotImplementedException();
            public void Add(T item) => throw new NotImplementedException();
            public void Clear() => throw new NotImplementedException();
            public bool Contains(T item) => throw new NotImplementedException();
            public void CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException();
            public IEnumerator<T> GetEnumerator() => throw new NotImplementedException();
            public bool Remove(T item) => throw new NotImplementedException();
            IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
        }
    }
}