Change RazorSyntaxTree.Diagnostics from an IReadOnlyList<RazorDiagnostic> to an ImmutableArray<RazorDiagnostic> (#10797)

This pull request represents several changes with the ultimate goal of
exposing `RazorSyntaxTree.Diagnostics` as an
`ImmutableArray<RazorDiagnostic>` rather than an
`IReadOnlyList<RazorDiagnostic>`:

- Clean up `RazorSyntaxTree` and get rid of `DefaultRazorSyntaxTree`.
- Add `(Drain)ToImmutableOrdered*` methods to `PooledArrayBuilder<T>`.
Note that this change also includes a refactoring to the various unit
tests for ordering to share test data that I've isolated to a single
commit.
- Clean up and improve `ErrorSink` to no longer greedily create a new
`List<T>` before any errors are encountered.
- Clean up `ParserContext` and make it used pooled collections.
- Use pooled collections when computing and caching the result of
`RazorSyntaxTree.Diagnostics`.
This commit is contained in:
Dustin Campbell 2024-09-04 08:41:16 -07:00 коммит произвёл GitHub
Родитель 4ec65fb914 5c0677ad27
Коммит 90b1855f86
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
50 изменённых файлов: 2132 добавлений и 1684 удалений

Просмотреть файл

@ -52,7 +52,7 @@ public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
IEnumerable<KeyValuePair<string, string>> expectedPairs)
{
// Arrange
var errorSink = new ErrorSink();
using var errorSink = new ErrorSink();
var parseResult = ParseDocument(documentContent);
var document = parseResult.Root;
@ -61,7 +61,7 @@ public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
var rootMarkup = Assert.IsType<MarkupBlockSyntax>(rootBlock.Document);
var childBlock = Assert.Single(rootMarkup.Children);
var element = Assert.IsType<MarkupElementSyntax>(childBlock);
Assert.Empty(errorSink.Errors);
Assert.Empty(errorSink.GetErrorsAndClear());
// Act
var pairs = TagHelperParseTreeRewriter.Rewriter.GetAttributeNameValuePairs(element.StartTag);

Просмотреть файл

@ -32,7 +32,7 @@ public class WhiteSpaceRewriterTest() : ParserTestBase(layer: TestProject.Layer.
var rewritten = rewriter.Visit(parsed.Root);
// Assert
var rewrittenTree = RazorSyntaxTree.Create(rewritten, parsed.Source, parsed.Diagnostics, parsed.Options);
var rewrittenTree = new RazorSyntaxTree(rewritten, parsed.Source, parsed.Diagnostics, parsed.Options);
BaselineTest(rewrittenTree);
}
}

Просмотреть файл

@ -604,7 +604,7 @@ public class DefaultRazorTagHelperContextDiscoveryPhaseTest : RazorProjectEngine
var expectedRewritingError = RazorDiagnosticFactory.CreateParsing_TagHelperFoundMalformedTagHelper(
new SourceSpan(new SourceLocation((Environment.NewLine.Length * 2) + 30, 2, 1), contentLength: 4), "form");
var erroredOriginalTree = RazorSyntaxTree.Create(originalTree.Root, originalTree.Source, [initialError], originalTree.Options);
var erroredOriginalTree = new RazorSyntaxTree(originalTree.Root, originalTree.Source, [initialError], originalTree.Options);
codeDocument.SetSyntaxTree(erroredOriginalTree);
// Act
@ -615,7 +615,7 @@ public class DefaultRazorTagHelperContextDiscoveryPhaseTest : RazorProjectEngine
var outputTree = codeDocument.GetSyntaxTree();
Assert.Empty(originalTree.Diagnostics);
Assert.NotSame(erroredOriginalTree, outputTree);
Assert.Equal([initialError, expectedRewritingError], outputTree.Diagnostics);
Assert.Equal<RazorDiagnostic>([initialError, expectedRewritingError], outputTree.Diagnostics);
}
private static string AssemblyA => "TestAssembly";

Просмотреть файл

@ -215,13 +215,15 @@ public class CSharpCodeParserTest
// Arrange
var source = TestRazorSourceDocument.Create();
var options = RazorParserOptions.CreateDefault();
var context = new ParserContext(source, options);
using var context = new ParserContext(source, options);
// Act & Assert (Does not throw)
var directiveDescriptors = new[] {
var directiveDescriptors = new[]
{
DirectiveDescriptor.CreateDirective("test", DirectiveKind.SingleLine),
DirectiveDescriptor.CreateDirective("test", DirectiveKind.SingleLine),
};
_ = new CSharpCodeParser(directiveDescriptors, context);
}
}

Просмотреть файл

