private static MarbleEventAssertionResultTable FillAssertionResultTable <TEvent>( Moment moment, Action <string, TEvent> assertion, IList <TEvent> received) { var table = new MarbleEventAssertionResultTable(); foreach (var m in moment.Marbles) { var row = new List <MarbleEventAssertionResultTable.Result>(); foreach (var @event in received) { var(succeeded, exception, _, _) = CheckEvent(assertion, m, @event); row.Add(new MarbleEventAssertionResultTable.Result(m, @event, succeeded ? string.Empty : exception.Message)); } table.AddResultsRow(row); } return(table); }
private static ExpectedMarble CreateUnorderedGroupExpectedMarble <TEvent>( Moment moment, Func <FSharpOption <TEvent> > eventProducer, Action <string, TEvent> assertion) => new ExpectedMarble( moment.Time, moment.ToString(), () => { var produced = moment.Marbles.Select(_ => eventProducer()).ToList(); var received = produced.Where(FSharpOption <TEvent> .get_IsSome).Select(t => t.Value).ToList(); if (received.Count != moment.Marbles.Length) { throw new Exception( $"At time {moment.Time}, expecting an unordered group {moment} with {moment.Marbles.Length} elements but got {received.Count} events: [{string.Join(" ", received)}]"); } var table = FillAssertionResultTable(moment, assertion, received); var success = table.AllRowsAtLeastOneSuccess() && table.AllColumnsAtLeastOneSuccess(); if (!success) { throw new Exception(table.ToString()); } });
private static IEnumerable <ExpectedMarble> CreateLooseExpectations <TEvent>( Moment moment, Func <FSharpOption <TEvent> > eventProducer, Action <string, TEvent> assertion) { var momentMarbles = new List <ExpectedMarble>(); switch (moment.Type) { case Moment.MomentType.Empty: break; case Moment.MomentType.Single: var marble = moment.Marbles[0]; momentMarbles.Add(CreateSingleLooselyExpectedMarble(moment, eventProducer, assertion, marble)); break; case Moment.MomentType.OrderedGroup: momentMarbles.Add(CreateOrderedGroupLooselyExpectedMarble(moment, eventProducer, assertion)); break; case Moment.MomentType.UnorderedGroup: momentMarbles.Add(CreateSingleLooselyExpectedMarble(moment, eventProducer, assertion, moment.ToString())); break; default: throw new ArgumentOutOfRangeException(); } momentMarbles.Add( new ExpectedMarble( moment.Time, null, () => ExhaustProducer(eventProducer))); return(momentMarbles); }
private static IEnumerable <InputMarble> CreateInputMarbles(Moment moment, Func <string, Task> whatToDo) => moment.Marbles.Select(marble => new InputMarble(moment.Time, marble, () => whatToDo(marble)));
private static ExpectedMarble CreateOrderedGroupExpectedMarble <TEvent>( Moment moment, Func <FSharpOption <TEvent> > eventProducer, Action <string, TEvent> assertion) =>
private static IEnumerable <ExpectedMarble> CreateAssertionExpectations( Moment moment, Action <string> assertion) => moment.Marbles.Select(marble => new ExpectedMarble(moment.Time, marble, () => assertion(marble)));
private static IEnumerable <Moment> ParseSequence(string sequence, out int?timeOffset) { var retVal = new List <Moment>(); var time = 0; var groupTime = 0; timeOffset = null; List <string> groupMarbles = null; List <string> unorderedGroupMarbles = null; var parsingState = ParsingState.NotInGroup; var marbleTime = 0; var marbleBuilder = new StringBuilder(); foreach (var character in sequence.Trim()) { if (parsingState == ParsingState.NotInGroup) { switch (character) { case ' ': case '-': FlushMarble(marbleBuilder, retVal, marbleTime, time); retVal.Add(Moment.Empty(time)); break; case '^': FlushMarble(marbleBuilder, retVal, marbleTime, time); if (timeOffset.HasValue) { throw new ArgumentException("Only one ^ character allowed", nameof(sequence)); } timeOffset = -time; retVal.Add(Moment.Single(time, character.ToString())); break; case '(': FlushMarble(marbleBuilder, retVal, marbleTime, time); groupTime = time; parsingState = ParsingState.InOrderedGroup; groupMarbles = new List <string>(); break; case '<': FlushMarble(marbleBuilder, retVal, marbleTime, time); groupTime = time; parsingState = ParsingState.InUnorderedGroup; unorderedGroupMarbles = new List <string>(); break; case ')': throw new ArgumentException("Closing parentheses without opening parentheses", nameof(sequence)); case '>': throw new ArgumentException("Closing brace without opening brace", nameof(sequence)); case ',': throw new ArgumentException("Comma should not occur outside of a group", nameof(sequence)); default: GrowMarble(marbleBuilder, ref marbleTime, time, character); break; } } else if (parsingState == ParsingState.InOrderedGroup) { switch (character) { case ',': case ' ': FlushMarbleToGroup(marbleBuilder, groupMarbles); break; case '-': throw new ArgumentException("Cannot use - within a group", nameof(sequence)); case '^': if (timeOffset.HasValue) { throw new ArgumentException("Only one ^ character allowed", nameof(sequence)); } FlushMarbleToGroup(marbleBuilder, groupMarbles); timeOffset = -groupTime; groupMarbles.Add(character.ToString()); break; case '(': throw new ArgumentException("Cannot have nested parentheses", nameof(sequence)); case ')': FlushMarbleToGroup(marbleBuilder, groupMarbles); if (groupMarbles.Count <= 1) { throw new ArgumentException("Only groups with multiple marbles are allowed", nameof(sequence)); } retVal.Add(Moment.OrderedGroup(groupTime, groupMarbles.ToArray())); groupMarbles = null; parsingState = ParsingState.NotInGroup; AddEmptyMoments(retVal, time + 1); break; default: GrowMarble(marbleBuilder, ref marbleTime, groupTime, character); break; } } else if (parsingState == ParsingState.InUnorderedGroup) { switch (character) { case ',': case ' ': FlushMarbleToGroup(marbleBuilder, unorderedGroupMarbles); break; case '-': throw new ArgumentException("Cannot use - within a group", nameof(sequence)); case '^': if (timeOffset.HasValue) { throw new ArgumentException("Only one ^ character allowed", nameof(sequence)); } FlushMarbleToGroup(marbleBuilder, unorderedGroupMarbles); timeOffset = -groupTime; unorderedGroupMarbles.Add(character.ToString()); break; case '>': FlushMarbleToGroup(marbleBuilder, unorderedGroupMarbles); if (unorderedGroupMarbles.Count <= 1) { throw new ArgumentException("Only groups with multiple marbles are allowed", nameof(sequence)); } retVal.Add(Moment.UnorderedGroup(groupTime, unorderedGroupMarbles.ToArray())); unorderedGroupMarbles = null; parsingState = ParsingState.NotInGroup; AddEmptyMoments(retVal, time + 1); break; case '<': throw new ArgumentException("Cannot have braces nested in an unordered group", nameof(sequence)); case '(': throw new ArgumentException("Cannot have parentheses nested in an unordered group", nameof(sequence)); case ')': throw new ArgumentException("Cannot have parentheses nested in an unordered group", nameof(sequence)); default: GrowMarble(marbleBuilder, ref marbleTime, groupTime, character); break; } } time++; } FlushMarble(marbleBuilder, retVal, marbleTime, time); if (parsingState == ParsingState.InOrderedGroup) { throw new ArgumentException("Opening parentheses without closing parentheses", nameof(sequence)); } if (parsingState == ParsingState.InUnorderedGroup) { throw new ArgumentException("Opening brace without closing brace", nameof(sequence)); } return(retVal); }