Skip to content

Commit c4a2c53

Browse files
committed
added tests for ReadOnlySpan.Split sequence
1 parent ad4c329 commit c4a2c53

File tree

3 files changed

+313
-1
lines changed

3 files changed

+313
-1
lines changed

tests/ReadOnlySpanTests.cs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,185 @@ static void AssertOptions(StringSplitOptions options)
364364
AssertOptions(_options);
365365
}
366366
}
367+
368+
[Fact]
369+
public void TestSplitSequence()
370+
{
371+
int[] integerArray = GenerateRandomIntegers(count, minValue, maxValue).ToArray();
372+
ReadOnlySpan<int> integerSpan = integerArray;
373+
374+
// source contains delimiter
375+
int startIndex = random.Next(integerArray.Length - 3);
376+
int[] integerSequenceDelimiter = integerArray[startIndex..(startIndex + 3)];
377+
AssertMethodResults(
378+
expected: Split(integerArray, integerSequenceDelimiter),
379+
actual: integerSpan.Split(integerSequenceDelimiter).ToSystemEnumerable(),
380+
source: integerArray,
381+
method: nameof(ReadOnlySpanExtensions.Split),
382+
args: ("delimiter", integerSequenceDelimiter)
383+
);
384+
385+
// source does not contain delimiter
386+
integerSequenceDelimiter[^1] = maxValue;
387+
AssertMethodResults(
388+
expected: Split(integerArray, integerSequenceDelimiter),
389+
actual: integerSpan.Split(integerSequenceDelimiter).ToSystemEnumerable(),
390+
source: integerArray,
391+
method: nameof(ReadOnlySpanExtensions.Split),
392+
args: ("delimiter", integerSequenceDelimiter)
393+
);
394+
395+
char[] charArray = GenerateRandomString(length).ToCharArray();
396+
ReadOnlySpan<char> charSpan = charArray;
397+
398+
// source contains delimiter
399+
startIndex = random.Next(charArray.Length - 3);
400+
char[] charSequenceDelimiter = charArray[startIndex..(startIndex + 3)];
401+
AssertMethodResults(
402+
expected: Split(charArray, charSequenceDelimiter),
403+
actual: charSpan.Split(charSequenceDelimiter).ToSystemEnumerable(),
404+
source: charArray,
405+
method: nameof(ReadOnlySpanExtensions.Split),
406+
args: ("delimiter", charSequenceDelimiter)
407+
);
408+
409+
// source does not contain delimiter
410+
charSequenceDelimiter[^1] = '!'; // the generated array only consists of lowercase letters and numbers
411+
AssertMethodResults(
412+
expected: Split(charArray, charSequenceDelimiter),
413+
actual: charSpan.Split(charSequenceDelimiter).ToSystemEnumerable(),
414+
source: charArray,
415+
method: nameof(ReadOnlySpanExtensions.Split),
416+
args: ("delimiter", charSequenceDelimiter)
417+
);
418+
}
419+
420+
[Fact]
421+
public void TestSplitSequenceWithCount()
422+
{
423+
int[] integerArray = GenerateRandomIntegers(count, minValue, maxValue).ToArray();
424+
ReadOnlySpan<int> integerSpan = integerArray;
425+
426+
// source contains delimiter
427+
int startIndex = random.Next(integerArray.Length - 3);
428+
int[] integerSequenceDelimiter = integerArray[startIndex..(startIndex + 3)];
429+
int countDelimiters = integerSpan.CountSequence(integerSequenceDelimiter);
430+
AssertMethodResults(
431+
expected: Split(integerArray, integerSequenceDelimiter, countDelimiters),
432+
actual: integerSpan.Split(integerSequenceDelimiter, countDelimiters).ToSystemEnumerable(),
433+
source: integerArray,
434+
method: nameof(ReadOnlySpanExtensions.Split),
435+
args: [("delimiter", integerSequenceDelimiter), ("count", countDelimiters)]
436+
);
437+
438+
// source does not contain delimiter
439+
integerSequenceDelimiter[^1] = maxValue;
440+
AssertMethodResults(
441+
expected: Split(integerArray, integerSequenceDelimiter, countDelimiters),
442+
actual: integerSpan.Split(integerSequenceDelimiter, countDelimiters).ToSystemEnumerable(),
443+
source: integerArray,
444+
method: nameof(ReadOnlySpanExtensions.Split),
445+
args: [("delimiter", integerSequenceDelimiter), ("count", countDelimiters)]
446+
);
447+
448+
char[] charArray = GenerateRandomString(length).ToCharArray();
449+
ReadOnlySpan<char> charSpan = charArray;
450+
451+
// source contains delimiter
452+
startIndex = random.Next(charArray.Length - 3);
453+
char[] charSequenceDelimiter = charArray[startIndex..(startIndex + 3)];
454+
countDelimiters = charSpan.CountSequence(charSequenceDelimiter);
455+
AssertMethodResults(
456+
expected: Split(charArray, charSequenceDelimiter, countDelimiters),
457+
actual: charSpan.Split(charSequenceDelimiter, countDelimiters).ToSystemEnumerable(),
458+
source: charArray,
459+
method: nameof(ReadOnlySpanExtensions.Split),
460+
args: [("delimiter", charSequenceDelimiter), ("count", countDelimiters)]
461+
);
462+
463+
// source does not contain delimiter
464+
charSequenceDelimiter[^1] = '!'; // the generated array only consists of lowercase letters and numbers
465+
AssertMethodResults(
466+
expected: Split(charArray, charSequenceDelimiter, countDelimiters),
467+
actual: charSpan.Split(charSequenceDelimiter, countDelimiters).ToSystemEnumerable(),
468+
source: charArray,
469+
method: nameof(ReadOnlySpanExtensions.Split),
470+
args: [("delimiter", charSequenceDelimiter), ("count", countDelimiters)]
471+
);
472+
}
473+
474+
[Fact]
475+
public void TestSplitSequenceString()
476+
{
477+
static void AssertOptions(StringSplitOptions options)
478+
{
479+
string @string = GenerateRandomString(length);
480+
ReadOnlySpan<char> charSpan = @string;
481+
482+
// source contains delimiter
483+
int startIndex = random.Next(@string.Length - 3);
484+
char[] charSequenceDelimiter = @string.AsSpan()[startIndex..(startIndex + 3)].ToArray();
485+
AssertMethodResults(
486+
expected: @string.Split(new string(charSequenceDelimiter), options),
487+
actual: charSpan.Split(charSequenceDelimiter, options).ToSystemEnumerable(),
488+
source: @string,
489+
method: nameof(ReadOnlySpanExtensions.Split),
490+
args: [("delimiter", charSequenceDelimiter), ("options", options)]
491+
);
492+
493+
// source does not contain delimiter
494+
charSequenceDelimiter[^1] = '!'; // the generated array only consists of lowercase letters and numbers
495+
AssertMethodResults(
496+
expected: @string.Split(new string(charSequenceDelimiter), options),
497+
actual: charSpan.Split(charSequenceDelimiter, options).ToSystemEnumerable(),
498+
source: @string,
499+
method: nameof(ReadOnlySpanExtensions.Split),
500+
args: [("delimiter", charSequenceDelimiter), ("options", options)]
501+
);
502+
}
503+
504+
foreach(StringSplitOptions _options in stringSplitOptions)
505+
{
506+
AssertOptions(_options);
507+
}
508+
}
509+
510+
[Fact]
511+
public void TestSplitSequenceStringWithCount()
512+
{
513+
static void AssertOptions(StringSplitOptions options)
514+
{
515+
string @string = GenerateRandomString(length);
516+
ReadOnlySpan<char> charSpan = @string;
517+
518+
// source contains delimiter
519+
int startIndex = random.Next(@string.Length - 3);
520+
char[] charSequenceDelimiter = @string.AsSpan()[startIndex..(startIndex + 3)].ToArray();
521+
int countDelimiters = @string.Count(new string(charSequenceDelimiter));
522+
AssertMethodResults(
523+
expected: @string.Split(new string(charSequenceDelimiter), countDelimiters, options),
524+
actual: charSpan.Split(charSequenceDelimiter, options, countDelimiters).ToSystemEnumerable(),
525+
source: @string,
526+
method: nameof(ReadOnlySpanExtensions.Split),
527+
args: [("delimiter", charSequenceDelimiter), ("options", options), ("count", countDelimiters)]
528+
);
529+
530+
// source does not contain delimiter
531+
charSequenceDelimiter[^1] = '!'; // the generated array only consists of lowercase letters and numbers
532+
AssertMethodResults(
533+
expected: @string.Split(new string(charSequenceDelimiter), countDelimiters, options),
534+
actual: charSpan.Split(charSequenceDelimiter, options, countDelimiters).ToSystemEnumerable(),
535+
source: @string,
536+
method: nameof(ReadOnlySpanExtensions.Split),
537+
args: [("delimiter", charSequenceDelimiter), ("options", options), ("count", countDelimiters)]
538+
);
539+
}
540+
541+
foreach(StringSplitOptions _options in stringSplitOptions)
542+
{
543+
AssertOptions(_options);
544+
}
545+
}
367546
}
368547