@ -53,7 +53,7 @@ public class HtmlMarkupParserTests
public void AcceptAllButLastDoubleHypens_ReturnsTheOnlyDoubleHyphenToken()
{
// Arrange
var sut = CreateTestParserForContent("-->");
using var sut = CreateTestParserForContent("-->");
// Act
var token = sut.AcceptAllButLastDoubleHyphens();
@ -68,7 +68,7 @@ public class HtmlMarkupParserTests
public void AcceptAllButLastDoubleHypens_ReturnsTheDoubleHyphenTokenAfterAcceptingTheDash()
{
// Arrange
var sut = CreateTestParserForContent("--->");
using var sut = CreateTestParserForContent("--->");
// Act
var token = sut.AcceptAllButLastDoubleHyphens();
@ -83,7 +83,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsTrueForEmptyCommentTag()
{
// Arrange
var sut = CreateTestParserForContent("<!---->");
using var sut = CreateTestParserForContent("<!---->");
// Act & Assert
Assert.True(sut.IsHtmlCommentAhead());
@ -93,7 +93,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTag()
{
// Arrange
var sut = CreateTestParserForContent("<!-- Some comment content in here -->");
using var sut = CreateTestParserForContent("<!-- Some comment content in here -->");
// Act & Assert
Assert.True(sut.IsHtmlCommentAhead());
@ -103,7 +103,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraDashesAtClosingTag()
{
// Arrange
var sut = CreateTestParserForContent("<!-- Some comment content in here ----->");
using var sut = CreateTestParserForContent("<!-- Some comment content in here ----->");
// Act & Assert
Assert.True(sut.IsHtmlCommentAhead());
@ -113,7 +113,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsFalseForContentWithBadEndingAndExtraDash()
{
// Arrange
var sut = CreateTestParserForContent("<!-- Some comment content in here <!--->");
using var sut = CreateTestParserForContent("<!-- Some comment content in here <!--->");
// Act & Assert
Assert.False(sut.IsHtmlCommentAhead());
@ -123,7 +123,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraInfoAfter()
{
// Arrange
var sut = CreateTestParserForContent("<!-- comment --> the first part is a valid comment without the Open angle and bang tokens");
using var sut = CreateTestParserForContent("<!-- comment --> the first part is a valid comment without the Open angle and bang tokens");
// Act & Assert
Assert.True(sut.IsHtmlCommentAhead());
@ -133,7 +133,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsFalseForNotClosedComment()
{
// Arrange
var sut = CreateTestParserForContent("<!-- not closed comment");
using var sut = CreateTestParserForContent("<!-- not closed comment");
// Act & Assert
Assert.False(sut.IsHtmlCommentAhead());
@ -143,7 +143,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsFalseForCommentWithoutLastClosingAngle()
{
// Arrange
var sut = CreateTestParserForContent("<!-- not closed comment--");
using var sut = CreateTestParserForContent("<!-- not closed comment--");
// Act & Assert
Assert.False(sut.IsHtmlCommentAhead());
@ -153,7 +153,7 @@ public class HtmlMarkupParserTests
public void IsHtmlCommentAhead_ReturnsTrueForCommentWithCodeInside()
{
// Arrange
var sut = CreateTestParserForContent("<!-- not closed @DateTime.Now comment-->");
using var sut = CreateTestParserForContent("<!-- not closed @DateTime.Now comment-->");
// Act & Assert
Assert.True(sut.IsHtmlCommentAhead());
@ -195,8 +195,19 @@ public class HtmlMarkupParserTests
Assert.False(HtmlMarkupParser.IsCommentContentEndingInvalid(sequence));
}
private class TestHtmlMarkupParser : HtmlMarkupParser
private class TestHtmlMarkupParser : HtmlMarkupParser, IDisposable
{
public TestHtmlMarkupParser(ParserContext context)
: base(context)
{
EnsureCurrent();
}
public void Dispose()
{
Context.Dispose();
}
public new SyntaxToken PreviousToken
{
get => base.PreviousToken;
@ -207,11 +218,6 @@ public class HtmlMarkupParserTests
return base.IsHtmlCommentAhead();
}
public TestHtmlMarkupParser(ParserContext context) : base(context)
{
EnsureCurrent();
}
public new SyntaxToken AcceptAllButLastDoubleHyphens()
{
return base.AcceptAllButLastDoubleHyphens();

Просмотреть файл

@ -52,7 +52,7 @@ public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
IEnumerable<KeyValuePair<string, string>> expectedPairs)
{
// Arrange
var errorSink = new ErrorSink();
using var errorSink = new ErrorSink();
var parseResult = ParseDocument(documentContent);
var document = parseResult.Root;
@ -61,7 +61,7 @@ public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
var rootMarkup = Assert.IsType<MarkupBlockSyntax>(rootBlock.Document);
var childBlock = Assert.Single(rootMarkup.Children);
var element = Assert.IsType<MarkupElementSyntax>(childBlock);
Assert.Empty(errorSink.Errors);
Assert.Empty(errorSink.GetErrorsAndClear());
// Act
var pairs = TagHelperParseTreeRewriter.Rewriter.GetAttributeNameValuePairs(element.StartTag);

Просмотреть файл

@ -63,7 +63,7 @@ public class TokenizerLookaheadTest : HtmlTokenizerTestBase
public void LookaheadUntil_PassesThePreviousTokensInTheSameOrder()
{
// Arrange
var tokenizer = CreateContentTokenizer("asdf--fvd--<");
using var tokenizer = CreateContentTokenizer("asdf--fvd--<");
// Act
var i = 3;
@ -89,7 +89,7 @@ public class TokenizerLookaheadTest : HtmlTokenizerTestBase
public void LookaheadUntil_ReturnsFalseAfterIteratingOverAllTokensIfConditionIsNotMet()
{
// Arrange
var tokenizer = CreateContentTokenizer("asdf--fvd");
using var tokenizer = CreateContentTokenizer("asdf--fvd");
// Act
var tokens = new Stack<SyntaxToken>();
@ -111,7 +111,7 @@ public class TokenizerLookaheadTest : HtmlTokenizerTestBase
public void LookaheadUntil_ReturnsTrueAndBreaksIteration()
{
// Arrange
var tokenizer = CreateContentTokenizer("asdf--fvd");
using var tokenizer = CreateContentTokenizer("asdf--fvd");
// Act
var tokens = new Stack<SyntaxToken>();
@ -134,8 +134,7 @@ public class TokenizerLookaheadTest : HtmlTokenizerTestBase
var options = RazorParserOptions.CreateDefault();
var context = new ParserContext(source, options);
var tokenizer = new TestTokenizerBackedParser(HtmlLanguageCharacteristics.Instance, context);
return tokenizer;
return new TestTokenizerBackedParser(HtmlLanguageCharacteristics.Instance, context);
}
private static void AssertTokenEqual(SyntaxToken expected, SyntaxToken actual)
@ -204,12 +203,18 @@ public class TokenizerLookaheadTest : HtmlTokenizerTestBase
}
}
private class TestTokenizerBackedParser : TokenizerBackedParser<HtmlTokenizer>
private class TestTokenizerBackedParser : TokenizerBackedParser<HtmlTokenizer>, IDisposable
{
internal TestTokenizerBackedParser(LanguageCharacteristics<HtmlTokenizer> language, ParserContext context) : base(language, context)
internal TestTokenizerBackedParser(LanguageCharacteristics<HtmlTokenizer> language, ParserContext context)
: base(language, context)
{
}
public void Dispose()
{
Context.Dispose();
}
internal new bool LookaheadUntil(Func<SyntaxToken, IEnumerable<SyntaxToken>, bool> condition)
{
return base.LookaheadUntil(condition);

Просмотреть файл

@ -32,7 +32,7 @@ public class WhiteSpaceRewriterTest() : ParserTestBase(layer: TestProject.Layer.
var rewritten = rewriter.Visit(parsed.Root);
// Assert
var rewrittenTree = RazorSyntaxTree.Create(rewritten, parsed.Source, parsed.Diagnostics, parsed.Options);
var rewrittenTree = new RazorSyntaxTree(rewritten, parsed.Source, parsed.Diagnostics, parsed.Options);
BaselineTest(rewrittenTree);
}
}

Просмотреть файл

@ -1,10 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Syntax;
@ -26,23 +24,19 @@ internal class DefaultDirectiveSyntaxTreePass : RazorEngineFeatureBase, IRazorSy
return sectionVerifier.Verify();
}
private class NestedSectionVerifier : SyntaxRewriter
private sealed class NestedSectionVerifier(RazorSyntaxTree syntaxTree) : SyntaxRewriter
{
private int _nestedLevel;
private readonly RazorSyntaxTree _syntaxTree;
private readonly List<RazorDiagnostic> _diagnostics;
private readonly RazorSyntaxTree _syntaxTree = syntaxTree;
public NestedSectionVerifier(RazorSyntaxTree syntaxTree)
{
_syntaxTree = syntaxTree;
_diagnostics = new List<RazorDiagnostic>(syntaxTree.Diagnostics);
}
private ImmutableArray<RazorDiagnostic>.Builder? _diagnostics;
private int _nestedLevel;
public RazorSyntaxTree Verify()
{
var root = Visit(_syntaxTree.Root);
var rewrittenTree = new DefaultRazorSyntaxTree(root, _syntaxTree.Source, _diagnostics, _syntaxTree.Options);
return rewrittenTree;
var diagnostics = _diagnostics?.DrainToImmutable() ?? _syntaxTree.Diagnostics;
return new RazorSyntaxTree(root, _syntaxTree.Source, diagnostics, _syntaxTree.Options);
}
public override SyntaxNode Visit(SyntaxNode node)
@ -55,6 +49,7 @@ internal class DefaultDirectiveSyntaxTreePass : RazorEngineFeatureBase, IRazorSy
{
// We're very close to reaching the stack limit. Let's not go any deeper.
// It's okay to not show nested section errors in deeply nested cases instead of crashing.
_diagnostics ??= _syntaxTree.Diagnostics.ToBuilder();
_diagnostics.Add(RazorDiagnosticFactory.CreateRewriter_InsufficientStack());
return node;

Просмотреть файл

@ -130,18 +130,18 @@ internal class DefaultRazorIntermediateNodeLoweringPhase : RazorEnginePhaseBase,
// The document should contain all errors that currently exist in the system. This involves
// adding the errors from the primary and imported syntax trees.
for (var i = 0; i < syntaxTree.Diagnostics.Count; i++)
foreach (var diagnostic in syntaxTree.Diagnostics)
{
document.Diagnostics.Add(syntaxTree.Diagnostics[i]);
document.Diagnostics.Add(diagnostic);
}
if (imports is { IsDefault: false } importsArray)
{
foreach (var import in importsArray)
{
for (var j = 0; j < import.Diagnostics.Count; j++)
foreach (var diagnostic in import.Diagnostics)
{
document.Diagnostics.Add(import.Diagnostics[j]);
document.Diagnostics.Add(diagnostic);
}
}
}

Просмотреть файл

@ -1,60 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax;
namespace Microsoft.AspNetCore.Razor.Language;
internal class DefaultRazorSyntaxTree : RazorSyntaxTree
{
private readonly IReadOnlyList<RazorDiagnostic> _diagnostics;
private IReadOnlyList<RazorDiagnostic> _allDiagnostics;
public DefaultRazorSyntaxTree(
SyntaxNode root,
RazorSourceDocument source,
IReadOnlyList<RazorDiagnostic> diagnostics,
RazorParserOptions options)
{
Root = root;
Source = source;
_diagnostics = diagnostics;
Options = options;
}
public override IReadOnlyList<RazorDiagnostic> Diagnostics
{
get
{
if (_allDiagnostics == null)
{
var allDiagnostics = new HashSet<RazorDiagnostic>();
for (var i = 0; i < _diagnostics.Count; i++)
{
allDiagnostics.Add(_diagnostics[i]);
}
var rootDiagnostics = Root.GetAllDiagnostics();
for (var i = 0; i < rootDiagnostics.Count; i++)
{
allDiagnostics.Add(rootDiagnostics[i]);
}
var allOrderedDiagnostics = allDiagnostics.OrderBy(diagnostic => diagnostic.Span.AbsoluteIndex);
_allDiagnostics = allOrderedDiagnostics.ToArray();
}
return _allDiagnostics;
}
}
public override RazorParserOptions Options { get; }
internal override SyntaxNode Root { get; }
public override RazorSourceDocument Source { get; }
}

Просмотреть файл

@ -27,7 +27,6 @@ internal class HtmlNodeOptimizationPass : RazorEngineFeatureBase, IRazorSyntaxTr
var whitespaceRewriter = new WhitespaceRewriter();
var rewritten = whitespaceRewriter.Visit(syntaxTree.Root);
var rewrittenSyntaxTree = RazorSyntaxTree.Create(rewritten, syntaxTree.Source, syntaxTree.Diagnostics, syntaxTree.Options);
return rewrittenSyntaxTree;
return new RazorSyntaxTree(rewritten, syntaxTree.Source, syntaxTree.Diagnostics, syntaxTree.Options);
}
}

Просмотреть файл

@ -9,6 +9,7 @@ using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
using static Microsoft.AspNetCore.Razor.Language.Syntax.GreenNodeExtensions;
using CSharpSyntaxFacts = Microsoft.CodeAnalysis.CSharp.SyntaxFacts;
using CSharpSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind;
@ -84,22 +85,15 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
private readonly ImmutableDictionary<string, Action<SyntaxListBuilder<RazorSyntaxNode>, CSharpTransitionSyntax>> _directiveParserMap;
public CSharpCodeParser(ParserContext context)
: this(directives: Enumerable.Empty<DirectiveDescriptor>(), context: context)
: this(directives: [], context)
{
}
public CSharpCodeParser(IEnumerable<DirectiveDescriptor> directives, ParserContext context)
: base(context.ParseLeadingDirectives ? FirstDirectiveCSharpLanguageCharacteristics.Instance : CSharpLanguageCharacteristics.Instance, context)
{
if (directives == null)
{
throw new ArgumentNullException(nameof(directives));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
ArgHelper.ThrowIfNull(directives);
ArgHelper.ThrowIfNull(context);
var keywordsBuilder = ImmutableHashSet<string>.Empty.ToBuilder();
var keywordParserMapBuilder = ImmutableDictionary<CSharpSyntaxKind, Action<SyntaxListBuilder<RazorSyntaxNode>, CSharpTransitionSyntax?>>.Empty.ToBuilder();
@ -298,7 +292,7 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
{
var diagnostic = RazorDiagnosticFactory.CreateParsing_HelperDirectiveNotAvailable(
new SourceSpan(CurrentStart, CurrentToken.Content.Length));
CurrentToken.SetDiagnostics(new[] { diagnostic });
CurrentToken.SetDiagnostics([diagnostic]);
Context.ErrorSink.OnError(diagnostic);
}
@ -1213,14 +1207,13 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
{
AssertDirective(keyword);
var savedErrorSink = Context.ErrorSink;
var directiveErrorSink = new ErrorSink();
RazorMetaCodeSyntax? keywordBlock = null;
using (var pooledResult = Pool.Allocate<RazorSyntaxNode>())
{
var directiveBuilder = pooledResult.Builder;
Context.ErrorSink = directiveErrorSink;
using var pooledResult = Pool.Allocate<RazorSyntaxNode>();
var directiveBuilder = pooledResult.Builder;
using var directiveErrorSink = new ErrorSink();
using (Context.PushNewErrorScope(directiveErrorSink))
{
string? directiveValue = null;
SourceLocation? valueStartLocation = null;
EnsureDirectiveIsAtStartOfLine();
@ -1278,19 +1271,18 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
chunkGenerator = chunkGeneratorFactory(
directiveValue,
directiveErrorSink.Errors.ToList(),
[.. directiveErrorSink.GetErrorsAndClear()],
valueStartLocation ?? CurrentStart);
Context.ErrorSink = savedErrorSink;
// Finish the block and output the tokens
CompleteBlock();
SetAcceptedCharacters(AcceptedCharactersInternal.AnyExceptNewline);
directiveBuilder.Add(OutputTokensAsStatementLiteral());
var directiveCodeBlock = SyntaxFactory.CSharpCodeBlock(directiveBuilder.ToList());
return SyntaxFactory.RazorDirectiveBody(keywordBlock, directiveCodeBlock);
}
// Finish the block and output the tokens
CompleteBlock();
SetAcceptedCharacters(AcceptedCharactersInternal.AnyExceptNewline);
directiveBuilder.Add(OutputTokensAsStatementLiteral());
var directiveCodeBlock = SyntaxFactory.CSharpCodeBlock(directiveBuilder.ToList());
return SyntaxFactory.RazorDirectiveBody(keywordBlock, directiveCodeBlock);
}
private ParsedDirective ParseDirective(
@ -1430,17 +1422,14 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
{
AssertDirective(descriptor.Directive);
var directiveErrorSink = new ErrorSink();
var savedErrorSink = Context.ErrorSink;
Context.ErrorSink = directiveErrorSink;
using (var pooledResult = Pool.Allocate<RazorSyntaxNode>())
{
var directiveBuilder = pooledResult.Builder;
RazorMetaCodeSyntax? keywordBlock = null;
bool shouldCaptureWhitespaceToEndOfLine = false;
try
using var directiveErrorSink = new ErrorSink();
using (Context.PushNewErrorScope(directiveErrorSink))
{
EnsureDirectiveIsAtStartOfLine();
var directiveStart = CurrentStart;
@ -1742,7 +1731,6 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
Resources.ErrorComponent_Newline));
}
// This should contain the optional whitespace after the optional semicolon and the new line.
// Output as Markup as we want intellisense here.
chunkGenerator = SpanChunkGenerator.Null;
@ -1811,10 +1799,6 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
break;
}
}
finally
{
Context.ErrorSink = savedErrorSink;
}
builder.Add(BuildDirective(SyntaxKind.Identifier));
@ -1836,7 +1820,9 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
var directiveBody = SyntaxFactory.RazorDirectiveBody(keywordBlock, directiveCodeBlock);
var directive = SyntaxFactory.RazorDirective(transition, directiveBody);
directive = (RazorDirectiveSyntax)directive.SetDiagnostics(directiveErrorSink.Errors.ToArray());
var diagnostics = directiveErrorSink.GetErrorsAndClear();
directive = directive.WithDiagnosticsGreen(diagnostics);
directive = directive.WithDirectiveDescriptor(descriptor);
return directive;
}
@ -2210,7 +2196,7 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
if (At(CSharpSyntaxKind.ElseKeyword))
{
Accept(in whitespace);
Assert(CSharpSyntaxKind.ElseKeyword);
Assert(CSharpSyntaxKind.ElseKeyword);
ParseElseClause(builder);
}
else

Просмотреть файл

@ -1,35 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Collections.Generic;
using System;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
/// <summary>
/// Used to manage <see cref="RazorDiagnostic"/>s encountered during the Razor parsing phase.
/// Used to manage <see cref="RazorDiagnostic">RazorDiagnostics</see> encountered during the Razor parsing phase.
/// </summary>
internal class ErrorSink
internal sealed class ErrorSink : IDisposable
{
private readonly List<RazorDiagnostic> _errors;
private ImmutableArray<RazorDiagnostic>.Builder? _errors;
/// <summary>
/// Instantiates a new instance of <see cref="ErrorSink"/>.
/// </summary>
public ErrorSink()
public void Dispose()
{
_errors = new List<RazorDiagnostic>();
var errors = _errors;
if (errors is not null)
{
ArrayBuilderPool<RazorDiagnostic>.Default.Return(errors);
_errors = null;
}
}
/// <summary>
/// <see cref="RazorDiagnostic"/>s collected.
/// </summary>
public IReadOnlyList<RazorDiagnostic> Errors => _errors;
public ImmutableArray<RazorDiagnostic> GetErrorsAndClear()
{
var errors = _errors;
if (errors is null)
{
return [];
}
var result = errors.DrainToImmutable();
ArrayBuilderPool<RazorDiagnostic>.Default.Return(errors);
_errors = null;
return result;
}
/// <summary>
/// Tracks the given <paramref name="error"/>.
/// </summary>
/// <param name="error">The <see cref="RazorDiagnostic"/> to track.</param>
public void OnError(RazorDiagnostic error) => _errors.Add(error);
public void OnError(RazorDiagnostic error)
{
var errors = _errors ??= ArrayBuilderPool<RazorDiagnostic>.Default.Get();
errors.Add(error);
}
}

Просмотреть файл

@ -19,10 +19,10 @@ internal class HtmlMarkupParser : TokenizerBackedParser<HtmlTokenizer>
private static readonly char[] ValidAfterTypeAttributeNameCharacters = { ' ', '\t', '\r', '\n', '\f', '=' };
private static readonly SyntaxToken[] nonAllowedHtmlCommentEnding = new[]
{
SyntaxFactory.Token(SyntaxKind.Text, "-"),
SyntaxFactory.Token(SyntaxKind.Bang, "!"),
SyntaxFactory.Token(SyntaxKind.OpenAngle, "<"),
};
SyntaxFactory.Token(SyntaxKind.Text, "-"),
SyntaxFactory.Token(SyntaxKind.Bang, "!"),
SyntaxFactory.Token(SyntaxKind.OpenAngle, "<"),
};
private Stack<TagTracker> _tagTracker = new Stack<TagTracker>();

Просмотреть файл

@ -3,12 +3,7 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
internal abstract class ParserBase
internal abstract class ParserBase(ParserContext context)
{
public ParserBase(ParserContext context)
{
Context = context;
}
public ParserContext Context { get; }
public ParserContext Context { get; } = context;
}

Просмотреть файл

@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
internal partial class ParserContext
{
public readonly ref struct ErrorScope(ParserContext context)
{
private readonly ParserContext _context = context;
public void Dispose()
{
_context._errorSinkStack.Pop();
}
}
}

Просмотреть файл

@ -4,60 +4,73 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
internal partial class ParserContext
internal sealed partial class ParserContext : IDisposable
{
public ParserContext(RazorSourceDocument source, RazorParserOptions options)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
SourceDocument = source;
Source = new SeekableTextReader(SourceDocument);
DesignTimeMode = options.DesignTime;
FeatureFlags = options.FeatureFlags;
ParseLeadingDirectives = options.ParseLeadingDirectives;
ErrorSink = new ErrorSink();
SeenDirectives = new HashSet<string>(StringComparer.Ordinal);
EnableSpanEditHandlers = options.EnableSpanEditHandlers;
}
public ErrorSink ErrorSink { get; set; }
public RazorParserFeatureFlags FeatureFlags { get; }
public HashSet<string> SeenDirectives { get; }
public SeekableTextReader Source { get; }
private readonly RazorParserOptions _options;
private readonly Stack<ErrorSink> _errorSinkStack;
private readonly HashSet<string> _seenDirectivesSet;
public RazorSourceDocument SourceDocument { get; }
public bool DesignTimeMode { get; }
public bool ParseLeadingDirectives { get; }
public bool EnableSpanEditHandlers { get; }
public SeekableTextReader Source { get; }
public bool WhiteSpaceIsSignificantToAncestorBlock { get; set; }
public bool NullGenerateWhitespaceAndNewLine { get; set; }
public bool InTemplateContext { get; set; }
public bool StartOfLine { get; set; } = true;
public bool MakeMarkerNode { get; set; } = true;
public AcceptedCharactersInternal CurrentAcceptedCharacters { get; set; } = AcceptedCharactersInternal.Any;
public bool EndOfFile
public ParserContext(RazorSourceDocument source, RazorParserOptions options)
{
get { return Source.Peek() == -1; }
ArgHelper.ThrowIfNull(source);
ArgHelper.ThrowIfNull(options);
SourceDocument = source;
_options = options;
_errorSinkStack = StackPool<ErrorSink>.Default.Get();
_errorSinkStack.Push(new ErrorSink());
_seenDirectivesSet = StringHashSetPool.Ordinal.Get();
Source = new SeekableTextReader(SourceDocument);
}
public void Dispose()
{
while (_errorSinkStack.Count > 0)
{
var errorSink = _errorSinkStack.Pop();
errorSink.Dispose();
}
StackPool<ErrorSink>.Default.Return(_errorSinkStack);
StringHashSetPool.Ordinal.Return(_seenDirectivesSet);
}
public ErrorSink ErrorSink => _errorSinkStack.Peek();
public RazorParserFeatureFlags FeatureFlags => _options.FeatureFlags;
public HashSet<string> SeenDirectives => _seenDirectivesSet;
public bool DesignTimeMode => _options.DesignTime;
public bool ParseLeadingDirectives => _options.ParseLeadingDirectives;
public bool EnableSpanEditHandlers => _options.EnableSpanEditHandlers;
public bool IsEndOfFile
=> Source.Peek() == -1;
public ErrorScope PushNewErrorScope(ErrorSink errorSink)
{
_errorSinkStack.Push(errorSink);
return new(this);
}
}

Просмотреть файл

@ -1,10 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System;
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
internal class RazorParser
@ -16,10 +12,7 @@ internal class RazorParser
public RazorParser(RazorParserOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
ArgHelper.ThrowIfNull(options);
Options = options;
}
@ -28,23 +21,18 @@ internal class RazorParser
public virtual RazorSyntaxTree Parse(RazorSourceDocument source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
ArgHelper.ThrowIfNull(source);
var context = new ParserContext(source, Options);
using var context = new ParserContext(source, Options);
var codeParser = new CSharpCodeParser(Options.Directives, context);
var markupParser = new HtmlMarkupParser(context);
codeParser.HtmlParser = markupParser;
markupParser.CodeParser = codeParser;
var diagnostics = context.ErrorSink.Errors;
var root = markupParser.ParseDocument().CreateRed();
var diagnostics = context.ErrorSink.GetErrorsAndClear();
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, Options);
return syntaxTree;
return new RazorSyntaxTree(root, source, diagnostics, Options);
}
}

Просмотреть файл

@ -292,7 +292,6 @@ internal static class TagHelperBlockRewriter
rewritten = rewritten.WithTagHelperAttributeInfo(
new TagHelperAttributeInfo(result.AttributeName, parameterName: null, result.AttributeStructure, result.IsBoundAttribute, isDirectiveAttribute: false));
result.RewrittenAttribute = rewritten;
return result;

Просмотреть файл

@ -16,7 +16,7 @@ internal static class TagHelperParseTreeRewriter
{
public static RazorSyntaxTree Rewrite(RazorSyntaxTree syntaxTree, TagHelperBinder binder, out ISet<TagHelperDescriptor> usedDescriptors)
{
var errorSink = new ErrorSink();
using var errorSink = new ErrorSink();
var rewriter = new Rewriter(
syntaxTree.Source,
@ -26,25 +26,26 @@ internal static class TagHelperParseTreeRewriter
var rewritten = rewriter.Visit(syntaxTree.Root);
var errorList = new List<RazorDiagnostic>();
errorList.AddRange(errorSink.Errors);
errorList.AddRange(binder.TagHelpers.SelectMany(d => d.GetAllDiagnostics()));
var treeDiagnostics = syntaxTree.Diagnostics;
var sinkDiagnostics = errorSink.GetErrorsAndClear();
var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList).OrderBy(error => error.Span.AbsoluteIndex);
using var builder = new PooledArrayBuilder<RazorDiagnostic>(capacity: treeDiagnostics.Length + sinkDiagnostics.Length);
builder.AddRange(treeDiagnostics);
builder.AddRange(sinkDiagnostics);
foreach (var tagHelper in binder.TagHelpers)
{
foreach (var diagnostic in tagHelper.GetAllDiagnostics())
{
builder.Add(diagnostic);
}
}
var diagnostics = builder.ToImmutableOrderedBy(static d => d.Span.AbsoluteIndex);
usedDescriptors = rewriter.UsedDescriptors;
var newSyntaxTree = RazorSyntaxTree.Create(rewritten, syntaxTree.Source, diagnostics, syntaxTree.Options);
return newSyntaxTree;
}
private static IReadOnlyList<RazorDiagnostic> CombineErrors(IReadOnlyList<RazorDiagnostic> errors1, IReadOnlyList<RazorDiagnostic> errors2)
{
var combinedErrors = new List<RazorDiagnostic>(errors1.Count + errors2.Count);
combinedErrors.AddRange(errors1);
combinedErrors.AddRange(errors2);
return combinedErrors;
return new RazorSyntaxTree(rewritten, syntaxTree.Source, diagnostics, syntaxTree.Options);
}
// Internal for testing.

Просмотреть файл

@ -1,72 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace Microsoft.AspNetCore.Razor.Language;
public abstract class RazorSyntaxTree
public sealed class RazorSyntaxTree
{
internal static RazorSyntaxTree Create(
internal SyntaxNode Root { get; }
public RazorParserOptions Options { get; }
public RazorSourceDocument Source { get; }
private readonly ImmutableArray<RazorDiagnostic> _diagnostics;
private ImmutableArray<RazorDiagnostic> _allDiagnostics;
internal RazorSyntaxTree(
SyntaxNode root,
RazorSourceDocument source,
IEnumerable<RazorDiagnostic> diagnostics,
ImmutableArray<RazorDiagnostic> diagnostics,
RazorParserOptions options)
{
if (root == null)
{
throw new ArgumentNullException(nameof(root));
}
ArgHelper.ThrowIfNull(root);
ArgHelper.ThrowIfNull(source);
ArgHelper.ThrowIfNull(options);
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (diagnostics == null)
{
throw new ArgumentNullException(nameof(diagnostics));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
return new DefaultRazorSyntaxTree(root, source, new List<RazorDiagnostic>(diagnostics), options);
Root = root;
Source = source;
_diagnostics = diagnostics.NullToEmpty();
Options = options;
}
public static RazorSyntaxTree Parse(RazorSourceDocument source)
public ImmutableArray<RazorDiagnostic> Diagnostics
{
if (source == null)
get
{
throw new ArgumentNullException(nameof(source));
}
if (_allDiagnostics.IsDefault)
{
ImmutableInterlocked.InterlockedInitialize(ref _allDiagnostics, ComputeAllDiagnostics(_diagnostics, Root));
}
return Parse(source, options: null);
return _allDiagnostics;
static ImmutableArray<RazorDiagnostic> ComputeAllDiagnostics(ImmutableArray<RazorDiagnostic> treeDiagnostics, SyntaxNode root)
{
using var pooledList = ListPool<RazorDiagnostic>.GetPooledObject(out var rootDiagnostics);
using var diagnosticSet = new PooledHashSet<RazorDiagnostic>();
foreach (var diagnostic in treeDiagnostics)
{
diagnosticSet.Add(diagnostic);
}
root.CollectAllDiagnostics(rootDiagnostics);
if (rootDiagnostics.Count > 0)
{
foreach (var diagnostic in rootDiagnostics)
{
diagnosticSet.Add(diagnostic);
}
}
return diagnosticSet.OrderByAsArray(static d => d.Span.AbsoluteIndex);
}
}
}
public static RazorSyntaxTree Parse(RazorSourceDocument source, RazorParserOptions options)
public static RazorSyntaxTree Parse(RazorSourceDocument source, RazorParserOptions? options = null)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
ArgHelper.ThrowIfNull(source);
var parser = new RazorParser(options ?? RazorParserOptions.CreateDefault());
options ??= RazorParserOptions.CreateDefault();
var parser = new RazorParser(options);
return parser.Parse(source);
}
public abstract IReadOnlyList<RazorDiagnostic> Diagnostics { get; }
public abstract RazorParserOptions Options { get; }
internal abstract SyntaxNode Root { get; }
public abstract RazorSourceDocument Source { get; }
}

Просмотреть файл

@ -3,9 +3,9 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Razor.Language.Syntax;
@ -52,8 +52,16 @@ internal static class GreenNodeExtensions
}
}
public static TNode WithDiagnosticsGreen<TNode>(this TNode node, params RazorDiagnostic[] diagnostics) where TNode : GreenNode
public static TNode WithDiagnosticsGreen<TNode>(this TNode node, RazorDiagnostic[] diagnostics)
where TNode : GreenNode
{
return (TNode)node.SetDiagnostics(diagnostics);
}
public static TNode WithDiagnosticsGreen<TNode>(this TNode node, params ImmutableArray<RazorDiagnostic> diagnostics)
where TNode : GreenNode
{
var array = ImmutableCollectionsMarshal.AsArray(diagnostics);
return node.WithDiagnosticsGreen(array);
}
}

Просмотреть файл

@ -42,14 +42,13 @@ internal static class SyntaxNodeExtensions
return (TNode)node.Green.SetDiagnostics(diagnostics).CreateRed(node.Parent, node.Position);
}
public static TNode AppendDiagnostic<TNode>(this TNode node, params RazorDiagnostic[] diagnostics) where TNode : SyntaxNode
public static TNode AppendDiagnostic<TNode>(this TNode node, params ReadOnlySpan<RazorDiagnostic> diagnostics) where TNode : SyntaxNode
{
var existingDiagnostics = node.GetDiagnostics();
var allDiagnostics = new RazorDiagnostic[diagnostics.Length + existingDiagnostics.Length];
Array.Copy(existingDiagnostics, allDiagnostics, existingDiagnostics.Length);
Array.Copy(diagnostics, 0, allDiagnostics, existingDiagnostics.Length, diagnostics.Length);
RazorDiagnostic[] allDiagnostics = [
.. node.GetDiagnostics(),
.. diagnostics];
return (TNode)node.WithDiagnostics(allDiagnostics);
return node.WithDiagnostics(allDiagnostics);
}
/// <summary>
@ -57,13 +56,13 @@ internal static class SyntaxNodeExtensions
/// </summary>
/// <typeparam name="TNode">The type of syntax node.</typeparam>
/// <param name="node">The syntax node.</param>
/// <returns>The list of <see cref="RazorDiagnostic"/>s.</returns>
public static IReadOnlyList<RazorDiagnostic> GetAllDiagnostics<TNode>(this TNode node) where TNode : SyntaxNode
/// <param name="list"></param>
/// <returns>The list of <see cref="RazorDiagnostic">RazorDiagnostics</see>.</returns>
public static void CollectAllDiagnostics<TNode>(this TNode node, List<RazorDiagnostic> list)
where TNode : SyntaxNode
{
var walker = new DiagnosticSyntaxWalker();
var walker = new DiagnosticSyntaxWalker(list);
walker.Visit(node);
return walker.Diagnostics;
}
public static SourceLocation GetSourceLocation(this SyntaxNode node, RazorSourceDocument source)
@ -211,16 +210,9 @@ internal static class SyntaxNodeExtensions
return writer.ToString();
}
private class DiagnosticSyntaxWalker : SyntaxWalker
private sealed class DiagnosticSyntaxWalker(List<RazorDiagnostic> diagnostics) : SyntaxWalker
{
private readonly List<RazorDiagnostic> _diagnostics;
public DiagnosticSyntaxWalker()
{
_diagnostics = new List<RazorDiagnostic>();
}
public IReadOnlyList<RazorDiagnostic> Diagnostics => _diagnostics;
private readonly List<RazorDiagnostic> _diagnostics = diagnostics ?? [];
public override void Visit(SyntaxNode node)
{

Просмотреть файл

@ -52,7 +52,7 @@ public class SyntaxTreeGenerationBenchmark
});
var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
if (syntaxTree.Diagnostics.Count != 0)
if (syntaxTree.Diagnostics.Length != 0)
{
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
}
@ -70,7 +70,7 @@ public class SyntaxTreeGenerationBenchmark
});
var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
if (syntaxTree.Diagnostics.Count != 0)
if (syntaxTree.Diagnostics.Length != 0)
{
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
}

Просмотреть файл

@ -437,7 +437,7 @@ internal class RazorTranslateDiagnosticsService(IDocumentMappingService document
private static bool CheckIfDocumentHasRazorDiagnostic(RazorCodeDocument codeDocument, string razorDiagnosticCode)
{
return codeDocument.GetSyntaxTree().Diagnostics.Any(d => d.Id.Equals(razorDiagnosticCode, StringComparison.Ordinal));
return codeDocument.GetSyntaxTree().Diagnostics.Any(razorDiagnosticCode, static (d, code) => d.Id == code);
}
private bool TryGetOriginalDiagnosticRange(Diagnostic diagnostic, RazorCodeDocument codeDocument, [NotNullWhen(true)] out Range? originalRange)

Просмотреть файл

@ -44,7 +44,7 @@ internal class RazorSyntaxTreePartialParser
// Remember if this was provisionally accepted for next partial parse.
_lastResultProvisional = (result & PartialParseResultInternal.Provisional) == PartialParseResultInternal.Provisional;
var newSyntaxTree = RazorSyntaxTree.Create(ModifiedSyntaxTreeRoot, OriginalSyntaxTree.Source, OriginalSyntaxTree.Diagnostics, OriginalSyntaxTree.Options);
var newSyntaxTree = new RazorSyntaxTree(ModifiedSyntaxTreeRoot, OriginalSyntaxTree.Source, OriginalSyntaxTree.Diagnostics, OriginalSyntaxTree.Options);
return (result, newSyntaxTree);
}

Просмотреть файл

@ -180,13 +180,13 @@ public abstract class ToolingParserTestBase : ToolingTestBase, IParserTest
internal virtual RazorSyntaxTree ParseDocument(RazorLanguageVersion version, string document, IEnumerable<DirectiveDescriptor> directives, bool designTime = false, RazorParserFeatureFlags featureFlags = null, string fileKind = null)
{
directives ??= Array.Empty<DirectiveDescriptor>();
directives ??= [];
var source = TestRazorSourceDocument.Create(document, filePath: null, relativePath: null, normalizeNewLines: true);
var options = CreateParserOptions(version, directives, designTime, EnableSpanEditHandlers, featureFlags, fileKind);
var context = new ParserContext(source, options);
using var context = new ParserContext(source, options);
var codeParser = new CSharpCodeParser(directives, context);
var markupParser = new HtmlMarkupParser(context);
@ -195,11 +195,11 @@ public abstract class ToolingParserTestBase : ToolingTestBase, IParserTest
var root = markupParser.ParseDocument().CreateRed();
var diagnostics = context.ErrorSink.Errors;
var diagnostics = context.ErrorSink.GetErrorsAndClear();
var codeDocument = RazorCodeDocument.Create(source);
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options);
var syntaxTree = new RazorSyntaxTree(root, source, diagnostics, options);
codeDocument.SetSyntaxTree(syntaxTree);
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass();

Просмотреть файл

@ -378,7 +378,7 @@ public class RazorSyntaxTreePartialParserTest(ITestOutputHelper testOutput) : To
Assert.Equal(PartialParseResultInternal.Accepted | additionalFlags, result);
var newSource = TestRazorSourceDocument.Create(edit.NewSnapshot.GetText());
var newSyntaxTree = RazorSyntaxTree.Create(parser.ModifiedSyntaxTreeRoot, newSource, parser.OriginalSyntaxTree.Diagnostics, parser.OriginalSyntaxTree.Options);
var newSyntaxTree = new RazorSyntaxTree(parser.ModifiedSyntaxTreeRoot, newSource, parser.OriginalSyntaxTree.Diagnostics, parser.OriginalSyntaxTree.Options);
BaselineTest(newSyntaxTree);
}

Просмотреть файл

@ -551,7 +551,7 @@ public class VisualStudioRazorParserIntegrationTest : VisualStudioTestBase
}
var sourceDocument = TestRazorSourceDocument.Create(content);
var syntaxTree = RazorSyntaxTree.Create(manager.PartialParsingSyntaxTreeRoot, sourceDocument, manager.CurrentSyntaxTree!.Diagnostics, manager.CurrentSyntaxTree.Options);
var syntaxTree = new RazorSyntaxTree(manager.PartialParsingSyntaxTreeRoot, sourceDocument, manager.CurrentSyntaxTree!.Diagnostics, manager.CurrentSyntaxTree.Options);
BaselineTest(syntaxTree);
}

