Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 1457ea6

Browse files
committed
[WIP] Cleanup and almost make tests compile
1 parent a787767 commit 1457ea6

File tree

7 files changed

+150
-123
lines changed

7 files changed

+150
-123
lines changed

src/System.Linq/src/System/Linq/Skip.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,17 @@ private static IEnumerable<TSource> SkipLastIterator<TSource>(IEnumerable<TSourc
152152
{
153153
if (queue.Count == count)
154154
{
155-
yield return queue.Dequeue();
155+
do
156+
{
157+
yield return queue.Dequeue();
158+
queue.Enqueue(e.Current);
159+
}
160+
while (e.MoveNext());
161+
}
162+
else
163+
{
164+
queue.Enqueue(e.Current);
156165
}
157-
158-
queue.Enqueue(e.Current);
159166
}
160167
}
161168
}

src/System.Linq/tests/EnumerableTests.cs

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -285,29 +285,48 @@ protected static List<Func<IEnumerable<T>, IEnumerable<T>>> IdentityTransforms<T
285285
e => e.Where(i => true)
286286
};
287287
}
288-
289-
protected class DelegateBasedEnumerator<T> : IEnumerator<T>
288+
289+
protected sealed class DelegateIterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
290290
{
291-
public Func<bool> MoveNextWorker { get; set; }
292-
public Func<T> CurrentWorker { get; set; }
293-
public Action DisposeWorker { get; set; }
294-
public Func<object> NonGenericCurrentWorker { get; set; }
295-
public Action ResetWorker { get; set; }
296-
297-
public T Current => CurrentWorker();
298-
public bool MoveNext() => MoveNextWorker();
299-
public void Dispose() => DisposeWorker();
300-
void IEnumerator.Reset() => ResetWorker();
301-
object IEnumerator.Current => NonGenericCurrentWorker();
302-
}
291+
private readonly Func<IEnumerator<TSource>> _getEnumerator;
292+
private readonly Func<bool> _moveNext;
293+
private readonly Func<TSource> _current;
294+
private readonly Func<IEnumerator> _explicitGetEnumerator;
295+
private readonly Func<object> _explicitCurrent;
296+
private readonly Action _reset;
297+
private readonly Action _dispose;
298+
299+
public DelegateIterator(
300+
Func<IEnumerator<TSource>> getEnumerator = null,
301+
Func<bool> moveNext = null,
302+
Func<TSource> current = null,
303+
Func<IEnumerator> explicitGetEnumerator = null,
304+
Func<object> explicitCurrent = null,
305+
Action reset = null,
306+
Action dispose = null)
307+
{
308+
_getEnumerator = getEnumerator ?? (() => { throw new NotImplementedException(); });
309+
_moveNext = moveNext ?? (() => { throw new NotImplementedException(); });
310+
_current = current ?? (() => { throw new NotImplementedException(); });
311+
_explicitGetEnumerator = explicitGetEnumerator ?? (() => { throw new NotImplementedException(); });
312+
_explicitCurrent = explicitCurrent ?? (() => { throw new NotImplementedException(); });
313+
_reset = reset ?? (() => { throw new NotImplementedException(); });
314+
_dispose = dispose ?? (() => { throw new NotImplementedException(); });
315+
}
303316

304-
protected class DelegateBasedEnumerable<T> : IEnumerable<T>
305-
{
306-
public Func<IEnumerator<T>> GetEnumeratorWorker { get; set; }
307-
public Func<IEnumerator> NonGenericGetEnumeratorWorker { get; set; }
317+
public IEnumerator<TSource> GetEnumerator() => _getEnumerator();
308318

309-
public IEnumerator<T> GetEnumerator() => GetEnumeratorWorker();
310-
IEnumerator IEnumerable.GetEnumerator() => NonGenericGetEnumeratorWorker();
319+
public bool MoveNext() => _moveNext();
320+
321+
public TSource Current => _current();
322+
323+
IEnumerator IEnumerable.GetEnumerator() => _explicitGetEnumerator();
324+
325+
object IEnumerator.Current => _explicitCurrent();
326+
327+
void IEnumerator.Reset() => _reset();
328+
329+
void IDisposable.Dispose() => _dispose();
311330
}
312331
}
313332
}

src/System.Linq/tests/SelectManyTests.cs

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -409,32 +409,20 @@ public void DisposeAfterEnumeration(int sourceLength, int subLength)
409409
bool sourceDisposed = false;
410410
bool[] subCollectionDisposed = new bool[sourceLength];
411411

