-
-
Notifications
You must be signed in to change notification settings - Fork 554
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1639 from dennisdoomen/MergeFromDevelop
Merge missed improvements from develop
- Loading branch information
Showing
25 changed files
with
903 additions
and
62 deletions.
There are no files selected for viewing
26 changes: 26 additions & 0 deletions
26
Src/FluentAssertions/CallerIdentification/AddNonEmptySymbolParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal class AddNonEmptySymbolParsingStrategy : IParsingStrategy | ||
{ | ||
public ParsingState Parse(char symbol, StringBuilder statement) | ||
{ | ||
if (!char.IsWhiteSpace(symbol)) | ||
{ | ||
statement.Append(symbol); | ||
} | ||
|
||
return ParsingState.GoToNextSymbol; | ||
} | ||
|
||
public bool IsWaitingForContextEnd() | ||
{ | ||
return false; | ||
} | ||
|
||
public void NotifyEndOfLineReached() | ||
{ | ||
} | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
Src/FluentAssertions/CallerIdentification/AwaitParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal class AwaitParsingStrategy : IParsingStrategy | ||
{ | ||
private const string KeywordToSkip = "await"; | ||
|
||
public ParsingState Parse(char symbol, StringBuilder statement) | ||
{ | ||
if (IsLongEnoughToContainOurKeyword(statement) && EndsWithOurKeyword(statement)) | ||
{ | ||
statement.Remove(statement.Length - KeywordToSkip.Length, KeywordToSkip.Length); | ||
} | ||
|
||
return ParsingState.InProgress; | ||
} | ||
|
||
private static bool EndsWithOurKeyword(StringBuilder statement) | ||
{ | ||
var leftIndex = statement.Length - 1; | ||
var rightIndex = KeywordToSkip.Length - 1; | ||
|
||
for (var offset = 0; offset < KeywordToSkip.Length; offset++) | ||
{ | ||
if (statement[leftIndex - offset] != KeywordToSkip[rightIndex - offset]) | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private static bool IsLongEnoughToContainOurKeyword(StringBuilder statement) => statement.Length >= KeywordToSkip.Length; | ||
|
||
public bool IsWaitingForContextEnd() | ||
{ | ||
return false; | ||
} | ||
|
||
public void NotifyEndOfLineReached() | ||
{ | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
Src/FluentAssertions/CallerIdentification/CallerStatementBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal class CallerStatementBuilder | ||
{ | ||
private readonly StringBuilder statement; | ||
private readonly List<IParsingStrategy> priorityOrderedParsingStrategies; | ||
private ParsingState parsingState = ParsingState.InProgress; | ||
|
||
internal CallerStatementBuilder() | ||
{ | ||
statement = new StringBuilder(); | ||
priorityOrderedParsingStrategies = new List<IParsingStrategy> | ||
{ | ||
new QuotesParsingStrategy(), | ||
new MultiLineCommentParsingStrategy(), | ||
new SingleLineCommentParsingStrategy(), | ||
new SemicolonParsingStrategy(), | ||
new ShouldCallParsingStrategy(), | ||
new AwaitParsingStrategy(), | ||
new AddNonEmptySymbolParsingStrategy() | ||
}; | ||
} | ||
|
||
internal void Append(string symbols) | ||
{ | ||
using var symbolEnumerator = symbols.GetEnumerator(); | ||
while (symbolEnumerator.MoveNext() && parsingState != ParsingState.Done) | ||
{ | ||
var hasParsingStrategyWaitingForEndContext = priorityOrderedParsingStrategies | ||
.Any(s => s.IsWaitingForContextEnd()); | ||
|
||
parsingState = ParsingState.InProgress; | ||
foreach (var parsingStrategy in | ||
priorityOrderedParsingStrategies | ||
.SkipWhile(parsingStrategy => | ||
hasParsingStrategyWaitingForEndContext | ||
&& !parsingStrategy.IsWaitingForContextEnd())) | ||
{ | ||
parsingState = parsingStrategy.Parse(symbolEnumerator.Current, statement); | ||
if (parsingState != ParsingState.InProgress) | ||
{ | ||
break; | ||
} | ||
} | ||
} | ||
|
||
if (IsDone()) | ||
{ | ||
return; | ||
} | ||
|
||
priorityOrderedParsingStrategies | ||
.ForEach(strategy => strategy.NotifyEndOfLineReached()); | ||
} | ||
|
||
internal bool IsDone() => parsingState == ParsingState.Done; | ||
|
||
public override string ToString() => statement.ToString(); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
Src/FluentAssertions/CallerIdentification/IParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
/// <summary> | ||
/// Represents a stateful parsing strategy that is used to help identify the "caller" to use in an assertion message. | ||
/// | ||
/// The strategies will be instantiated at the beginning of a "caller identification" task, and will live until | ||
/// the statement can be identified (and thus some are stateful). | ||
/// </summary> | ||
internal interface IParsingStrategy | ||
{ | ||
/// <summary> | ||
/// Given a symbol, the parsing strategy should add/remove from the statement if needed, and then return | ||
/// - InProgress if the symbol isn't relevant to the strategies (so other strategies can be tried) | ||
/// - Handled if an action has been taken (and no other strategies should be used for this symbol) | ||
/// - Done if the statement is complete, and thus further symbols should be read. | ||
/// </summary> | ||
ParsingState Parse(char symbol, StringBuilder statement); | ||
|
||
/// <summary> | ||
/// Returns true if strategy is in the middle of a context (ex: strategy read "/*" and is waiting for "*/" | ||
/// </summary> | ||
bool IsWaitingForContextEnd(); | ||
|
||
/// <summary> | ||
/// Used to notify the strategy that we have reached the end of the line (very useful to detect the end of | ||
/// a single line comment). | ||
/// </summary> | ||
void NotifyEndOfLineReached(); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
Src/FluentAssertions/CallerIdentification/MultiLineCommentParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal class MultiLineCommentParsingStrategy : IParsingStrategy | ||
{ | ||
private bool isCommentContext; | ||
private char? commentContextPreviousChar; | ||
|
||
public ParsingState Parse(char symbol, StringBuilder statement) | ||
{ | ||
if (isCommentContext) | ||
{ | ||
var isEndOfMultilineComment = symbol == '/' && commentContextPreviousChar == '*'; | ||
if (isEndOfMultilineComment) | ||
{ | ||
isCommentContext = false; | ||
commentContextPreviousChar = null; | ||
} | ||
else | ||
{ | ||
commentContextPreviousChar = symbol; | ||
} | ||
|
||
return ParsingState.GoToNextSymbol; | ||
} | ||
|
||
var isStartOfMultilineComment = | ||
symbol == '*' | ||
&& statement.Length > 0 | ||
&& statement[statement.Length - 1] == '/'; | ||
if (isStartOfMultilineComment) | ||
{ | ||
statement.Remove(statement.Length - 1, 1); | ||
isCommentContext = true; | ||
return ParsingState.GoToNextSymbol; | ||
} | ||
|
||
return ParsingState.InProgress; | ||
} | ||
|
||
public bool IsWaitingForContextEnd() | ||
{ | ||
return isCommentContext; | ||
} | ||
|
||
public void NotifyEndOfLineReached() | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal enum ParsingState | ||
{ | ||
InProgress, | ||
GoToNextSymbol, | ||
Done | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
Src/FluentAssertions/CallerIdentification/QuotesParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal class QuotesParsingStrategy : IParsingStrategy | ||
{ | ||
private char isQuoteEscapeSymbol = '\\'; | ||
private bool isQuoteContext; | ||
private char? previousChar; | ||
|
||
public ParsingState Parse(char symbol, StringBuilder statement) | ||
{ | ||
if (symbol == '"') | ||
{ | ||
if (isQuoteContext) | ||
{ | ||
if (previousChar != isQuoteEscapeSymbol) | ||
{ | ||
isQuoteContext = false; | ||
isQuoteEscapeSymbol = '\\'; | ||
previousChar = null; | ||
statement.Append(symbol); | ||
return ParsingState.GoToNextSymbol; | ||
} | ||
} | ||
else | ||
{ | ||
isQuoteContext = true; | ||
if (IsVerbatim(statement)) | ||
{ | ||
isQuoteEscapeSymbol = '"'; | ||
} | ||
} | ||
} | ||
|
||
if (isQuoteContext) | ||
{ | ||
statement.Append(symbol); | ||
} | ||
|
||
previousChar = symbol; | ||
return isQuoteContext ? ParsingState.GoToNextSymbol : ParsingState.InProgress; | ||
} | ||
|
||
public bool IsWaitingForContextEnd() | ||
{ | ||
return isQuoteContext; | ||
} | ||
|
||
public void NotifyEndOfLineReached() | ||
{ | ||
} | ||
|
||
private bool IsVerbatim(StringBuilder statement) | ||
{ | ||
return | ||
statement.Length >= 1 | ||
&& | ||
new[] | ||
{ | ||
"$@", | ||
"@$", | ||
} | ||
.Any(verbatimStringOpener => | ||
previousChar == verbatimStringOpener[1] | ||
&& statement[statement.Length - 1] == verbatimStringOpener[1] | ||
&& statement[statement.Length - 2] == verbatimStringOpener[0]); | ||
} | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
Src/FluentAssertions/CallerIdentification/SemicolonParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal class SemicolonParsingStrategy : IParsingStrategy | ||
{ | ||
public ParsingState Parse(char symbol, StringBuilder statement) | ||
{ | ||
if (symbol == ';') | ||
{ | ||
statement.Clear(); | ||
return ParsingState.Done; | ||
} | ||
|
||
return ParsingState.InProgress; | ||
} | ||
|
||
public bool IsWaitingForContextEnd() | ||
{ | ||
return false; | ||
} | ||
|
||
public void NotifyEndOfLineReached() | ||
{ | ||
} | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
Src/FluentAssertions/CallerIdentification/ShouldCallParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System.Text; | ||
|
||
namespace FluentAssertions.CallerIdentification | ||
{ | ||
internal class ShouldCallParsingStrategy : IParsingStrategy | ||
{ | ||
private const string ShouldCall = ".Should("; | ||
|
||
public ParsingState Parse(char symbol, StringBuilder statement) | ||
{ | ||
if (statement.Length >= ShouldCall.Length) | ||
{ | ||
var leftIndex = statement.Length - 1; | ||
var rightIndex = ShouldCall.Length - 1; | ||
|
||
for (var i = 0; i < ShouldCall.Length; i++) | ||
{ | ||
if (statement[leftIndex - i] != ShouldCall[rightIndex - i]) | ||
{ | ||
return ParsingState.InProgress; | ||
} | ||
} | ||
|
||
statement.Remove(statement.Length - ShouldCall.Length, ShouldCall.Length); | ||
return ParsingState.Done; | ||
} | ||
|
||
return ParsingState.InProgress; | ||
} | ||
|
||
public bool IsWaitingForContextEnd() | ||
{ | ||
return false; | ||
} | ||
|
||
public void NotifyEndOfLineReached() | ||
{ | ||
} | ||
} | ||
} |
Oops, something went wrong.