1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Linq;
7 using Xunit;
8 
9 namespace System.Collections.Tests
10 {
11     /// <summary>
12     /// Contains tests that ensure the correctness of the List class.
13     /// </summary>
14     public abstract partial class List_Generic_Tests<T> : IList_Generic_Tests<T>
15     {
16         #region Helpers
17 
IndexOfDelegate(List<T> list, T value)18         public delegate int IndexOfDelegate(List<T> list, T value);
19         public enum IndexOfMethod
20         {
21             IndexOf_T,
22             IndexOf_T_int,
23             IndexOf_T_int_int,
24             LastIndexOf_T,
25             LastIndexOf_T_int,
26             LastIndexOf_T_int_int,
27         };
28 
IndexOfDelegateFromType(IndexOfMethod methodType)29         private IndexOfDelegate IndexOfDelegateFromType(IndexOfMethod methodType)
30         {
31             switch (methodType)
32             {
33                 case (IndexOfMethod.IndexOf_T):
34                     return ((List<T> list, T value) => { return list.IndexOf(value); });
35                 case (IndexOfMethod.IndexOf_T_int):
36                     return ((List<T> list, T value) => { return list.IndexOf(value, 0); });
37                 case (IndexOfMethod.IndexOf_T_int_int):
38                     return ((List<T> list, T value) => { return list.IndexOf(value, 0, list.Count); });
39                 case (IndexOfMethod.LastIndexOf_T):
40                     return ((List<T> list, T value) => { return list.LastIndexOf(value); });
41                 case (IndexOfMethod.LastIndexOf_T_int):
42                     return ((List<T> list, T value) => { return list.LastIndexOf(value, list.Count - 1); });
43                 case (IndexOfMethod.LastIndexOf_T_int_int):
44                     return ((List<T> list, T value) => { return list.LastIndexOf(value, list.Count - 1, list.Count); });
45                 default:
46                     throw new Exception("Invalid IndexOfMethod");
47             }
48         }
49 
50         /// <summary>
51         /// MemberData for a Theory to test the IndexOf methods for List. To avoid high code reuse of tests for the 6 IndexOf
52         /// methods in List, delegates are used to cover the basic behavioral cases shared by all IndexOf methods. A bool
53         /// is used to specify the ordering (front-to-back or back-to-front (e.g. LastIndexOf)) that the IndexOf method
54         /// searches in.
55         /// </summary>
IndexOfTestData()56         public static IEnumerable<object[]> IndexOfTestData()
57         {
58             foreach (object[] sizes in ValidCollectionSizes())
59             {
60                 int count = (int)sizes[0];
61                 yield return new object[] { IndexOfMethod.IndexOf_T, count, true };
62                 yield return new object[] { IndexOfMethod.LastIndexOf_T, count, false };
63 
64                 if (count > 0) // 0 is an invalid index for IndexOf when the count is 0.
65                 {
66                     yield return new object[] { IndexOfMethod.IndexOf_T_int, count, true };
67                     yield return new object[] { IndexOfMethod.LastIndexOf_T_int, count, false };
68                     yield return new object[] { IndexOfMethod.IndexOf_T_int_int, count, true };
69                     yield return new object[] { IndexOfMethod.LastIndexOf_T_int_int, count, false };
70                 }
71             }
72         }
73 
74         #endregion
75 
76         #region IndexOf
77 
78         [Theory]
79         [MemberData(nameof(IndexOfTestData))]
IndexOf_NoDuplicates(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)80         public void IndexOf_NoDuplicates(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)
81         {
82             List<T> list = GenericListFactory(count);
83             List<T> expectedList = list.ToList();
84             IndexOfDelegate IndexOf = IndexOfDelegateFromType(indexOfMethod);
85 
86             Assert.All(Enumerable.Range(0, count), i =>
87             {
88                 Assert.Equal(i, IndexOf(list, expectedList[i]));
89             });
90         }
91 
92         [Theory]
93         [MemberData(nameof(IndexOfTestData))]
IndexOf_NonExistingValues(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)94         public void IndexOf_NonExistingValues(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)
95         {
96             List<T> list = GenericListFactory(count);
97             IEnumerable<T> nonexistentValues = CreateEnumerable(EnumerableType.List, list, count: count, numberOfMatchingElements: 0, numberOfDuplicateElements: 0);
98             IndexOfDelegate IndexOf = IndexOfDelegateFromType(indexOfMethod);
99 
100             Assert.All(nonexistentValues, nonexistentValue =>
101             {
102                 Assert.Equal(-1, IndexOf(list, nonexistentValue));
103             });
104         }
105 
106         [Theory]
107         [MemberData(nameof(IndexOfTestData))]
IndexOf_DefaultValue(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)108         public void IndexOf_DefaultValue(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)
109         {
110             T defaultValue = default(T);
111             List<T> list = GenericListFactory(count);
112             IndexOfDelegate IndexOf = IndexOfDelegateFromType(indexOfMethod);
113             while (list.Remove(defaultValue))
114                 count--;
115             list.Add(defaultValue);
116             Assert.Equal(count, IndexOf(list, defaultValue));
117         }
118 
119         [Theory]
120         [MemberData(nameof(IndexOfTestData))]
IndexOf_OrderIsCorrect(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)121         public void IndexOf_OrderIsCorrect(IndexOfMethod indexOfMethod, int count, bool frontToBackOrder)
122         {
123             List<T> list = GenericListFactory(count);
124             List<T> withoutDuplicates = list.ToList();
125             list.AddRange(list);
126             IndexOfDelegate IndexOf = IndexOfDelegateFromType(indexOfMethod);
127 
128             Assert.All(Enumerable.Range(0, count), i =>
129             {
130                 if (frontToBackOrder)
131                     Assert.Equal(i, IndexOf(list, withoutDuplicates[i]));
132                 else
133                     Assert.Equal(count + i, IndexOf(list, withoutDuplicates[i]));
134             });
135         }
136 
137         [Theory]
138         [MemberData(nameof(ValidCollectionSizes))]
IndexOf_int_OrderIsCorrectWithManyDuplicates(int count)139         public void IndexOf_int_OrderIsCorrectWithManyDuplicates(int count)
140         {
141             List<T> list = GenericListFactory(count);
142             List<T> withoutDuplicates = list.ToList();
143             list.AddRange(list);
144             list.AddRange(list);
145             list.AddRange(list);
146 
147             Assert.All(Enumerable.Range(0, count), i =>
148             {
149                 Assert.All(Enumerable.Range(0, 4), j =>
150                 {
151                     int expectedIndex = (j * count) + i;
152                     Assert.Equal(expectedIndex, list.IndexOf(withoutDuplicates[i], (count * j)));
153                     Assert.Equal(expectedIndex, list.IndexOf(withoutDuplicates[i], (count * j), count));
154                 });
155             });
156         }
157 
158         [Theory]
159         [MemberData(nameof(ValidCollectionSizes))]
LastIndexOf_int_OrderIsCorrectWithManyDuplicates(int count)160         public void LastIndexOf_int_OrderIsCorrectWithManyDuplicates(int count)
161         {
162             List<T> list = GenericListFactory(count);
163             List<T> withoutDuplicates = list.ToList();
164             list.AddRange(list);
165             list.AddRange(list);
166             list.AddRange(list);
167 
168             Assert.All(Enumerable.Range(0, count), i =>
169             {
170                 Assert.All(Enumerable.Range(0, 4), j =>
171                 {
172                     int expectedIndex = (j * count) + i;
173                     Assert.Equal(expectedIndex, list.LastIndexOf(withoutDuplicates[i], (count * (j + 1)) - 1));
174                     Assert.Equal(expectedIndex, list.LastIndexOf(withoutDuplicates[i], (count * (j + 1)) - 1, count));
175                 });
176             });
177         }
178 
179         [Theory]
180         [MemberData(nameof(ValidCollectionSizes))]
IndexOf_int_OutOfRangeExceptions(int count)181         public void IndexOf_int_OutOfRangeExceptions(int count)
182         {
183             List<T> list = GenericListFactory(count);
184             T element = CreateT(234);
185             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, count + 1)); //"Expect ArgumentOutOfRangeException for index greater than length of list.."
186             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, count + 10)); //"Expect ArgumentOutOfRangeException for index greater than length of list.."
187             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, -1)); //"Expect ArgumentOutOfRangeException for negative index."
188             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, int.MinValue)); //"Expect ArgumentOutOfRangeException for negative index."
189         }
190 
191         [Theory]
192         [MemberData(nameof(ValidCollectionSizes))]
IndexOf_int_int_OutOfRangeExceptions(int count)193         public void IndexOf_int_int_OutOfRangeExceptions(int count)
194         {
195             List<T> list = GenericListFactory(count);
196             T element = CreateT(234);
197             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, count, 1)); //"ArgumentOutOfRangeException expected on index larger than array."
198             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, count + 1, 1)); //"ArgumentOutOfRangeException expected  on index larger than array."
199             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, 0, count + 1)); //"ArgumentOutOfRangeException expected  on count larger than array."
200             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, count / 2, count / 2 + 2)); //"ArgumentOutOfRangeException expected.."
201             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, 0, count + 1)); //"ArgumentOutOfRangeException expected  on count larger than array."
202             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, 0, -1)); //"ArgumentOutOfRangeException expected on negative count."
203             Assert.Throws<ArgumentOutOfRangeException>(() => list.IndexOf(element, -1, 1)); //"ArgumentOutOfRangeException expected on negative index."
204         }
205 
206         [Theory]
207         [MemberData(nameof(ValidCollectionSizes))]
LastIndexOf_int_OutOfRangeExceptions(int count)208         public void LastIndexOf_int_OutOfRangeExceptions(int count)
209         {
210             List<T> list = GenericListFactory(count);
211             T element = CreateT(234);
212             Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, count)); //"ArgumentOutOfRangeException expected."
213             if (count == 0)  // IndexOf with a 0 count List is special cased to return -1.
214                 Assert.Equal(-1, list.LastIndexOf(element, -1));
215             else
216                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, -1));
217         }
218 
219         [Theory]
220         [MemberData(nameof(ValidCollectionSizes))]
LastIndexOf_int_int_OutOfRangeExceptions(int count)221         public void LastIndexOf_int_int_OutOfRangeExceptions(int count)
222         {
223             List<T> list = GenericListFactory(count);
224             T element = CreateT(234);
225 
226             if (count > 0)
227             {
228                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, 0, count + 1)); //"Expected ArgumentOutOfRangeException."
229                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, count / 2, count / 2 + 2)); //"Expected ArgumentOutOfRangeException."
230                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, 0, count + 1)); //"Expected ArgumentOutOfRangeException."
231                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, 0, -1)); //"Expected ArgumentOutOfRangeException."
232                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, -1, count)); //"Expected ArgumentOutOfRangeException."
233                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, -1, 1)); //"Expected ArgumentOutOfRangeException."                Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, count, 0)); //"Expected ArgumentOutOfRangeException."
234                 Assert.Throws<ArgumentOutOfRangeException>(() => list.LastIndexOf(element, count, 1)); //"Expected ArgumentOutOfRangeException."
235             }
236             else // IndexOf with a 0 count List is special cased to return -1.
237             {
238                 Assert.Equal(-1, list.LastIndexOf(element, 0, count + 1));
239                 Assert.Equal(-1, list.LastIndexOf(element, count / 2, count / 2 + 2));
240                 Assert.Equal(-1, list.LastIndexOf(element, 0, count + 1));
241                 Assert.Equal(-1, list.LastIndexOf(element, 0, -1));
242                 Assert.Equal(-1, list.LastIndexOf(element, -1, count));
243                 Assert.Equal(-1, list.LastIndexOf(element, -1, 1));
244                 Assert.Equal(-1, list.LastIndexOf(element, count, 0));
245                 Assert.Equal(-1, list.LastIndexOf(element, count, 1));
246             }
247         }
248 
249         #endregion
250     }
251 }
252