412-
var sourceEnumerator = new DelegateBasedEnumerator<int>
413-
{
414-
MoveNextWorker = () => ++sourceState <= sourceLength,
415-
CurrentWorker = () => 0,
416-
DisposeWorker = () => sourceDisposed = true
417-
};
418-
419-
var source = new DelegateBasedEnumerable<int>
420-
{
421-
GetEnumeratorWorker = () => sourceEnumerator
422-
};
412+
DelegateIterator<int> source = null;
413+
source = new DelegateIterator<int>(
414+
getEnumerator: () => source,
415+
moveNext: () => ++sourceState <= sourceLength,
416+
current: () => 0,
417+
dispose: () => sourceDisposed = true);
418+
419+
DelegateIterator<int> subCollection = null;
420+
subCollection = new DelegateIterator<int>(
421+
getEnumerator: () => subCollection,
422+
moveNext: () => ++subState[subIndex] <= subLength, // Return true `subLength` times.
423+
current: () => subState[subIndex],
424+
dispose: () => subCollectionDisposed[subIndex++] = true); // Record that Dispose was called, and move on to the next index.
423425

424-
var subEnumerator = new DelegateBasedEnumerator<int>
425-
{
426-
// MoveNext: Return true subLength times.
427-
// Dispose: Record that Dispose was called & move to the next index.
428-
MoveNextWorker = () => ++subState[subIndex] <= subLength,
429-
CurrentWorker = () => subState[subIndex],
430-
DisposeWorker = () => subCollectionDisposed[subIndex++] = true
431-
};
432-
433-
var subCollection = new DelegateBasedEnumerable<int>
434-
{
435-
GetEnumeratorWorker = () => subEnumerator
436-
};
437-
438426
var iterator = source.SelectMany(_ => subCollection);
439427

440428
int index = 0; // How much have we gone into the iterator?

src/System.Linq/tests/SkipLastTests.netcoreapp1.1.cs

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,83 +4,70 @@
44

55
using System.Collections.Generic;
66
using Xunit;
7+
using static System.Linq.Tests.SkipTakeData;
78

89
namespace System.Linq.Tests
910
{
1011
public class SkipLastTests : EnumerableTests
1112
{
1213
[Theory]
13-
[MemberData(nameof(SkipLastData))]
14+
[ClassData(typeof(SkipTakeData))]
1415
public void SkipLast(IEnumerable<int> source, int count)
1516
{
16-
foreach (IEnumerable<int> equivalent in IdentityTransforms<int>().Select(t => t(source)))
17+
Assert.All(IdentityTransforms<int>(), transform =>
1718
{
19+
IEnumerable<int> equivalent = transform(source);
20+
1821
IEnumerable<int> expected = equivalent.Reverse().Skip(count).Reverse();
1922
IEnumerable<int> actual = equivalent.SkipLast(count);
2023

2124
Assert.Equal(expected, actual);
22-
Assert.Equal(expected.Count(), actual.Count()); // Count may be optimized. The above assert does not imply this will pass.
25+
Assert.Equal(expected.Count(), actual.Count());
2326
Assert.Equal(expected, actual.ToArray());
2427
Assert.Equal(expected, actual.ToList());
2528

2629
Assert.Equal(expected.FirstOrDefault(), actual.FirstOrDefault());
2730
Assert.Equal(expected.LastOrDefault(), actual.LastOrDefault());
2831

29-
// Likewise, ElementAt may be optimized.
30-
int count = expected.Count();
31-
for (int i = 0; i < count; i++)
32+
Assert.All(Enumerable.Range(0, expected.Count()), index =>
3233
{
33-
Assert.Equal(expected.ElementAt(i), actual.ElementAt(i));
34-
}
34+
Assert.Equal(expected.ElementAt(index), actual.ElementAt(index));
35+
});
3536

3637
Assert.Equal(0, actual.ElementAtOrDefault(-1));
3738
Assert.Equal(0, actual.ElementAtOrDefault(actual.Count()));
38-
}
39-
}
40-
41-
public static IEnumerable<object[]> SkipLastData()
42-
{
43-
IEnumerable<int> counts = new[] { int.MinValue + 1, -1, 0, 1, 2, 3, 4, 5, int.MaxValue };
44-
IEnumerable<IEnumerable<int>> enumerables = from count in counts
45-
select Enumerable.Range(count, Math.Min(100, Math.Abs(count)));
46-
47-
return from count in counts
48-
from enumerable in enumerables
49-
select new object[] { count, enumerable };
39+
});
5040
}
5141