Просмотреть файл

@ -192,13 +192,13 @@ public abstract class ParserTestBase : IParserTest
internal virtual RazorSyntaxTree ParseDocument(RazorLanguageVersion version, string document, IEnumerable<DirectiveDescriptor> directives, bool designTime = false, RazorParserFeatureFlags featureFlags = null, string fileKind = null)
{
directives = directives ?? Array.Empty<DirectiveDescriptor>();
directives = directives ?? [];
var source = TestRazorSourceDocument.Create(document, filePath: null, relativePath: null, normalizeNewLines: true);
var options = CreateParserOptions(version, directives, designTime, _validateSpanEditHandlers, featureFlags, fileKind);
var context = new ParserContext(source, options);
using var context = new ParserContext(source, options);
var codeParser = new CSharpCodeParser(directives, context);
var markupParser = new HtmlMarkupParser(context);
@ -207,11 +207,11 @@ public abstract class ParserTestBase : IParserTest
var root = markupParser.ParseDocument().CreateRed();
var diagnostics = context.ErrorSink.Errors;
var diagnostics = context.ErrorSink.GetErrorsAndClear();
var codeDocument = RazorCodeDocument.Create(source);
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options);
var syntaxTree = new RazorSyntaxTree(root, source, diagnostics, options);
codeDocument.SetSyntaxTree(syntaxTree);
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass();

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using Xunit;
using SR = Microsoft.AspNetCore.Razor.Utilities.Shared.Resources.SR;
@ -141,260 +140,4 @@ public class EnumerableExtensionsTests
public static CustomReadOnlyCollection Create(ReadOnlySpan<int> span)
=> new(span);
}
private static Comparison<int> OddBeforeEven
=> (x, y) => (x % 2 != 0, y % 2 != 0) switch
{
(true, false) => -1,
(false, true) => 1,
_ => x.CompareTo(y)
};
public readonly record struct ValueHolder(int Value)
{
public static implicit operator ValueHolder(int value)
=> new(value);
}
public static TheoryData<IEnumerable<int>, ImmutableArray<int>> OrderTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
public static TheoryData<IEnumerable<int>, ImmutableArray<int>> OrderTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
};
public static TheoryData<IEnumerable<int>, ImmutableArray<int>> OrderDescendingTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
public static TheoryData<IEnumerable<int>, ImmutableArray<int>> OrderDescendingTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
};
public static TheoryData<IEnumerable<ValueHolder>, ImmutableArray<ValueHolder>> OrderByTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
public static TheoryData<IEnumerable<ValueHolder>, ImmutableArray<ValueHolder>> OrderByTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
};
public static TheoryData<IEnumerable<ValueHolder>, ImmutableArray<ValueHolder>> OrderByDescendingTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
public static TheoryData<IEnumerable<ValueHolder>, ImmutableArray<ValueHolder>> OrderByDescendingTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
};
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_OddBeforeEven(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_OddBeforeEven(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray(IEnumerable<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_OddBeforeEven(IEnumerable<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray(IEnumerable<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_OddBeforeEven(IEnumerable<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
#if NET // Enumerable.Order(...) and Enumerable.OrderDescending(...) were introduced in .NET 7
[Fact]
public void OrderAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(),
testFunction: data => data.OrderAsArray());
}
[Fact]
public void OrderAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(),
testFunction: data => data.OrderDescendingAsArray());
}
[Fact]
public void OrderDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
#endif
[Fact]
public void OrderByAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text),
testFunction: data => data.OrderByAsArray(static x => x?.Text));
}
[Fact]
public void OrderByAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderByDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text));
}
[Fact]
public void OrderByDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
private static void OrderAndAssertStableSort(
Func<IEnumerable<StringHolder?>, IEnumerable<StringHolder?>> linqFunction,
Func<IEnumerable<StringHolder?>, ImmutableArray<StringHolder?>> testFunction)
{
IEnumerable<StringHolder?> data = [
"All", "Your", "Base", "Are", "belong", null, "To", "Us",
"all", "your", null, "Base", "are", "belong", "to", "us"];
var expected = linqFunction(data);
var actual = testFunction(data);
Assert.Equal<StringHolder?>(expected, actual, ReferenceEquals);
}
}

