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;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Runtime.CompilerServices;
10 using Xunit;
11 
12 namespace System.Linq.Expressions.Tests
13 {
14     public class ReadOnlyCollectionBuilderTests
15     {
16         [Fact]
ReadOnlyCollectionBuilder_Ctor_Default()17         public void ReadOnlyCollectionBuilder_Ctor_Default()
18         {
19             var rocb = new ReadOnlyCollectionBuilder<int>();
20 
21             Assert.Equal(0, rocb.Capacity);
22 
23             AssertEmpty(rocb);
24         }
25 
26         [Theory]
27         [InlineData(-2)]
28         [InlineData(-1)]
ReadOnlyCollectionBuilder_Ctor_Capacity_ArgumentChecking(int capacity)29         public void ReadOnlyCollectionBuilder_Ctor_Capacity_ArgumentChecking(int capacity)
30         {
31             AssertExtensions.Throws<ArgumentOutOfRangeException>("capacity", () => new ReadOnlyCollectionBuilder<int>(capacity));
32         }
33 
34         [Theory]
35         [InlineData(0)]
36         [InlineData(1)]
37         [InlineData(2)]
ReadOnlyCollectionBuilder_Ctor_Capacity(int capacity)38         public void ReadOnlyCollectionBuilder_Ctor_Capacity(int capacity)
39         {
40             var rocb = new ReadOnlyCollectionBuilder<int>(capacity);
41 
42             Assert.Equal(capacity, rocb.Capacity);
43 
44             AssertEmpty(rocb);
45         }
46 
47         [Fact]
ReadOnlyCollectionBuilder_Ctor_Collection_ArgumentChecking()48         public void ReadOnlyCollectionBuilder_Ctor_Collection_ArgumentChecking()
49         {
50             AssertExtensions.Throws<ArgumentNullException>("collection", () => new ReadOnlyCollectionBuilder<int>(null));
51         }
52 
53         [Theory]
54         [MemberData(nameof(InitialCollections))]
ReadOnlyCollectionBuilder_Ctor_Collection(IEnumerable<int> collection)55         public void ReadOnlyCollectionBuilder_Ctor_Collection(IEnumerable<int> collection)
56         {
57             var rocb = new ReadOnlyCollectionBuilder<int>(collection);
58 
59             Assert.Equal(collection.Count(), rocb.Count);
60             Assert.True(collection.SequenceEqual(rocb));
61 
62             int[] array = rocb.ToArray();
63 
64             Assert.Equal(collection.Count(), array.Length);
65             Assert.True(collection.SequenceEqual(array));
66 
67             ReadOnlyCollection<int> roc = rocb.ToReadOnlyCollection();
68 
69             Assert.Equal(collection.Count(), roc.Count);
70             Assert.True(collection.SequenceEqual(roc));
71 
72             AssertEmpty(rocb); // ToReadOnlyCollection behavior is to empty the builder
73         }
74 
75         [Fact]
ReadOnlyCollectionBuilder_Capacity1()76         public void ReadOnlyCollectionBuilder_Capacity1()
77         {
78             var rocb = new ReadOnlyCollectionBuilder<int>();
79 
80             Assert.Equal(0, rocb.Capacity);
81             Assert.Equal(0, rocb.Count);
82 
83             Assert.Throws<ArgumentOutOfRangeException>(() => rocb.Capacity = -1);
84 
85             rocb.Capacity = 0;
86 
87             Assert.Equal(0, rocb.Capacity);
88             Assert.Equal(0, rocb.Count);
89 
90             rocb.Capacity = 1;
91 
92             Assert.Equal(1, rocb.Capacity);
93             Assert.Equal(0, rocb.Count);
94 
95             rocb.Capacity = 2;
96 
97             Assert.Equal(2, rocb.Capacity);
98             Assert.Equal(0, rocb.Count);
99         }
100 
101         [Fact]
ReadOnlyCollectionBuilder_Capacity2()102         public void ReadOnlyCollectionBuilder_Capacity2()
103         {
104             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1 });
105 
106             Assert.Equal(1, rocb.Capacity);
107             Assert.Equal(1, rocb.Count);
108 
109             AssertExtensions.Throws<ArgumentOutOfRangeException>("value", () => { rocb.Capacity = 0; });
110             AssertExtensions.Throws<ArgumentOutOfRangeException>("value", () => { rocb.Capacity = -1; });
111 
112             rocb.Capacity = 1;
113 
114             Assert.Equal(1, rocb.Capacity);
115             Assert.Equal(1, rocb.Count);
116 
117             rocb.Capacity = 2;
118 
119             Assert.Equal(2, rocb.Capacity);
120             Assert.Equal(1, rocb.Count);
121         }
122 
123         [Fact]
ReadOnlyCollectionBuilder_Capacity3()124         public void ReadOnlyCollectionBuilder_Capacity3()
125         {
126             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1 });
127 
128             Assert.Equal(1, rocb.Capacity);
129             Assert.Equal(1, rocb.Count);
130 
131             rocb.RemoveAt(0);
132 
133             Assert.Equal(1, rocb.Capacity);
134             Assert.Equal(0, rocb.Count);
135 
136             rocb.Capacity = 0;
137 
138             Assert.Equal(0, rocb.Capacity);
139             Assert.Equal(0, rocb.Count);
140         }
141 
142         [Fact]
ReadOnlyCollectionBuilder_IList_IsReadOnly()143         public void ReadOnlyCollectionBuilder_IList_IsReadOnly()
144         {
145             IList rocb = new ReadOnlyCollectionBuilder<int>();
146 
147             Assert.False(rocb.IsReadOnly);
148         }
149 
150         [Fact]
ReadOnlyCollectionBuilder_ICollectionOfT_IsReadOnly()151         public void ReadOnlyCollectionBuilder_ICollectionOfT_IsReadOnly()
152         {
153             ICollection<int> rocb = new ReadOnlyCollectionBuilder<int>();
154 
155             Assert.False(rocb.IsReadOnly);
156         }
157 
158         [Fact]
ReadOnlyCollectionBuilder_IList_IsFixedSize()159         public void ReadOnlyCollectionBuilder_IList_IsFixedSize()
160         {
161             IList rocb = new ReadOnlyCollectionBuilder<int>();
162 
163             Assert.False(rocb.IsFixedSize);
164         }
165 
166         [Fact]
ReadOnlyCollectionBuilder_IList_IsSynchronized()167         public void ReadOnlyCollectionBuilder_IList_IsSynchronized()
168         {
169             ICollection rocb = new ReadOnlyCollectionBuilder<int>();
170 
171             Assert.False(rocb.IsSynchronized);
172         }
173 
174         [Fact]
ReadOnlyCollectionBuilder_ICollection_SyncRoot()175         public void ReadOnlyCollectionBuilder_ICollection_SyncRoot()
176         {
177             ICollection rocb = new ReadOnlyCollectionBuilder<int>();
178 
179             object root1 = rocb.SyncRoot;
180             Assert.NotNull(root1);
181 
182             object root2 = rocb.SyncRoot;
183             Assert.Same(root1, root2);
184         }
185 
186         [Fact]
ReadOnlyCollectionBuilder_IndexOf()187         public void ReadOnlyCollectionBuilder_IndexOf()
188         {
189             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 2, 3 });
190 
191             Assert.Equal(4, rocb.Count);
192 
193             Assert.Equal(0, rocb.IndexOf(1));
194             Assert.Equal(1, rocb.IndexOf(2));
195             Assert.Equal(3, rocb.IndexOf(3));
196 
197             Assert.InRange(rocb.IndexOf(0), int.MinValue, -1);
198             Assert.InRange(rocb.IndexOf(4), int.MinValue, -1);
199 
200             rocb.Capacity = 5;
201 
202             Assert.Equal(4, rocb.Count);
203             Assert.InRange(rocb.IndexOf(0), int.MinValue, -1); // No default values leak in through underlying array
204 
205             Assert.True(rocb.Remove(3));
206 
207             Assert.Equal(3, rocb.Count);
208             Assert.InRange(rocb.IndexOf(0), int.MinValue, -1); // No default values leak in through underlying array
209             Assert.InRange(rocb.IndexOf(3), int.MinValue, -1);
210         }
211 
212         [Fact]
ReadOnlyCollectionBuilder_IList_IndexOf()213         public void ReadOnlyCollectionBuilder_IList_IndexOf()
214         {
215             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 2, 3 });
216             IList list = rocb;
217 
218             Assert.Equal(4, list.Count);
219 
220             Assert.Equal(0, list.IndexOf(1));
221             Assert.Equal(1, list.IndexOf(2));
222             Assert.Equal(3, list.IndexOf(3));
223 
224             Assert.InRange(list.IndexOf(0), int.MinValue, -1);
225             Assert.InRange(list.IndexOf(4), int.MinValue, -1);
226 
227             rocb.Capacity = 5;
228 
229             Assert.Equal(4, list.Count);
230             Assert.InRange(list.IndexOf(0), int.MinValue, -1); // No default values leak in through underlying array
231 
232             list.Remove(3);
233 
234             Assert.Equal(3, list.Count);
235             Assert.InRange(list.IndexOf(0), int.MinValue, -1); // No default values leak in through underlying array
236             Assert.InRange(list.IndexOf(3), int.MinValue, -1);
237 
238             Assert.InRange(list.IndexOf("bar"), int.MinValue, -1);
239             Assert.InRange(list.IndexOf(null), int.MinValue, -1);
240         }
241 
242         [Fact]
ReadOnlyCollectionBuilder_Insert()243         public void ReadOnlyCollectionBuilder_Insert()
244         {
245             var rocb = new ReadOnlyCollectionBuilder<int>();
246 
247             Assert.Equal(0, rocb.Count);
248 
249             Assert.Throws<ArgumentOutOfRangeException>(() => rocb.Insert(-1, 1));
250             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.Insert(1, 1));
251 
252             rocb.Insert(0, 1);
253 
254             Assert.True(new[] { 1 }.SequenceEqual(rocb));
255 
256             rocb.Insert(0, 2);
257 
258             Assert.True(new[] { 2, 1 }.SequenceEqual(rocb));
259 
260             rocb.Insert(0, 3);
261 
262             Assert.True(new[] { 3, 2, 1 }.SequenceEqual(rocb));
263 
264             rocb.Insert(1, 4);
265 
266             Assert.True(new[] { 3, 4, 2, 1 }.SequenceEqual(rocb));
267 
268             rocb.Insert(4, 5);
269 
270             Assert.True(new[] { 3, 4, 2, 1, 5 }.SequenceEqual(rocb));
271         }
272 
273         [Fact]
ReadOnlyCollectionBuilder_IList_Insert()274         public void ReadOnlyCollectionBuilder_IList_Insert()
275         {
276             IList rocb = new ReadOnlyCollectionBuilder<int>();
277 
278             Assert.Equal(0, rocb.Count);
279 
280             Assert.Throws<ArgumentOutOfRangeException>(() => rocb.Insert(-1, 1));
281             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.Insert(1, 1));
282 
283             AssertExtensions.Throws<ArgumentException>("value", () => rocb.Insert(1, "bar"));
284             AssertExtensions.Throws<ArgumentException>("value", () => rocb.Insert(1, null));
285 
286             rocb.Insert(0, 1);
287 
288             Assert.True(new[] { 1 }.SequenceEqual(rocb.Cast<int>()));
289 
290             rocb.Insert(0, 2);
291 
292             Assert.True(new[] { 2, 1 }.SequenceEqual(rocb.Cast<int>()));
293 
294             rocb.Insert(0, 3);
295 
296             Assert.True(new[] { 3, 2, 1 }.SequenceEqual(rocb.Cast<int>()));
297 
298             rocb.Insert(1, 4);
299 
300             Assert.True(new[] { 3, 4, 2, 1 }.SequenceEqual(rocb.Cast<int>()));
301 
302             rocb.Insert(4, 5);
303 
304             Assert.True(new[] { 3, 4, 2, 1, 5 }.SequenceEqual(rocb.Cast<int>()));
305         }
306 
307         [Fact]
ReadOnlyCollectionBuilder_RemoveAt()308         public void ReadOnlyCollectionBuilder_RemoveAt()
309         {
310             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3, 4 });
311 
312             Assert.True(new[] { 1, 2, 3, 4 }.SequenceEqual(rocb));
313 
314             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.RemoveAt(-1));
315             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.RemoveAt(4));
316 
317             rocb.RemoveAt(0);
318 
319             Assert.True(new[] { 2, 3, 4 }.SequenceEqual(rocb));
320             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.RemoveAt(3));
321 
322             rocb.RemoveAt(1);
323 
324             Assert.True(new[] { 2, 4 }.SequenceEqual(rocb));
325             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.RemoveAt(2));
326 
327             rocb.RemoveAt(1);
328 
329             Assert.True(new[] { 2 }.SequenceEqual(rocb));
330             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.RemoveAt(1));
331 
332             rocb.RemoveAt(0);
333 
334             Assert.Equal(0, rocb.Count);
335             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.RemoveAt(0));
336         }
337 
338         [Fact]
ReadOnlyCollectionBuilder_Remove()339         public void ReadOnlyCollectionBuilder_Remove()
340         {
341             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 2, 4 });
342 
343             Assert.True(new[] { 1, 2, 2, 4 }.SequenceEqual(rocb));
344 
345             Assert.False(rocb.Remove(0));
346 
347             Assert.True(new[] { 1, 2, 2, 4 }.SequenceEqual(rocb));
348 
349             Assert.True(rocb.Remove(2));
350 
351             Assert.True(new[] { 1, 2, 4 }.SequenceEqual(rocb));
352 
353             Assert.True(rocb.Remove(1));
354 
355             Assert.True(new[] { 2, 4 }.SequenceEqual(rocb));
356 
357             Assert.True(rocb.Remove(4));
358 
359             Assert.True(new[] { 2 }.SequenceEqual(rocb));
360 
361             Assert.True(rocb.Remove(2));
362 
363             Assert.Equal(0, rocb.Count);
364         }
365 
366         [Fact]
ReadOnlyCollectionBuilder_IList_Remove()367         public void ReadOnlyCollectionBuilder_IList_Remove()
368         {
369             IList rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 2, 4 });
370 
371             Assert.True(new[] { 1, 2, 2, 4 }.SequenceEqual(rocb.Cast<int>()));
372 
373             rocb.Remove(0);
374             rocb.Remove("bar");
375             rocb.Remove(null);
376 
377             Assert.True(new[] { 1, 2, 2, 4 }.SequenceEqual(rocb.Cast<int>()));
378 
379             rocb.Remove(2);
380 
381             Assert.True(new[] { 1, 2, 4 }.SequenceEqual(rocb.Cast<int>()));
382 
383             rocb.Remove(1);
384 
385             Assert.True(new[] { 2, 4 }.SequenceEqual(rocb.Cast<int>()));
386 
387             rocb.Remove(4);
388 
389             Assert.True(new[] { 2 }.SequenceEqual(rocb.Cast<int>()));
390 
391             rocb.Remove(2);
392 
393             Assert.Equal(0, rocb.Count);
394         }
395 
396         [Fact]
ReadOnlyCollectionBuilder_Indexer_Get()397         public void ReadOnlyCollectionBuilder_Indexer_Get()
398         {
399             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3, 4 });
400 
401             // CONSIDER: Throw ArgumentOutOfRangeException instead, see https://github.com/dotnet/corefx/issues/14059
402             Assert.Throws<IndexOutOfRangeException>(() => rocb[-1]);
403             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb[4]);
404 
405             Assert.Equal(1, rocb[0]);
406             Assert.Equal(2, rocb[1]);
407             Assert.Equal(3, rocb[2]);
408             Assert.Equal(4, rocb[3]);
409         }
410 
411         [Fact]
ReadOnlyCollectionBuilder_Indexer_IList_Get()412         public void ReadOnlyCollectionBuilder_Indexer_IList_Get()
413         {
414             IList rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3, 4 });
415 
416             // CONSIDER: Throw ArgumentOutOfRangeException instead, see https://github.com/dotnet/corefx/issues/14059
417             Assert.Throws<IndexOutOfRangeException>(() => rocb[-1]);
418             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb[4]);
419 
420             Assert.Equal(1, rocb[0]);
421             Assert.Equal(2, rocb[1]);
422             Assert.Equal(3, rocb[2]);
423             Assert.Equal(4, rocb[3]);
424         }
425 
426         [Fact]
ReadOnlyCollectionBuilder_Indexer_Set()427         public void ReadOnlyCollectionBuilder_Indexer_Set()
428         {
429             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3, 4 });
430 
431             // CONSIDER: Throw ArgumentOutOfRangeException instead, see https://github.com/dotnet/corefx/issues/14059
432             Assert.Throws<IndexOutOfRangeException>(() => rocb[-1] = -1);
433             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb[4] = -1);
434 
435             rocb[0] = -1;
436             Assert.Equal(-1, rocb[0]);
437 
438             rocb[1] = -2;
439             Assert.Equal(-2, rocb[1]);
440 
441             rocb[2] = -3;
442             Assert.Equal(-3, rocb[2]);
443 
444             rocb[3] = -4;
445             Assert.Equal(-4, rocb[3]);
446         }
447 
448         [Fact]
ReadOnlyCollectionBuilder_Indexer_IList_Set()449         public void ReadOnlyCollectionBuilder_Indexer_IList_Set()
450         {
451             IList rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3, 4 });
452 
453             // CONSIDER: Throw ArgumentOutOfRangeException instead, see https://github.com/dotnet/corefx/issues/14059
454             Assert.Throws<IndexOutOfRangeException>(() => rocb[-1] = -1);
455             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb[4] = -1);
456 
457             AssertExtensions.Throws<ArgumentException>("value", () => rocb[0] = "bar");
458             AssertExtensions.Throws<ArgumentException>("value", () => rocb[0] = null);
459 
460             rocb[0] = -1;
461             Assert.Equal(-1, rocb[0]);
462 
463             rocb[1] = -2;
464             Assert.Equal(-2, rocb[1]);
465 
466             rocb[2] = -3;
467             Assert.Equal(-3, rocb[2]);
468 
469             rocb[3] = -4;
470             Assert.Equal(-4, rocb[3]);
471         }
472 
473         [Fact]
ReadOnlyCollectionBuilder_Add()474         public void ReadOnlyCollectionBuilder_Add()
475         {
476             var rocb = new ReadOnlyCollectionBuilder<int>();
477 
478             for (int i = 1; i <= 10; i++)
479             {
480                 rocb.Add(i);
481 
482                 Assert.True(Enumerable.Range(1, i).SequenceEqual(rocb));
483             }
484         }
485 
486         [Fact]
ReadOnlyCollectionBuilder_IList_Add()487         public void ReadOnlyCollectionBuilder_IList_Add()
488         {
489             IList rocb = new ReadOnlyCollectionBuilder<int>();
490 
491             for (int i = 1; i <= 10; i++)
492             {
493                 rocb.Add(i);
494 
495                 Assert.True(Enumerable.Range(1, i).SequenceEqual(rocb.Cast<int>()));
496             }
497 
498             AssertExtensions.Throws<ArgumentException>("value", () => rocb.Add(null));
499             AssertExtensions.Throws<ArgumentException>("value", () => rocb.Add("foo"));
500         }
501 
502         [Fact]
ReadOnlyCollectionBuilder_Clear1()503         public void ReadOnlyCollectionBuilder_Clear1()
504         {
505             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
506 
507             rocb.Clear();
508 
509             Assert.Equal(0, rocb.Count);
510         }
511 
512         [Fact]
ReadOnlyCollectionBuilder_Clear2()513         public void ReadOnlyCollectionBuilder_Clear2()
514         {
515             var rocb = new ReadOnlyCollectionBuilder<int>();
516 
517             rocb.Clear();
518 
519             Assert.Equal(0, rocb.Count);
520         }
521 
522         [Fact]
ReadOnlyCollectionBuilder_Contains1()523         public void ReadOnlyCollectionBuilder_Contains1()
524         {
525             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 2, 3 });
526 
527             Assert.True(rocb.Contains(1));
528             Assert.True(rocb.Contains(2));
529             Assert.True(rocb.Contains(3));
530 
531             Assert.False(rocb.Contains(-1));
532         }
533 
534         [Fact]
ReadOnlyCollectionBuilder_Contains2()535         public void ReadOnlyCollectionBuilder_Contains2()
536         {
537             var rocb = new ReadOnlyCollectionBuilder<string>(new[] { "bar", "foo", "qux" });
538 
539             Assert.True(rocb.Contains("bar"));
540             Assert.True(rocb.Contains("foo"));
541             Assert.True(rocb.Contains("qux"));
542 
543             Assert.False(rocb.Contains(null));
544             Assert.False(rocb.Contains("baz"));
545         }
546 
547         [Fact]
ReadOnlyCollectionBuilder_Contains3()548         public void ReadOnlyCollectionBuilder_Contains3()
549         {
550             var rocb = new ReadOnlyCollectionBuilder<string>(new[] { "bar", "foo", "qux", null });
551 
552             Assert.True(rocb.Contains("bar"));
553             Assert.True(rocb.Contains("foo"));
554             Assert.True(rocb.Contains("qux"));
555             Assert.True(rocb.Contains(null));
556 
557             Assert.False(rocb.Contains("baz"));
558         }
559 
560         [Fact]
ReadOnlyCollectionBuilder_IList_Contains1()561         public void ReadOnlyCollectionBuilder_IList_Contains1()
562         {
563             IList rocb = new ReadOnlyCollectionBuilder<string>(new[] { "bar", "foo", "qux", null });
564 
565             Assert.True(rocb.Contains("bar"));
566             Assert.True(rocb.Contains("foo"));
567             Assert.True(rocb.Contains("qux"));
568             Assert.True(rocb.Contains(null));
569 
570             Assert.False(rocb.Contains("baz"));
571             Assert.False(rocb.Contains(42));
572         }
573 
574         [Fact]
ReadOnlyCollectionBuilder_IList_Contains2()575         public void ReadOnlyCollectionBuilder_IList_Contains2()
576         {
577             IList rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
578 
579             Assert.True(rocb.Contains(1));
580             Assert.True(rocb.Contains(2));
581             Assert.True(rocb.Contains(3));
582 
583             Assert.False(rocb.Contains("baz"));
584             Assert.False(rocb.Contains(0));
585             Assert.False(rocb.Contains(null));
586         }
587 
588         [Fact]
ReadOnlyCollectionBuilder_Reverse()589         public void ReadOnlyCollectionBuilder_Reverse()
590         {
591             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
592 
593             rocb.Reverse();
594 
595             Assert.True(new[] { 3, 2, 1 }.SequenceEqual(rocb));
596         }
597 
598         [Fact]
ReadOnlyCollectionBuilder_Reverse_Range_ArgumentChecking()599         public void ReadOnlyCollectionBuilder_Reverse_Range_ArgumentChecking()
600         {
601             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
602 
603             AssertExtensions.Throws<ArgumentOutOfRangeException>("index", () => rocb.Reverse(-1, 1));
604             AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => rocb.Reverse(1, -1));
605 
606             // CONSIDER: Throw ArgumentException just like List<T> does, see https://github.com/dotnet/corefx/issues/14059
607             // AssertExtensions.Throws<ArgumentException>(null, () => rocb.Reverse(3, 1));
608             // AssertExtensions.Throws<ArgumentException>(null, () => rocb.Reverse(1, 3));
609             // AssertExtensions.Throws<ArgumentException>(null, () => rocb.Reverse(2, 2));
610             // AssertExtensions.Throws<ArgumentException>(null, () => rocb.Reverse(3, 1));
611         }
612 
613         [Fact]
ReadOnlyCollectionBuilder_Reverse_Range()614         public void ReadOnlyCollectionBuilder_Reverse_Range()
615         {
616             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3, 4, 5 });
617 
618             rocb.Reverse(1, 3);
619 
620             Assert.True(new[] { 1, 4, 3, 2, 5 }.SequenceEqual(rocb));
621         }
622 
623         [Theory]
624         [MemberData(nameof(Lengths))]
ReadOnlyCollectionBuilder_ToArray(int length)625         public void ReadOnlyCollectionBuilder_ToArray(int length)
626         {
627             var rocb = new ReadOnlyCollectionBuilder<int>();
628 
629             for (int i = 0; i < length; i++)
630             {
631                 rocb.Add(i);
632             }
633 
634             int[] array = rocb.ToArray();
635 
636             Assert.True(Enumerable.Range(0, length).SequenceEqual(array));
637         }
638 
639         [Theory]
640         [MemberData(nameof(Lengths))]
ReadOnlyCollectionBuilder_ToReadOnlyCollection(int length)641         public void ReadOnlyCollectionBuilder_ToReadOnlyCollection(int length)
642         {
643             var rocb = new ReadOnlyCollectionBuilder<int>();
644 
645             for (int i = 0; i < length; i++)
646             {
647                 rocb.Add(i);
648             }
649 
650             ReadOnlyCollection<int> collection = rocb.ToReadOnlyCollection();
651 
652             Assert.Equal(length, collection.Count);
653 
654             Assert.True(Enumerable.Range(0, length).SequenceEqual(collection));
655 
656             AssertEmpty(rocb);
657         }
658 
659         [Theory]
660         [MemberData(nameof(Lengths))]
ReadOnlyCollectionBuilder_GetEnumerator(int length)661         public void ReadOnlyCollectionBuilder_GetEnumerator(int length)
662         {
663             var rocb = new ReadOnlyCollectionBuilder<int>();
664 
665             for (int i = 0; i < length; i++)
666             {
667                 rocb.Add(i);
668             }
669 
670             for (int j = 0; j < 2; j++)
671             {
672                 IEnumerator<int> enumerator = rocb.GetEnumerator();
673 
674                 // NB: Current property on generic enumerator doesn't throw; this is consistent with List<T>.
675 
676                 for (int i = 0; i < length; i++)
677                 {
678                     Assert.True(enumerator.MoveNext());
679                     Assert.Equal(i, enumerator.Current);
680                     Assert.Equal(i, ((IEnumerator)enumerator).Current);
681 
682                     enumerator.Dispose(); // NB: Similar to List<T>, calling Dispose does not have an effect here
683                 }
684 
685                 Assert.False(enumerator.MoveNext());
686                 Assert.False(enumerator.MoveNext());
687 
688                 // NB: Current property on generic enumerator doesn't throw; this is consistent with List<T>.
689 
690                 enumerator.Reset();
691             }
692         }
693 
694         [Theory]
695         [MemberData(nameof(Lengths))]
ReadOnlyCollectionBuilder_IEnumerable_GetEnumerator(int length)696         public void ReadOnlyCollectionBuilder_IEnumerable_GetEnumerator(int length)
697         {
698             var rocb = new ReadOnlyCollectionBuilder<int>();
699 
700             for (int i = 0; i < length; i++)
701             {
702                 rocb.Add(i);
703             }
704 
705             for (int j = 0; j < 2; j++)
706             {
707                 IEnumerator enumerator = ((IEnumerable)rocb).GetEnumerator();
708 
709                 Assert.Throws<InvalidOperationException>(() => enumerator.Current);
710 
711                 for (int i = 0; i < length; i++)
712                 {
713                     Assert.True(enumerator.MoveNext());
714                     Assert.Equal(i, enumerator.Current);
715                     Assert.Equal(i, ((IEnumerator)enumerator).Current);
716 
717                     ((IDisposable)enumerator).Dispose(); // NB: Similar to List<T>, calling Dispose does not have an effect here
718                 }
719 
720                 Assert.False(enumerator.MoveNext());
721                 Assert.False(enumerator.MoveNext());
722 
723                 Assert.Throws<InvalidOperationException>(() => enumerator.Current);
724 
725                 enumerator.Reset();
726             }
727         }
728 
729         [Theory]
730         [MemberData(nameof(Versioning))]
ReadOnlyCollectionBuilder_IEnumeratorOfT_Versioning_MoveNext(int index, Action<ReadOnlyCollectionBuilder<int>> edit)731         public void ReadOnlyCollectionBuilder_IEnumeratorOfT_Versioning_MoveNext(int index, Action<ReadOnlyCollectionBuilder<int>> edit)
732         {
733             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
734 
735             IEnumerator<int> enumerator = rocb.GetEnumerator();
736 
737             Assert.True(enumerator.MoveNext());
738 
739             edit(rocb);
740 
741             Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
742         }
743 
744         [Theory]
745         [MemberData(nameof(Versioning))]
ReadOnlyCollectionBuilder_IEnumeratorOfT_Versioning_Reset(int index, Action<ReadOnlyCollectionBuilder<int>> edit)746         public void ReadOnlyCollectionBuilder_IEnumeratorOfT_Versioning_Reset(int index, Action<ReadOnlyCollectionBuilder<int>> edit)
747         {
748             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
749 
750             IEnumerator<int> enumerator = rocb.GetEnumerator();
751 
752             Assert.True(enumerator.MoveNext());
753 
754             edit(rocb);
755 
756             Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
757         }
758 
759         [Theory]
760         [MemberData(nameof(Versioning))]
ReadOnlyCollectionBuilder_IEnumerator_Versioning_MoveNext(int index, Action<ReadOnlyCollectionBuilder<int>> edit)761         public void ReadOnlyCollectionBuilder_IEnumerator_Versioning_MoveNext(int index, Action<ReadOnlyCollectionBuilder<int>> edit)
762         {
763             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
764 
765             IEnumerator enumerator = ((IEnumerable)rocb).GetEnumerator();
766 
767             Assert.True(enumerator.MoveNext());
768 
769             edit(rocb);
770 
771             Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
772         }
773 
774         [Theory]
775         [MemberData(nameof(Versioning))]
ReadOnlyCollectionBuilder_IEnumerator_Versioning_Reset(int index, Action<ReadOnlyCollectionBuilder<int>> edit)776         public void ReadOnlyCollectionBuilder_IEnumerator_Versioning_Reset(int index, Action<ReadOnlyCollectionBuilder<int>> edit)
777         {
778             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
779 
780             IEnumerator enumerator = ((IEnumerable)rocb).GetEnumerator();
781 
782             Assert.True(enumerator.MoveNext());
783 
784             edit(rocb);
785 
786             Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
787         }
788 
789         [Fact]
ReadOnlyCollectionBuilder_CopyTo_ArgumentChecking()790         public void ReadOnlyCollectionBuilder_CopyTo_ArgumentChecking()
791         {
792             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
793 
794             Assert.Throws<ArgumentNullException>(() => rocb.CopyTo(null, 0));
795             Assert.Throws<ArgumentOutOfRangeException>(() => rocb.CopyTo(new int[3], -1));
796             AssertExtensions.Throws<ArgumentException>("destinationArray", () => rocb.CopyTo(new int[3], 3)); // NB: Consistent with List<T> behavior
797         }
798 
799         [Fact]
ReadOnlyCollectionBuilder_CopyTo1()800         public void ReadOnlyCollectionBuilder_CopyTo1()
801         {
802             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
803 
804             var array = new int[3];
805 
806             rocb.CopyTo(array, 0);
807 
808             Assert.True(new[] { 1, 2, 3 }.SequenceEqual(array));
809         }
810 
811         [Fact]
ReadOnlyCollectionBuilder_CopyTo2()812         public void ReadOnlyCollectionBuilder_CopyTo2()
813         {
814             var rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
815 
816             var array = new int[5] { 1, 2, 3, 4, 5 };
817 
818             rocb.CopyTo(array, 1);
819 
820             Assert.True(new[] { 1, 1, 2, 3, 5 }.SequenceEqual(array));
821         }
822 
823         [Fact]
ReadOnlyCollectionBuilder_ICollection_CopyTo_ArgumentChecking()824         public void ReadOnlyCollectionBuilder_ICollection_CopyTo_ArgumentChecking()
825         {
826             ICollection rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
827 
828             Assert.Throws<ArgumentNullException>(() => rocb.CopyTo(null, 0));
829             Assert.Throws<ArgumentOutOfRangeException>(() => rocb.CopyTo(new int[3], -1));
830             AssertExtensions.Throws<ArgumentException>("destinationArray", () => rocb.CopyTo(new int[3], 3)); // NB: Consistent with List<T> behavior
831             AssertExtensions.Throws<ArgumentException>(null, () => rocb.CopyTo(new int[3, 3], 0));
832 
833             // CONSIDER: Throw ArgumentException instead to be consistent with List<T>, see https://github.com/dotnet/corefx/issues/14059
834             Assert.Throws<ArrayTypeMismatchException>(() => rocb.CopyTo(new string[3], 0));
835         }
836 
837         [Fact]
ReadOnlyCollectionBuilder_ICollection_CopyTo1()838         public void ReadOnlyCollectionBuilder_ICollection_CopyTo1()
839         {
840             ICollection rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
841 
842             var array = new int[3];
843 
844             rocb.CopyTo(array, 0);
845 
846             Assert.True(new[] { 1, 2, 3 }.SequenceEqual(array));
847         }
848 
849         [Fact]
ReadOnlyCollectionBuilder_ICollection_CopyTo2()850         public void ReadOnlyCollectionBuilder_ICollection_CopyTo2()
851         {
852             ICollection rocb = new ReadOnlyCollectionBuilder<int>(new[] { 1, 2, 3 });
853 
854             var array = new int[5] { 1, 2, 3, 4, 5 };
855 
856             rocb.CopyTo(array, 1);
857 
858             Assert.True(new[] { 1, 1, 2, 3, 5 }.SequenceEqual(array));
859         }
860 
AssertEmpty(ReadOnlyCollectionBuilder<T> rocb)861         private static void AssertEmpty<T>(ReadOnlyCollectionBuilder<T> rocb)
862         {
863             Assert.Equal(0, rocb.Count);
864 
865             Assert.False(rocb.Contains(default(T)));
866             Assert.False(rocb.Remove(default(T)));
867             Assert.InRange(rocb.IndexOf(default(T)), int.MinValue, -1);
868 
869             IEnumerator<T> e = rocb.GetEnumerator();
870             Assert.False(e.MoveNext());
871         }
872 
InitialCollections()873         private static IEnumerable<object[]> InitialCollections() =>
874             new IEnumerable<int>[]
875             {
876                 new int[0],
877                 new int[] { 1 },
878                 new int[] { 1, 2 },
879                 new int[] { 1, 2, 3 },
880                 new int[] { 1, 2, 3, 4 },
881                 new int[] { 1, 2, 3, 4, 5 },
882 
883                 new List<int>(),
884                 new List<int>() { 1 },
885                 new List<int>() { 1, 2 },
886                 new List<int>() { 1, 2, 3 },
887                 new List<int>() { 1, 2, 3, 4 },
888                 new List<int>() { 1, 2, 3, 4, 5 },
889 
890                 Enumerable.Empty<int>(),
891                 Enumerable.Range(1, 1),
892                 Enumerable.Range(1, 2),
893                 Enumerable.Range(1, 3),
894                 Enumerable.Range(1, 4),
895                 Enumerable.Range(1, 5),
896             }.Select(x => new object[] { x });
897 
Lengths()898         private static IEnumerable<object[]> Lengths() => Enumerable.Range(0, 10).Select(i => new object[] { i });
899 
Versioning()900         private static IEnumerable<object[]> Versioning() =>
901             new Action<ReadOnlyCollectionBuilder<int>>[]
902             {
903                 e => e.Add(1),
904                 e => ((IList)e).Add(1),
905                 e => e[0] = 1,
906                 e => ((IList)e)[0] = 1,
907                 e => e.Insert(0, 1),
908                 e => ((IList)e).Insert(0, 1),
909                 e => e.Remove(1),
910                 e => ((IList)e).Remove(1),
911                 e => e.RemoveAt(0),
912                 e => e.Reverse(),
913             }.Select((x, i) => new object[] { i, x });
914     }
915 }
916