5242
[Theory]
53-
[InlineData(1)]
54-
[InlineData(3)]
55-
[InlineData(10)]
43+
[MemberData(nameof(EvaluationBehaviorData), MemberType = typeof(SkipTakeData))]
5644
public void EvaluationBehavior(int count)
5745
{
58-
// We want to make sure no more than `count` items are ever evaluated
59-
// ahead of the current position.
60-
// As an example, if [0, 1, 2, 3, 4, 5].SkipLast(2) is called, then we
61-
// should read in [0, 1, 2], yield 0, read in 3, yield 1, read in 4,
62-
// yield 2, etc.
63-
46+
// We want to make sure no more than `count` items are ever evaluated ahead of the current position.
47+
// As an example, if Enumerable.Range(1, 6).SkipLast(2) is called, then we should read in the first 3 items,
48+
// yield 1, read in 4, yield 2, and so on.
6449
int index = 0;
50+
int limit = count * 2;
51+
52+
DelegateIterator<int> source = null;
53+
source = new DelegateIterator<int>(
54+
getEnumerator: () => source,
55+
moveNext: () => ++index <= limit, // Stop once we go past the limit.
56+
current: () => index, // Yield from 1 up to the limit, inclusive.
57+
dispose: () => index = -1);
6558

66-
// Represents the range [1..2 * count].
67-
var enumerable = EphemeralSequence(new DelegateBasedEnumerator<int>
68-
{
69-
MoveNextWorker = () => ++index <= 2 * count,
70-
CurrentWorker = () => index
71-
});
72-
73-
IEnumerator<int> iterator = enumerable.SkipLast(count).GetEnumerator();
74-
Assert.Equal(0, index);
59+
IEnumerator<int> iterator = source.SkipLast(count).GetEnumerator();
60+
Assert.Equal(0, index); // Nothing should be done before MoveNext is called.
7561

76-
for (int i = 0; i < count; i++)
62+
for (int i = 1; i <= count; i++)
7763
{
7864
Assert.True(iterator.MoveNext());
7965
Assert.Equal(i, iterator.Current);
8066
Assert.Equal(count + i, index);
8167
}
8268

8369
Assert.False(iterator.MoveNext());
70+
Assert.Equal(-1, index);
8471
}
8572
}
8673
}

src/System.Linq/tests/SkipTakeData.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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;
6+
using System.Collections.Generic;
7+
8+
namespace System.Linq.Tests
9+
{
10+
public class SkipTakeData : IEnumerable<object[]>
11+
{
12+
private static IEnumerable<object[]> Data { get; } = CreateData();
13+
14+
private static IEnumerable<object[]> CreateData()
15+
{
16+
IEnumerable<int> sourceCounts = new[] { 1, 2, 3, 5, 8, 13, 55, 100, 250, 1000, 2500 };
17+
18+
IEnumerable<int> counts = new[] { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 100, 250, 500, 1000, 250000, int.MaxValue };
19+
counts = counts.Concat(counts.Select(c => -c)).Append(0);
20+
21+
return from sourceCount in sourceCounts
22+
let source = Enumerable.Range(0, sourceCount)
23+
from count in counts
24+
select new object[] { source, count };
25+
}
26+
27+
public IEnumerator<object[]> GetEnumerator() => Data.GetEnumerator();
28+
29+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
30+
31+
public static IEnumerable<object[]> EvaluationBehaviorData()
32+
{
33+
return Enumerable.Range(-1, 15).Select(count => new object[] { count });
34+
}
35+
}
36+
}

src/System.Linq/tests/System.Linq.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<Compile Include="Shuffler.cs" />
6060
<Compile Include="SingleOrDefaultTests.cs" />
6161
<Compile Include="SingleTests.cs" />
62+
<Compile Include="SkipTakeData.cs" />
6263
<Compile Include="SkipTests.cs" />
6364
<Compile Include="SkipWhileTests.cs" />
6465
<Compile Include="SumTests.cs" />

src/System.Linq/tests/TakeLastTests.netcoreapp1.1.cs

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,69 @@
44

55
using System.Collections.Generic;
66
using Xunit;
7+
using static System.Linq.Tests.SkipTakeData;
78