Просмотреть файл

@ -0,0 +1,176 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test;
public class EnumerableOrderingTests : EnumerableOrderingTestBase
{
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_OddBeforeEven(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_OddBeforeEven(IEnumerable<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray(IEnumerable<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_OddBeforeEven(IEnumerable<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray(IEnumerable<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_OddBeforeEven(IEnumerable<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
#if NET // Enumerable.Order(...) and Enumerable.OrderDescending(...) were introduced in .NET 7
[Fact]
public void OrderAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(),
testFunction: data => data.OrderAsArray());
}
[Fact]
public void OrderAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(),
testFunction: data => data.OrderDescendingAsArray());
}
[Fact]
public void OrderDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
#endif
[Fact]
public void OrderByAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text),
testFunction: data => data.OrderByAsArray(static x => x?.Text));
}
[Fact]
public void OrderByAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderByDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text));
}
[Fact]
public void OrderByDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
private static void OrderAndAssertStableSort(
Func<IEnumerable<StringHolder?>, IEnumerable<StringHolder?>> linqFunction,
Func<IEnumerable<StringHolder?>, ImmutableArray<StringHolder?>> testFunction)
{
IEnumerable<StringHolder?> data = [
"All", "Your", "Base", "Are", "belong", null, "To", "Us",
"all", "your", null, "Base", "are", "belong", "to", "us"];
var expected = linqFunction(data);
var actual = testFunction(data);
Assert.Equal<StringHolder?>(expected, actual, ReferenceEquals);
}
}