369548
public static class Facts

tests/TestHelper.cs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System.Collections;
22
using System.Diagnostics;
3+
using System.Globalization;
4+
using System.Linq;
35
using System.Text;
6+
using System.Text.RegularExpressions;
47

58
namespace Tests
69
{
@@ -230,6 +233,34 @@ public static int Count<T>(this Span<T> span, ReadOnlySpan<T> values) where T :
230233
return Count((ReadOnlySpan<T>)span, values);
231234
}
232235

236+
/// <summary>
237+
/// Counts the number of times any of the specified <paramref name="substring"/> occur in the <paramref name="string"/>.
238+
/// </summary>
239+
/// <param name="string">The string to search.</param>
240+
/// <param name="substring">The substring for which to search.</param>
241+
/// <returns>The number of times any of the <paramref name="substring"/> was found in the <paramref name="string"/>.</returns>
242+
public static int Count(this string @string, string substring)
243+
{
244+
#if NET7_0_OR_GREATER
245+
return Regex.Count(@string, substring);
246+
#else
247+
return Regex.Matches(@string, substring).Count;
248+
#endif
249+
}
250+
251+
/// <summary>Counts the number of times the specified <paramref name="value"/> subsequence occurs in the <paramref name="span"/>.</summary>
252+
/// <typeparam name="T">The element type of the span.</typeparam>
253+
/// <param name="span">The span to search.</param>
254+
/// <param name="value">The value subsequence for which to search.</param>
255+
/// <returns>The number of times <paramref name="value"/> subsequence was found in the <paramref name="span"/>.</returns>
256+
public static int CountSequence<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
257+
{
258+
string source = string.Join(",", span.ToArray());
259+
string substring = string.Join(",", value.ToArray());
260+
261+
return source.Count(substring);
262+
}
263+
233264
/// <summary>
234265
/// Splits a sequence into a maximum number of subsequences based on a specified delimiter.
235266
/// </summary>
@@ -275,7 +306,7 @@ public static IEnumerable<IEnumerable<T>> Split<T>(IEnumerable<T> source, T deli
275306
/// <typeparam name="T">The element type of the sequence.</typeparam>
276307
/// <param name="source">The sequence to be split.</param>
277308
/// <param name="delimiters">A <see cref="IEnumerable{T}"/> with the instances of <typeparamref name="T"/> that delimit the subsequences in <paramref name="source"/>.</param>
278-
/// <param name="count">The maximum number of splits. If zero, split on every occurence of <paramref name="delimiter"/>.</param>
309+
/// <param name="count">The maximum number of splits. If zero, split on every occurence of <paramref name="delimiters"/>.</param>
279310
/// <returns>A sequence of split subsequences.</returns>
280311
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is negative.</exception>
281312
public static IEnumerable<IEnumerable<T>> SplitAny<T>(IEnumerable<T> source, IEnumerable<T> delimiters, int count = 0) where T : IEquatable<T>
@@ -308,6 +339,60 @@ public static IEnumerable<IEnumerable<T>> SplitAny<T>(IEnumerable<T> source, IEn
308339
yield return segment;
309340
}
310341

342+
/// <summary>
343+
/// Splits a sequence into a maximum number of subsequences based on specified delimiter subsequence.
344+
/// </summary>
345+
/// <param name="source">The sequence to be split.</param>
346+
/// <param name="delimiter">A <see cref="IEnumerable{int}"/> that delimits the subsequences in <paramref name="source"/>.</param>
347+
/// <param name="count">The maximum number of splits. If zero, split on every occurence of <paramref name="delimiter"/>.</param>
348+
/// <returns>A sequence of split subsequences.</returns>
349+
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is negative.</exception>
350+
public static IEnumerable<IEnumerable<int>> Split(IEnumerable<int> source, IEnumerable<int> delimiter, int count = 0)
351+
{
352+
#if NET8_0_OR_GREATER
353+
ArgumentOutOfRangeException.ThrowIfNegative(count);
354+
#else
355+
if(count < 0)
356+
{
357+
throw new ArgumentOutOfRangeException(nameof(count));
358+
}
359+
#endif
360+
361+
string _source = string.Join(",", source);
362+
string _delimiter = string.Join(",", delimiter);
363+
364+
string[] splits = _source.Split(_delimiter, count == 0 ? int.MaxValue : count, StringSplitOptions.None);
365+
366+
return splits.Select(s => s.Trim(',').Split(',').Select(x => int.Parse(x, CultureInfo.InvariantCulture)));
367+
}
368+
369+
/// <summary>
370+
/// Splits a sequence into a maximum number of subsequences based on specified delimiter subsequence.
371+
/// </summary>
372+
/// <param name="source">The sequence to be split.</param>
373+
/// <param name="delimiter">A <see cref="IEnumerable{char}"/> that delimits the subsequences in <paramref name="source"/>.</param>
374+
/// <param name="count">The maximum number of splits. If zero, split on every occurence of <paramref name="delimiter"/>.</param>
375+
/// <returns>A sequence of split subsequences.</returns>
376+
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is negative.</exception>
377+
public static IEnumerable<IEnumerable<char>> Split(IEnumerable<char> source, IEnumerable<char> delimiter, int count = 0)
378+
{
379+
#if NET8_0_OR_GREATER
380+
ArgumentOutOfRangeException.ThrowIfNegative(count);
381+
#else
382+
if(count < 0)
383+
{
384+
throw new ArgumentOutOfRangeException(nameof(count));
385+
}
386+
#endif
387+
388+
string _source = string.Join(",", source);
389+
string _delimiter = string.Join(",", delimiter);
390+
391+
string[] splits = _source.Split(_delimiter, count == 0 ? int.MaxValue : count, StringSplitOptions.None);
392+
393+
return splits.Select(s => s.Trim(',').Split(',').Select(x => x[0]));
394+
}
395+
311396
/// <summary>
312397
/// Get an <see cref="IEnumerable{StringSplitOptions}"/> with all permutations of the <see cref="StringSplitOptions"/> enum flags.
313398
/// </summary>

tests/ToSystemEnumerableExtensions.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,53 @@ public static IEnumerable<IEnumerable<char>> ToSystemEnumerable(this SpanSplitAn
103103

104104
return list;
105105
}
106+
107+
public static IEnumerable<IEnumerable<T>> ToSystemEnumerable<T>(this SpanSplitSequenceEnumerator<T> spanEnumerator) where T : IEquatable<T>
108+
{
109+
List<T[]> list = [];
110+
111+
foreach(ReadOnlySpan<T> element in spanEnumerator)
112+
{
113+
list.Add(element.ToArray());
114+
}
115+
116+
return list;
117+
}
118+
119+
public static IEnumerable<IEnumerable<T>> ToSystemEnumerable<T>(this SpanSplitSequenceWithCountEnumerator<T> spanEnumerator) where T : IEquatable<T>
120+
{
121+
List<T[]> list = [];
122+
123+
foreach(ReadOnlySpan<T> element in spanEnumerator)
124+
{
125+
list.Add(element.ToArray());
126+
}
127+
128+
return list;
129+
}
130+
131+
public static IEnumerable<IEnumerable<char>> ToSystemEnumerable(this SpanSplitSequenceStringSplitOptionsEnumerator spanEnumerator)
132+
{
133+
List<char[]> list = [];
134+
135+
foreach(ReadOnlySpan<char> element in spanEnumerator)
136+
{
137+
list.Add(element.ToArray());
138+
}
139+
140+
return list;
141+
}
142+
143+
public static IEnumerable<IEnumerable<char>> ToSystemEnumerable(this SpanSplitSequenceStringSplitOptionsWithCountEnumerator spanEnumerator)
144+
{
145+
List<char[]> list = [];
146+
147+
foreach(ReadOnlySpan<char> element in spanEnumerator)
148+
{
149+
list.Add(element.ToArray());
150+
}
151+
152+
return list;
153+
}
106154
}
107155
}

0 commit comments

Comments
 (0)