89
namespace System.Linq.Tests
910
{
1011
public class TakeLastTests : EnumerableTests
1112
{
13+
// TODO: RunOnce tests. Same for SkipLast.
14+
1215
[Theory]
13-
[MemberData(nameof(TakeLastData))]
16+
[ClassData(typeof(SkipTakeData))]
1417
public void TakeLast(IEnumerable<int> source, int count)
1518
{
16-
foreach (IEnumerable<int> equivalent in IdentityTransforms<int>().Select(t => t(source)))
19+
Assert.All(IdentityTransforms<int>(), transform =>
1720
{
21+
IEnumerable<int> equivalent = transform(source);
22+
1823
IEnumerable<int> expected = equivalent.Reverse().Take(count).Reverse();
1924
IEnumerable<int> actual = equivalent.TakeLast(count);
2025

2126
Assert.Equal(expected, actual);
22-
Assert.Equal(expected.Count(), actual.Count()); // Count may be optimized. The above assert does not imply this will pass.
27+
Assert.Equal(expected.Count(), actual.Count());
2328
Assert.Equal(expected, actual.ToArray());
2429
Assert.Equal(expected, actual.ToList());
2530

2631
Assert.Equal(expected.FirstOrDefault(), actual.FirstOrDefault());
2732
Assert.Equal(expected.LastOrDefault(), actual.LastOrDefault());
2833

29-
// Likewise, ElementAt may be optimized.
30-
int count = expected.Count();
31-
for (int i = 0; i < count; i++)
34+
Assert.All(Enumerable.Range(0, expected.Count()), index =>
3235
{
33-
Assert.Equal(expected.ElementAt(i), actual.ElementAt(i));
34-
}
36+
Assert.Equal(expected.ElementAt(index), actual.ElementAt(index));
37+
});
3538

3639
Assert.Equal(0, actual.ElementAtOrDefault(-1));
3740
Assert.Equal(0, actual.ElementAtOrDefault(actual.Count()));
38-
}
39-
}
40-
41-
public static IEnumerable<object[]> TakeLastData()
42-
{
43-
IEnumerable<int> counts = new[] { int.MinValue + 1, -1, 0, 1, 2, 3, 4, 5, int.MaxValue };
44-
IEnumerable<IEnumerable<int>> enumerables = from count in counts
45-
select Enumerable.Range(count, Math.Min(100, Math.Abs(count)));
46-
47-
return from count in counts
48-
from enumerable in enumerables
49-
select new object[] { count, enumerable };
41+
});
5042
}
5143

5244
[Theory]
53-
[InlineData(1)]
54-
[InlineData(3)]
55-
[InlineData(10)]
45+
[MemberData(nameof(EvaluationBehaviorData), MemberType = typeof(SkipTakeData))]
5646
public void EvaluationBehavior(int count)
5747
{
58-
// After the first `MoveNext` call to TakeLast's enumerator,
59-
// everything should be evaluated.
60-
6148
int index = 0;
49+
int limit = count * 2;
6250

63-
// Represents the range [1..2 * count].
64-
var enumerable = EphemeralSequence(new DelegateBasedEnumerator<int>
65-
{
66-
MoveNextWorker = () => ++index <= 2 * count,
67-
CurrentWorker = () => index
68-
});
51+
DelegateIterator<int> source = null;
52+
source = new DelegateIterator<int>(
53+
getEnumerator: () => source,
54+
moveNext: () => ++index <= limit, // Stop once we go past the limit.
55+
current: () => index, // Yield from 1 up to the limit, inclusive.
56+
dispose: () => index = -1);
6957

70-
IEnumerator<int> iterator = enumerable.TakeLast(count).GetEnumerator();
71-
Assert.Equal(0, index);
58+
IEnumerator<int> iterator = source.TakeLast(count).GetEnumerator();
59+
Assert.Equal(0, index); // Nothing should be done before MoveNext is called.
7260

73-
for (int i = 0; i < count; i++)
61+
for (int i = 1; i <= count; i++)
7462
{
7563
Assert.True(iterator.MoveNext());
7664
Assert.Equal(count + i, iterator.Current);
77-
Assert.Equal(2 * count, index);
65+
Assert.Equal(limit, index); // After the first MoveNext call, everything should be evaluated.
7866
}
7967

8068
Assert.False(iterator.MoveNext());
69+
Assert.Equal(-1, index);
8170
}
8271
}
8372
}

0 commit comments

Comments
 (0)