/
EventRaisingExtensions.cs
136 lines (114 loc) · 5.47 KB
/
EventRaisingExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using FluentAssertions.Common;
using FluentAssertions.Events;
using FluentAssertions.Execution;
namespace FluentAssertions
{
/// <summary>
/// Provides extension methods for monitoring and querying events.
/// </summary>
public static class EventRaisingExtensions
{
/// <summary>
/// Asserts that all occurrences of the event originated from the <param name="expectedSender"/> and
/// returns only the events that came from that sender.
/// </summary>
public static IEventRecording WithSender(this IEventRecording eventRecording, object expectedSender)
{
var eventsForSender = new List<OccurredEvent>();
var otherSenders = new List<object>();
foreach (OccurredEvent @event in eventRecording)
{
bool hasSender = Execute.Assertion
.ForCondition(@event.Parameters.Any())
.FailWith("Expected event from sender {0}, " +
$"but event {eventRecording.EventName} does not have any parameters", expectedSender);
if (hasSender)
{
object sender = @event.Parameters.First();
if (ReferenceEquals(sender, expectedSender))
{
eventsForSender.Add(@event);
}
else
{
otherSenders.Add(sender);
}
}
}
Execute.Assertion
.ForCondition(eventsForSender.Any())
.FailWith("Expected sender {0}, but found {1}.",
() => expectedSender,
() => otherSenders.Distinct());
return new FilteredEventRecording(eventRecording, eventsForSender);
}
/// <summary>
/// Asserts that at least one occurrence of the events had at least one of the arguments matching a predicate. Returns
/// only the events that matched that predicate.
/// </summary>
public static IEventRecording WithArgs<T>(this IEventRecording eventRecording, Expression<Func<T, bool>> predicate)
{
Guard.ThrowIfArgumentIsNull(predicate, nameof(predicate));
Func<T, bool> compiledPredicate = predicate.Compile();
var eventsMatchingPredicate = new List<OccurredEvent>();
foreach (OccurredEvent @event in eventRecording)
{
var typedParameters = @event.Parameters.OfType<T>().ToArray();
if (typedParameters.Any(parameter => compiledPredicate(parameter)))
{
eventsMatchingPredicate.Add(@event);
}
}
bool hasFoundArgumentsBefore = eventsMatchingPredicate.Any();
Execute.Assertion
.ForCondition(hasFoundArgumentsBefore)
.FailWith($"Expected at least one event with arguments matching either the type <{{0}}> or parameters {{1}}, but found none.",
typeof(T),
predicate.Body);
return new FilteredEventRecording(eventRecording, eventsMatchingPredicate);
}
/// <summary>
/// Asserts that at least one of the occurred events has arguments the match the predicates in the same order. Returns
/// only the events that matched those predicates.
/// </summary>
/// <remarks>
/// If a <c>null</c> is provided as predicate argument, the corresponding event parameter value is ignored.
/// </remarks>
public static IEventRecording WithArgs<T>(this IEventRecording eventRecording, params Expression<Func<T, bool>>[] predicates)
{
Func<T, bool>[] compiledPredicates = predicates.Select(p => p?.Compile()).ToArray();
bool hasArgumentOfRightType = false;
var eventsMatchingPredicate = new List<OccurredEvent>();
foreach (OccurredEvent @event in eventRecording)
{
var typedParameters = @event.Parameters.OfType<T>().ToArray();
hasArgumentOfRightType = typedParameters.Any();
if (predicates.Length > typedParameters.Length)
{
throw new ArgumentException(
$"Expected the event to have at least {predicates.Length} parameters of type {typeof(T)}, but only found {typedParameters.Length}.");
}
bool isMatch = true && hasArgumentOfRightType;
for (int index = 0; index < predicates.Length && isMatch; index++)
{
isMatch = compiledPredicates[index]?.Invoke(typedParameters[index]) ?? true;
}
if (isMatch)
{
eventsMatchingPredicate.Add(@event);
}
}
bool hasFoundArgumentsBefore = eventsMatchingPredicate.Any();
Execute.Assertion
.ForCondition(hasFoundArgumentsBefore)
.FailWith($"Expected at least one event with arguments matching either the type <{{0}}> or parameters {{1}}, but found none.",
typeof(T),
string.Join(" | ", predicates.Where(p => p is not null).Select(p => p.Body.ToString())));
return new FilteredEventRecording(eventRecording, eventsMatchingPredicate);
}
}
}