Просмотреть файл

@ -2,10 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test;
@ -39,748 +36,4 @@ public class ImmutableArrayExtensionsTests
},
s => Assert.Equal("WoRlD", s));
}
private static Comparison<int> OddBeforeEven
=> (x, y) => (x % 2 != 0, y % 2 != 0) switch
{
(true, false) => -1,
(false, true) => 1,
_ => x.CompareTo(y)
};
public readonly record struct ValueHolder(int Value)
{
public static implicit operator ValueHolder(int value)
=> new(value);
}
public static TheoryData<ImmutableArray<int>, ImmutableArray<int>> OrderTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
public static TheoryData<ImmutableArray<int>, ImmutableArray<int>> OrderTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
};
public static TheoryData<ImmutableArray<int>, ImmutableArray<int>> OrderDescendingTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
public static TheoryData<ImmutableArray<int>, ImmutableArray<int>> OrderDescendingTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
};
public static TheoryData<ImmutableArray<ValueHolder>, ImmutableArray<ValueHolder>> OrderByTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
public static TheoryData<ImmutableArray<ValueHolder>, ImmutableArray<ValueHolder>> OrderByTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
};
public static TheoryData<ImmutableArray<ValueHolder>, ImmutableArray<ValueHolder>> OrderByDescendingTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
public static TheoryData<ImmutableArray<ValueHolder>, ImmutableArray<ValueHolder>> OrderByDescendingTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
};
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray_ReadOnlyList(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray_ReadOnlyList(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderDescendingAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderDescendingAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray_ReadOnlyList(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder>)data;
var sorted = readOnlyList.OrderByAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder>)data;
var sorted = readOnlyList.OrderByAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray_ReadOnlyList(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder>)data;
var sorted = readOnlyList.OrderByDescendingAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder>)data;
var sorted = readOnlyList.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray_Enumerable(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_Enumerable_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray_Enumerable(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_Enumerable_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray_Enumerable(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_Enumerable_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray_Enumerable(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_Enumerable_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void ToImmutableOrdered(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrdered();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void ToImmutableOrdered_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrdered(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void ToImmutableOrderedDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedDescending();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void ToImmutableOrderedDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedDescending(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void ToImmutableOrderedBy(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedBy(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void ToImmutableOrderedBy_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedBy(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void ToImmutableOrderedByDescending(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedByDescending(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void ToImmutableOrderedByDescending_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedByDescending(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void DrainToImmutableOrdered(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrdered();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void DrainToImmutableOrdered_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrdered(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void DrainToImmutableOrderedDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedDescending();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void DrainToImmutableOrderedDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedDescending(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void DrainToImmutableOrderedBy(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedBy(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void DrainToImmutableOrderedBy_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedBy(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void DrainToImmutableOrderedByDescending(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedByDescending(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void DrainToImmutableOrderedByDescending_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedByDescending(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Fact]
public void OrderAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<int>.Empty);
var immutableArray = ImmutableArray<int>.Empty;
immutableArray = immutableArray.OrderAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderAsArray_SingleElementArrayReturnsSameArray()
{
var array = new int[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderAsArray_SortedArrayReturnsSameArray()
{
var values = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
immutableArray = immutableArray.OrderAsArray();
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderDescendingAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<int>.Empty);
var immutableArray = ImmutableArray<int>.Empty;
immutableArray = immutableArray.OrderDescendingAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderDescendingAsArray_SingleElementArrayReturnsSameArray()
{
var array = new int[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderDescendingAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderDescendingAsArray_SortedArrayReturnsSameArray()
{
var values = new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
var presortedArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
presortedArray = presortedArray.OrderDescendingAsArray();
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(presortedArray));
}
[Fact]
public void OrderByAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<ValueHolder>.Empty);
var immutableArray = ImmutableArray<ValueHolder>.Empty;
immutableArray = immutableArray.OrderByAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByAsArray_SingleElementArrayReturnsSameArray()
{
var array = new ValueHolder[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderByAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByAsArray_SortedArrayReturnsSameArray()
{
var values = new ValueHolder[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var presortedArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
presortedArray = presortedArray.OrderByAsArray(static x => x.Value);
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(presortedArray));
}
[Fact]
public void OrderByDescendingAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<ValueHolder>.Empty);
var immutableArray = ImmutableArray<ValueHolder>.Empty;
immutableArray = immutableArray.OrderByDescendingAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByDescendingAsArray_SingleElementArrayReturnsSameArray()
{
var array = new ValueHolder[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderByDescendingAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByDescendingAsArray_SortedArrayReturnsSameArray()
{
var values = new ValueHolder[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
var presortedArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
presortedArray = presortedArray.OrderByDescendingAsArray(static x => x.Value);
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(presortedArray));
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void UnsafeOrder(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().Order();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void UnsafeOrder_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().Order(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void UnsafeOrderDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderDescending();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void UnsafeOrderDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderDescending(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void UnsafeOrderBy(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderBy(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void UnsafeOrderBy_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderBy(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void UnsafeOrderByDescending(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderByDescending(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void UnsafeOrderByDescending_OddBeforeEven(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderByDescending(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
#if NET // Enumerable.Order(...) and Enumerable.OrderDescending(...) were introduced in .NET 7
[Fact]
public void OrderAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(),
testFunction: data => data.OrderAsArray());
}
[Fact]
public void OrderAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(),
testFunction: data => data.OrderDescendingAsArray());
}
[Fact]
public void OrderDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
#endif
[Fact]
public void OrderByAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text),
testFunction: data => data.OrderByAsArray(static x => x?.Text));
}
[Fact]
public void OrderByAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderByDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text));
}
[Fact]
public void OrderByDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
private static void OrderAndAssertStableSort(
Func<ImmutableArray<StringHolder?>, IEnumerable<StringHolder?>> linqFunction,
Func<ImmutableArray<StringHolder?>, ImmutableArray<StringHolder?>> testFunction)
{
ImmutableArray<StringHolder?> data = [
"All", "Your", "Base", "Are", "belong", null, "To", "Us",
"all", "your", null, "Base", "are", "belong", "to", "us"];
var expected = linqFunction(data);
var actual = testFunction(data);
Assert.Equal<StringHolder?>(expected, actual, ReferenceEquals);
}
}

Просмотреть файл

@ -0,0 +1,665 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test;
public class ImmutableArrayOrderingTests : ImmutableArrayOrderingTestBase
{
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray_ReadOnlyList(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray_ReadOnlyList(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderDescendingAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var readOnlyList = (IReadOnlyList<int>)data;
var sorted = readOnlyList.OrderDescendingAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray_ReadOnlyList(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder<int>>)data;
var sorted = readOnlyList.OrderByAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder<int>>)data;
var sorted = readOnlyList.OrderByAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray_ReadOnlyList(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder<int>>)data;
var sorted = readOnlyList.OrderByDescendingAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_ReadOnlyList_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var readOnlyList = (IReadOnlyList<ValueHolder<int>>)data;
var sorted = readOnlyList.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray_Enumerable(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_Enumerable_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray_Enumerable(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_Enumerable_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray_Enumerable(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_Enumerable_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray_Enumerable(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_Enumerable_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void ToImmutableOrdered(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrdered();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void ToImmutableOrdered_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrdered(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void ToImmutableOrderedDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedDescending();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void ToImmutableOrderedDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedDescending(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void ToImmutableOrderedBy(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedBy(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void ToImmutableOrderedBy_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedBy(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void ToImmutableOrderedByDescending(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedByDescending(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void ToImmutableOrderedByDescending_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.ToImmutableOrderedByDescending(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void DrainToImmutableOrdered(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrdered();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void DrainToImmutableOrdered_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrdered(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void DrainToImmutableOrderedDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedDescending();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void DrainToImmutableOrderedDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedDescending(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void DrainToImmutableOrderedBy(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedBy(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void DrainToImmutableOrderedBy_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedBy(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void DrainToImmutableOrderedByDescending(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedByDescending(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void DrainToImmutableOrderedByDescending_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builder = data.ToBuilder();
var sorted = builder.DrainToImmutableOrderedByDescending(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Fact]
public void OrderAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<int>.Empty);
var immutableArray = ImmutableArray<int>.Empty;
immutableArray = immutableArray.OrderAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderAsArray_SingleElementArrayReturnsSameArray()
{
var array = new int[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderAsArray_SortedArrayReturnsSameArray()
{
var values = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
immutableArray = immutableArray.OrderAsArray();
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderDescendingAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<int>.Empty);
var immutableArray = ImmutableArray<int>.Empty;
immutableArray = immutableArray.OrderDescendingAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderDescendingAsArray_SingleElementArrayReturnsSameArray()
{
var array = new int[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderDescendingAsArray();
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderDescendingAsArray_SortedArrayReturnsSameArray()
{
var values = new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
var presortedArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
presortedArray = presortedArray.OrderDescendingAsArray();
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(presortedArray));
}
[Fact]
public void OrderByAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<ValueHolder<int>>.Empty);
var immutableArray = ImmutableArray<ValueHolder<int>>.Empty;
immutableArray = immutableArray.OrderByAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByAsArray_SingleElementArrayReturnsSameArray()
{
var array = new ValueHolder<int>[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderByAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByAsArray_SortedArrayReturnsSameArray()
{
var values = new ValueHolder<int>[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var presortedArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
presortedArray = presortedArray.OrderByAsArray(static x => x.Value);
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(presortedArray));
}
[Fact]
public void OrderByDescendingAsArray_EmptyArrayReturnsSameArray()
{
var array = ImmutableCollectionsMarshal.AsArray(ImmutableArray<ValueHolder<int>>.Empty);
var immutableArray = ImmutableArray<ValueHolder<int>>.Empty;
immutableArray = immutableArray.OrderByDescendingAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByDescendingAsArray_SingleElementArrayReturnsSameArray()
{
var array = new ValueHolder<int>[] { 42 };
var immutableArray = ImmutableCollectionsMarshal.AsImmutableArray(array);
immutableArray = immutableArray.OrderByDescendingAsArray(static x => x.Value);
Assert.Same(array, ImmutableCollectionsMarshal.AsArray(immutableArray));
}
[Fact]
public void OrderByDescendingAsArray_SortedArrayReturnsSameArray()
{
var values = new ValueHolder<int>[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
var presortedArray = ImmutableCollectionsMarshal.AsImmutableArray(values);
presortedArray = presortedArray.OrderByDescendingAsArray(static x => x.Value);
Assert.Same(values, ImmutableCollectionsMarshal.AsArray(presortedArray));
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void UnsafeOrder(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().Order();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void UnsafeOrder_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().Order(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void UnsafeOrderDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderDescending();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void UnsafeOrderDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderDescending(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void UnsafeOrderBy(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderBy(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void UnsafeOrderBy_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderBy(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void UnsafeOrderByDescending(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderByDescending(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void UnsafeOrderByDescending_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
// Clone array, since we're modifying it in-place.
var sorted = ImmutableArray.Create(data.AsSpan());
sorted.Unsafe().OrderByDescending(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
#if NET // Enumerable.Order(...) and Enumerable.OrderDescending(...) were introduced in .NET 7
[Fact]
public void OrderAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(),
testFunction: data => data.OrderAsArray());
}
[Fact]
public void OrderAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(),
testFunction: data => data.OrderDescendingAsArray());
}
[Fact]
public void OrderDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
#endif
[Fact]
public void OrderByAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text),
testFunction: data => data.OrderByAsArray(static x => x?.Text));
}
[Fact]
public void OrderByAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderByDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text));
}
[Fact]
public void OrderByDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
private static void OrderAndAssertStableSort(
Func<ImmutableArray<StringHolder?>, IEnumerable<StringHolder?>> linqFunction,
Func<ImmutableArray<StringHolder?>, ImmutableArray<StringHolder?>> testFunction)
{
ImmutableArray<StringHolder?> data = [
"All", "Your", "Base", "Are", "belong", null, "To", "Us",
"all", "your", null, "Base", "are", "belong", "to", "us"];
var expected = linqFunction(data);
var actual = testFunction(data);
Assert.Equal<StringHolder?>(expected, actual, ReferenceEquals);
}
}

Просмотреть файл

@ -4,6 +4,7 @@
<TargetFrameworks>$(DefaultNetCoreTargetFrameworks)</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);$(DefaultNetFxTargetFramework)</TargetFrameworks>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<IsShipping>false</IsShipping>
</PropertyGroup>
<ItemGroup>

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.PooledObjects;
internal static class Extensions
{
public static void Validate<T>(this ref readonly PooledArrayBuilder<T> builder, Action<PooledArrayBuilder<T>.TestAccessor> validator)
{
validator(builder.GetTestAccessor());
}
}

Просмотреть файл

@ -0,0 +1,216 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.PooledObjects;
public class PooledArrayBuilderOrderingTests : ImmutableArrayOrderingTestBase
{
[Theory]
[MemberData(nameof(OrderTestData))]
public void ToImmutableOrdered(ImmutableArray<int> data, ImmutableArray<int> expected)
{
using var builder = new PooledArrayBuilder<int>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrdered();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void ToImmutableOrdered_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
using var builder = new PooledArrayBuilder<int>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrdered(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void ToImmutableOrderedDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
using var builder = new PooledArrayBuilder<int>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrderedDescending();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void ToImmutableOrderedDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
using var builder = new PooledArrayBuilder<int>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrderedDescending(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void ToImmutableOrderedBy(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrderedBy(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void ToImmutableOrderedBy_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrderedBy(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void ToImmutableOrderedByDescending(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrderedByDescending(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void ToImmutableOrderedByDescending_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length);
builder.AddRange(data);
var sorted = builder.ToImmutableOrderedByDescending(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void DrainToImmutableOrdered(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builderPool = TestArrayBuilderPool<int>.Create();
using var builder = new PooledArrayBuilder<int>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrdered();
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void DrainToImmutableOrdered_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builderPool = TestArrayBuilderPool<int>.Create();
using var builder = new PooledArrayBuilder<int>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrdered(OddBeforeEven);
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void DrainToImmutableOrderedDescending(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builderPool = TestArrayBuilderPool<int>.Create();
using var builder = new PooledArrayBuilder<int>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrderedDescending();
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void DrainToImmutableOrderedDescending_OddBeforeEven(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var builderPool = TestArrayBuilderPool<int>.Create();
using var builder = new PooledArrayBuilder<int>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrderedDescending(OddBeforeEven);
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void DrainToImmutableOrderedBy(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builderPool = TestArrayBuilderPool<ValueHolder<int>>.Create();
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrderedBy(static x => x.Value);
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void DrainToImmutableOrderedBy_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builderPool = TestArrayBuilderPool<ValueHolder<int>>.Create();
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrderedBy(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void DrainToImmutableOrderedByDescending(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builderPool = TestArrayBuilderPool<ValueHolder<int>>.Create();
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrderedByDescending(static x => x.Value);
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void DrainToImmutableOrderedByDescending_OddBeforeEven(ImmutableArray<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var builderPool = TestArrayBuilderPool<ValueHolder<int>>.Create();
using var builder = new PooledArrayBuilder<ValueHolder<int>>(capacity: data.Length, builderPool);
builder.AddRange(data);
var sorted = builder.DrainToImmutableOrderedByDescending(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
AssertIsDrained(in builder);
}
private static void AssertIsDrained<T>(ref readonly PooledArrayBuilder<T> builder)
{
builder.Validate(static t =>
{
Assert.NotNull(t.InnerArrayBuilder);
Assert.Empty(t.InnerArrayBuilder);
// After draining, the capacity of the inner array builder should be 0.
Assert.Equal(0, t.InnerArrayBuilder.Capacity);
});
}
}

Просмотреть файл

@ -86,6 +86,61 @@ public class PooledArrayBuilderTests
}
}
[Fact]
public void UseDrainAndReuse()
{
var builderPool = TestArrayBuilderPool<int>.Create();
using var builder = new PooledArrayBuilder<int>(capacity: 10, builderPool);
for (var i = 0; i < 10; i++)
{
builder.Add(i);
}
// Verify that the builder contains 10 items within its inner array builder.
builder.Validate(static t =>
{
Assert.Equal(0, t.InlineItemCount);
Assert.Equal(10, t.Capacity);
Assert.NotNull(t.InnerArrayBuilder);
Assert.Equal(10, t.InnerArrayBuilder.Count);
Assert.Equal(10, t.InnerArrayBuilder.Capacity);
});
var result = builder.DrainToImmutable();
// After draining, the builder should contain 0 items in its inner array builder
// and the capacity should have been set to 0.
builder.Validate(static t =>
{
Assert.Equal(0, t.InlineItemCount);
Assert.Equal(10, t.Capacity);
Assert.NotNull(t.InnerArrayBuilder);
Assert.Empty(t.InnerArrayBuilder);
Assert.Equal(0, t.InnerArrayBuilder.Capacity);
});
// Add another 10 items to the builder. At the end, the inner array builder
// should hold 10 items with a capacity of 10.
for (var i = 0; i < 10; i++)
{
builder.Add(i);
}
// Verify that the builder contains 10 items within its inner array builder.
builder.Validate(static t =>
{
Assert.Equal(0, t.InlineItemCount);
Assert.Equal(10, t.Capacity);
Assert.NotNull(t.InnerArrayBuilder);
Assert.Equal(10, t.InnerArrayBuilder.Count);
Assert.Equal(10, t.InnerArrayBuilder.Capacity);
});
}
private static Func<int, bool> IsEven => x => x % 2 == 0;
private static Func<int, bool> IsOdd => x => x % 2 != 0;

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.Extensions.ObjectPool;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.PooledObjects;
internal static class TestArrayBuilderPool<T>
{
public static ObjectPool<ImmutableArray<T>.Builder> Create(
IPooledObjectPolicy<ImmutableArray<T>.Builder>? policy = null, int size = 1)
=> DefaultPool.Create(policy ?? NoReturnPolicy.Instance, size);
public sealed class NoReturnPolicy : IPooledObjectPolicy<ImmutableArray<T>.Builder>
{
public static readonly NoReturnPolicy Instance = new();
private NoReturnPolicy()
{
}
public ImmutableArray<T>.Builder Create()
=> ImmutableArray.CreateBuilder<T>();
public bool Return(ImmutableArray<T>.Builder obj)
=> false;
}
}

Просмотреть файл

@ -278,332 +278,4 @@ public class ReadOnlyListExtensionsTest
public static CustomReadOnlyList Create(ReadOnlySpan<int> span)
=> new(span);
}
private static Comparison<int> OddBeforeEven
=> (x, y) => (x % 2 != 0, y % 2 != 0) switch
{
(true, false) => -1,
(false, true) => 1,
_ => x.CompareTo(y)
};
public readonly record struct ValueHolder(int Value)
{
public static implicit operator ValueHolder(int value)
=> new(value);
}
public static TheoryData<IReadOnlyList<int>, ImmutableArray<int>> OrderTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
public static TheoryData<IReadOnlyList<int>, ImmutableArray<int>> OrderTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
};
public static TheoryData<IReadOnlyList<int>, ImmutableArray<int>> OrderDescendingTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
public static TheoryData<IReadOnlyList<int>, ImmutableArray<int>> OrderDescendingTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
};
public static TheoryData<IReadOnlyList<ValueHolder>, ImmutableArray<ValueHolder>> OrderByTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
public static TheoryData<IReadOnlyList<ValueHolder>, ImmutableArray<ValueHolder>> OrderByTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] },
};
public static TheoryData<IReadOnlyList<ValueHolder>, ImmutableArray<ValueHolder>> OrderByDescendingTestData
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
public static TheoryData<IReadOnlyList<ValueHolder>, ImmutableArray<ValueHolder>> OrderByDescendingTestData_OddBeforeEven
=> new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 8, 6, 4, 2, 9, 7, 5, 3, 1] },
};
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_OddBeforeEven(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_OddBeforeEven(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray_Enumerable(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_Enumerable_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray_Enumerable(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray();
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_Enumerable_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray(OddBeforeEven);
Assert.Equal<int>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray_Enumerable(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_Enumerable_OddBeforeEven(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray_Enumerable(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_Enumerable_OddBeforeEven(IReadOnlyList<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var enumerable = (IEnumerable<ValueHolder>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
Assert.Equal<ValueHolder>(expected, sorted);
}
#if NET // Enumerable.Order(...) and Enumerable.OrderDescending(...) were introduced in .NET 7
[Fact]
public void OrderAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(),
testFunction: data => data.OrderAsArray());
}
[Fact]
public void OrderAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(),
testFunction: data => data.OrderDescendingAsArray());
}
[Fact]
public void OrderDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
#endif
[Fact]
public void OrderByAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text),
testFunction: data => data.OrderByAsArray(static x => x?.Text));
}
[Fact]
public void OrderByAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderByDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text));
}
[Fact]
public void OrderByDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
private static void OrderAndAssertStableSort(
Func<IReadOnlyList<StringHolder?>, IEnumerable<StringHolder?>> linqFunction,
Func<IReadOnlyList<StringHolder?>, ImmutableArray<StringHolder?>> testFunction)
{
IReadOnlyList<StringHolder?> data = [
"All", "Your", "Base", "Are", "belong", null, "To", "Us",
"all", "your", null, "Base", "are", "belong", "to", "us"];
var expected = linqFunction(data);
var actual = testFunction(data);
Assert.Equal<StringHolder?>(expected, actual, ReferenceEquals);
}
}

Просмотреть файл

@ -0,0 +1,248 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test;
public class ReadOnlyListOrderingTests : ReadOnlyListOrderingTestBase
{
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderDescendingAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_OddBeforeEven(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_OddBeforeEven(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData))]
public void OrderAsArray_Enumerable(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderTestData_OddBeforeEven))]
public void OrderAsArray_Enumerable_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData))]
public void OrderDescendingAsArray_Enumerable(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray();
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderDescendingTestData_OddBeforeEven))]
public void OrderDescendingAsArray_Enumerable_OddBeforeEven(IReadOnlyList<int> data, ImmutableArray<int> expected)
{
var enumerable = (IEnumerable<int>)data;
var sorted = enumerable.OrderDescendingAsArray(OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData))]
public void OrderByAsArray_Enumerable(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByTestData_OddBeforeEven))]
public void OrderByAsArray_Enumerable_OddBeforeEven(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData))]
public void OrderByDescendingAsArray_Enumerable(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value);
AssertEqual(expected, sorted);
}
[Theory]
[MemberData(nameof(OrderByDescendingTestData_OddBeforeEven))]
public void OrderByDescendingAsArray_Enumerable_OddBeforeEven(IReadOnlyList<ValueHolder<int>> data, ImmutableArray<ValueHolder<int>> expected)
{
var enumerable = (IEnumerable<ValueHolder<int>>)data;
var sorted = enumerable.OrderByDescendingAsArray(static x => x.Value, OddBeforeEven);
AssertEqual(expected, sorted);
}
#if NET // Enumerable.Order(...) and Enumerable.OrderDescending(...) were introduced in .NET 7
[Fact]
public void OrderAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(),
testFunction: data => data.OrderAsArray());
}
[Fact]
public void OrderAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.Order(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(),
testFunction: data => data.OrderDescendingAsArray());
}
[Fact]
public void OrderDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.Ordinal),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.Ordinal));
OrderAndAssertStableSort(
linqFunction: data => data.OrderDescending(StringHolder.Comparer.OrdinalIgnoreCase),
testFunction: data => data.OrderDescendingAsArray(StringHolder.Comparer.OrdinalIgnoreCase));
}
#endif
[Fact]
public void OrderByAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text),
testFunction: data => data.OrderByAsArray(static x => x?.Text));
}
[Fact]
public void OrderByAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderBy(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void OrderByDescendingAsArray_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text));
}
[Fact]
public void OrderByDescendingAsArray_Comparer_IsStable()
{
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
OrderAndAssertStableSort(
linqFunction: data => data.OrderByDescending(static x => x?.Text, StringComparer.OrdinalIgnoreCase),
testFunction: data => data.OrderByDescendingAsArray(static x => x?.Text, StringComparer.OrdinalIgnoreCase));
}
private static void OrderAndAssertStableSort(
Func<IReadOnlyList<StringHolder?>, IEnumerable<StringHolder?>> linqFunction,
Func<IReadOnlyList<StringHolder?>, ImmutableArray<StringHolder?>> testFunction)
{
IReadOnlyList<StringHolder?> data = [
"All", "Your", "Base", "Are", "belong", null, "To", "Us",
"all", "your", null, "Base", "are", "belong", "to", "us"];
var expected = linqFunction(data);
var actual = testFunction(data);
Assert.Equal<StringHolder?>(expected, actual, ReferenceEquals);
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
public abstract class EnumerableOrderingTestBase : OrderingTestBase<IEnumerable<int>, IEnumerable<ValueHolder<int>>, OrderingCaseConverters.Enumerable>
{
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
public abstract class ImmutableArrayOrderingTestBase : OrderingTestBase<ImmutableArray<int>, ImmutableArray<ValueHolder<int>>, OrderingCaseConverters.ImmutableArray>
{
}

Просмотреть файл

@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
public interface IOrderingCaseConverter<TOrderCollection, TOrderByCollection>
where TOrderCollection : IEnumerable<int>
where TOrderByCollection : IEnumerable<ValueHolder<int>>
{
TOrderCollection ConvertOrderCase(ImmutableArray<int> data);
TOrderByCollection ConvertOrderByCase(ImmutableArray<ValueHolder<int>> data);
}
public static class OrderingCaseConverters
{
public sealed class Enumerable : IOrderingCaseConverter<IEnumerable<int>, IEnumerable<ValueHolder<int>>>
{
public IEnumerable<int> ConvertOrderCase(ImmutableArray<int> data) => data;
public IEnumerable<ValueHolder<int>> ConvertOrderByCase(ImmutableArray<ValueHolder<int>> data) => data;
}
public sealed class ImmutableArray : IOrderingCaseConverter<ImmutableArray<int>, ImmutableArray<ValueHolder<int>>>
{
public ImmutableArray<int> ConvertOrderCase(ImmutableArray<int> data) => data;
public ImmutableArray<ValueHolder<int>> ConvertOrderByCase(ImmutableArray<ValueHolder<int>> data) => data;
}
public sealed class ReadOnlyList : IOrderingCaseConverter<IReadOnlyList<int>, IReadOnlyList<ValueHolder<int>>>
{
public IReadOnlyList<int> ConvertOrderCase(ImmutableArray<int> data) => data;
public IReadOnlyList<ValueHolder<int>> ConvertOrderByCase(ImmutableArray<ValueHolder<int>> data) => data;
}
}

Просмотреть файл

@ -0,0 +1,84 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
public abstract class OrderingTestBase<TOrderCollection, TOrderByCollection, TCaseConverter>
where TOrderCollection : IEnumerable<int>
where TOrderByCollection : IEnumerable<ValueHolder<int>>
where TCaseConverter : IOrderingCaseConverter<TOrderCollection, TOrderByCollection>, new()
{
private static readonly TheoryData<TOrderCollection, ImmutableArray<int>> s_orderTestData = [];
private static readonly TheoryData<TOrderCollection, ImmutableArray<int>> s_orderTestData_OddBeforeEven = [];
private static readonly TheoryData<TOrderCollection, ImmutableArray<int>> s_orderDescendingTestData = [];
private static readonly TheoryData<TOrderCollection, ImmutableArray<int>> s_orderDescendingTestData_OddBeforeEven = [];
private static readonly TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> s_orderByTestData = [];
private static readonly TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> s_orderByTestData_OddBeforeEven = [];
private static readonly TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> s_orderByDescendingTestData = [];
private static readonly TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> s_orderByDescendingTestData_OddBeforeEven = [];
protected static void AddCase(TOrderCollection collection)
{
s_orderTestData.Add(collection, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
s_orderTestData_OddBeforeEven.Add(collection, [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]);
s_orderDescendingTestData.Add(collection, [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
s_orderDescendingTestData_OddBeforeEven.Add(collection, [10, 8, 6, 4, 2, 9, 7, 5, 3, 1]);
}
protected static void AddCase(TOrderByCollection collection)
{
s_orderByTestData.Add(collection, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
s_orderByTestData_OddBeforeEven.Add(collection, [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]);
s_orderByDescendingTestData.Add(collection, [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
s_orderByDescendingTestData_OddBeforeEven.Add(collection, [10, 8, 6, 4, 2, 9, 7, 5, 3, 1]);
}
static OrderingTestBase()
{
var converter = new TCaseConverter();
AddCase(converter.ConvertOrderCase([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
AddCase(converter.ConvertOrderCase([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]));
AddCase(converter.ConvertOrderCase([1, 3, 5, 7, 9, 2, 4, 6, 8, 10]));
AddCase(converter.ConvertOrderCase([2, 5, 8, 1, 3, 9, 7, 4, 10, 6]));
AddCase(converter.ConvertOrderCase([6, 10, 4, 7, 9, 3, 1, 8, 5, 2]));
AddCase(converter.ConvertOrderByCase([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
AddCase(converter.ConvertOrderByCase([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]));
AddCase(converter.ConvertOrderByCase([1, 3, 5, 7, 9, 2, 4, 6, 8, 10]));
AddCase(converter.ConvertOrderByCase([2, 5, 8, 1, 3, 9, 7, 4, 10, 6]));
AddCase(converter.ConvertOrderByCase([6, 10, 4, 7, 9, 3, 1, 8, 5, 2]));
}
protected static Comparison<int> OddBeforeEven
=> (x, y) => (x % 2 != 0, y % 2 != 0) switch
{
(true, false) => -1,
(false, true) => 1,
_ => x.CompareTo(y)
};
public static TheoryData<TOrderCollection, ImmutableArray<int>> OrderTestData => s_orderTestData;
public static TheoryData<TOrderCollection, ImmutableArray<int>> OrderTestData_OddBeforeEven => s_orderTestData_OddBeforeEven;
public static TheoryData<TOrderCollection, ImmutableArray<int>> OrderDescendingTestData => s_orderDescendingTestData;
public static TheoryData<TOrderCollection, ImmutableArray<int>> OrderDescendingTestData_OddBeforeEven => s_orderDescendingTestData_OddBeforeEven;
public static TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> OrderByTestData => s_orderByTestData;
public static TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> OrderByTestData_OddBeforeEven => s_orderByTestData_OddBeforeEven;
public static TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> OrderByDescendingTestData => s_orderByDescendingTestData;
public static TheoryData<TOrderByCollection, ImmutableArray<ValueHolder<int>>> OrderByDescendingTestData_OddBeforeEven => s_orderByDescendingTestData_OddBeforeEven;
protected void AssertEqual<T>(ImmutableArray<T> result, ImmutableArray<T> expected)
{
Assert.Equal<T>(result, expected);
}
protected void AssertEqual<T>(ImmutableArray<T> result, ImmutableArray<T> expected, IEqualityComparer<T> comparer)
{
Assert.Equal(result, expected, comparer);
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
public abstract class ReadOnlyListOrderingTestBase : OrderingTestBase<IReadOnlyList<int>, IReadOnlyList<ValueHolder<int>>, OrderingCaseConverters.ReadOnlyList>
{
}

Просмотреть файл

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
public sealed class StringHolder(string? text) : IComparable<StringHolder>
{

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.TestData;
public readonly record struct ValueHolder<T>(T Value)
{
public static implicit operator ValueHolder<T>(T value)
=> new(value);
}

Просмотреть файл

@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.Extensions.ObjectPool;
namespace Microsoft.AspNetCore.Razor.PooledObjects;
@ -33,6 +34,8 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
/// </summary>
private const int InlineCapacity = 4;
private ObjectPool<ImmutableArray<T>.Builder>? _builderPool;
/// <summary>
/// A builder to be used as storage after the first time that the number
/// of items exceeds <see cref="InlineCapacity"/>. Once the builder is used,
@ -56,9 +59,10 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
/// </summary>
private int _inlineCount;
public PooledArrayBuilder(int? capacity = null)
public PooledArrayBuilder(int? capacity = null, ObjectPool<ImmutableArray<T>.Builder>? builderPool = null)
{
_capacity = capacity is > InlineCapacity ? capacity : null;
_builderPool = builderPool;
_element0 = default!;
_element1 = default!;
_element2 = default!;
@ -79,13 +83,50 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
// Return _builder to the pool if necessary. Note that we don't need to clear the inline elements here
// because this type is intended to be allocated on the stack and the GC can reclaim objects from the
// stack after the last use of a reference to them.
if (_builder is { } builder)
if (_builder is { } innerBuilder)
{
ArrayBuilderPool<T>.Default.Return(builder);
_builderPool?.Return(innerBuilder);
_builder = null;
}
}
/// <summary>
/// Retrieves the inner <see cref="_builder"/>.
/// </summary>
/// <returns>
/// Returns <see langword="true"/> if <see cref="_builder"/> is available; otherwise <see langword="false"/>
/// </returns>
/// <remarks>
/// This should only be used by methods that will not add to the inner <see cref="_builder"/>.
/// </remarks>
private readonly bool TryGetBuilder([NotNullWhen(true)] out ImmutableArray<T>.Builder? builder)
{
builder = _builder;
return builder is not null;
}
/// <summary>
/// Retrieves the inner <see cref="_builder"/> and resets its capacity if necessary.
/// </summary>
/// <returns>
/// Returns <see langword="true"/> if <see cref="_builder"/> is available; otherwise <see langword="false"/>
/// </returns>
/// <remarks>
/// This should only be used by methods that will add to the inner <see cref="_builder"/>.
/// </remarks>
private readonly bool TryGetBuilderAndEnsureCapacity([NotNullWhen(true)] out ImmutableArray<T>.Builder? builder)
{
if (TryGetBuilder(out builder))
{
if (builder.Capacity == 0 && _capacity is int capacity)
{
builder.Capacity = capacity;
}
}
return builder is not null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ClearInlineElement(int index)
{
@ -108,7 +149,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
readonly get
{
if (_builder is { } builder)
if (TryGetBuilder(out var builder))
{
return builder[index];
}
@ -124,7 +165,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (_builder is { } builder)
if (TryGetBuilder(out var builder))
{
builder[index] = value;
return;
@ -194,7 +235,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
public void Add(T item)
{
if (_builder is { } builder)
if (TryGetBuilderAndEnsureCapacity(out var builder))
{
builder.Add(item);
}
@ -229,7 +270,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
return;
}
if (_builder is { } builder)
if (TryGetBuilderAndEnsureCapacity(out var builder))
{
builder.AddRange(items);
}
@ -250,7 +291,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
public void AddRange(IEnumerable<T> items)
{
if (_builder is { } builder)
if (TryGetBuilderAndEnsureCapacity(out var builder))
{
builder.AddRange(items);
return;
@ -292,7 +333,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
public void Clear()
{
if (_builder is { } builder)
if (TryGetBuilder(out var builder))
{
// Keep using a real builder to avoid churn in the object pool.
builder.Clear();
@ -310,7 +351,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
public void RemoveAt(int index)
{
if (_builder is { } builder)
if (TryGetBuilderAndEnsureCapacity(out var builder))
{
builder.RemoveAt(index);
return;
@ -380,7 +421,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
/// <returns>An immutable array.</returns>
public ImmutableArray<T> DrainToImmutable()
{
if (_builder is { } builder)
if (TryGetBuilder(out var builder))
{
return builder.DrainToImmutable();
}
@ -396,7 +437,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
public readonly ImmutableArray<T> ToImmutable()
{
if (_builder is { } builder)
if (TryGetBuilder(out var builder))
{
return builder.ToImmutable();
}
@ -420,7 +461,7 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
public readonly T[] ToArray()
{
if (_builder is { } builder)
if (TryGetBuilder(out var builder))
{
return builder.ToArray();
}
@ -1314,7 +1355,8 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
{
Debug.Assert(_builder is null);
var builder = ArrayBuilderPool<T>.Default.Get();
_builderPool ??= ArrayBuilderPool<T>.Default;
var builder = _builderPool.Get();
if (_capacity is int capacity)
{
@ -1333,4 +1375,205 @@ internal partial struct PooledArrayBuilder<T> : IDisposable
// Since _inlineCount tracks the number of inline items used, we zero it out here.
_inlineCount = 0;
}
public readonly ImmutableArray<T> ToImmutableOrdered()
{
var result = ToImmutable();
result.Unsafe().Order();
return result;
}
public readonly ImmutableArray<T> ToImmutableOrdered(IComparer<T> comparer)
{
var result = ToImmutable();
result.Unsafe().Order(comparer);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrdered(Comparison<T> comparison)
{
var result = ToImmutable();
result.Unsafe().Order(comparison);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedDescending()
{
var result = ToImmutable();
result.Unsafe().OrderDescending();
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedDescending(IComparer<T> comparer)
{
var result = ToImmutable();
result.Unsafe().OrderDescending(comparer);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedDescending(Comparison<T> comparison)
{
var result = ToImmutable();
result.Unsafe().OrderDescending(comparison);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedBy<TKey>(Func<T, TKey> keySelector)
{
var result = ToImmutable();
result.Unsafe().OrderBy(keySelector);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedBy<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
{
var result = ToImmutable();
result.Unsafe().OrderBy(keySelector, comparer);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedBy<TKey>(Func<T, TKey> keySelector, Comparison<TKey> comparison)
{
var result = ToImmutable();
result.Unsafe().OrderBy(keySelector, comparison);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedByDescending<TKey>(Func<T, TKey> keySelector)
{
var result = ToImmutable();
result.Unsafe().OrderByDescending(keySelector);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedByDescending<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
{
var result = ToImmutable();
result.Unsafe().OrderByDescending(keySelector, comparer);
return result;
}
public readonly ImmutableArray<T> ToImmutableOrderedByDescending<TKey>(Func<T, TKey> keySelector, Comparison<TKey> comparison)
{
var result = ToImmutable();
result.Unsafe().OrderByDescending(keySelector, comparison);
return result;
}
public ImmutableArray<T> DrainToImmutableOrdered()
{
var result = DrainToImmutable();
result.Unsafe().Order();
return result;
}
public ImmutableArray<T> DrainToImmutableOrdered(IComparer<T> comparer)
{
var result = DrainToImmutable();
result.Unsafe().Order(comparer);
return result;
}
public ImmutableArray<T> DrainToImmutableOrdered(Comparison<T> comparison)
{
var result = DrainToImmutable();
result.Unsafe().Order(comparison);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedDescending()
{
var result = DrainToImmutable();
result.Unsafe().OrderDescending();
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedDescending(IComparer<T> comparer)
{
var result = DrainToImmutable();
result.Unsafe().OrderDescending(comparer);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedDescending(Comparison<T> comparison)
{
var result = DrainToImmutable();
result.Unsafe().OrderDescending(comparison);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedBy<TKey>(Func<T, TKey> keySelector)
{
var result = DrainToImmutable();
result.Unsafe().OrderBy(keySelector);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedBy<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
{
var result = DrainToImmutable();
result.Unsafe().OrderBy(keySelector, comparer);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedBy<TKey>(Func<T, TKey> keySelector, Comparison<TKey> comparison)
{
var result = DrainToImmutable();
result.Unsafe().OrderBy(keySelector, comparison);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedByDescending<TKey>(Func<T, TKey> keySelector)
{
var result = DrainToImmutable();
result.Unsafe().OrderByDescending(keySelector);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedByDescending<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
{
var result = DrainToImmutable();
result.Unsafe().OrderByDescending(keySelector, comparer);
return result;
}
public ImmutableArray<T> DrainToImmutableOrderedByDescending<TKey>(Func<T, TKey> keySelector, Comparison<TKey> comparison)
{
var result = DrainToImmutable();
result.Unsafe().OrderByDescending(keySelector, comparison);
return result;
}
internal readonly TestAccessor GetTestAccessor() => new(in this);
internal readonly struct TestAccessor(ref readonly PooledArrayBuilder<T> builder)
{
public ImmutableArray<T>.Builder? InnerArrayBuilder { get; } = builder._builder;
public int? Capacity { get; } = builder._capacity;
public int InlineItemCount { get; } = builder._inlineCount;
}
}