Skip to content

Commit 3c4777c

Browse files
authored
feat: add virtual and override filters and assertions for events (#341)
Add `WhichAreVirtual` / `WhichOverride` filters and `IsVirtual` / `Overrides` assertions (plus collection and negated variants) for `EventInfo`, matching what already exists for methods and properties.
1 parent 4079331 commit 3c4777c

24 files changed

Lines changed: 1645 additions & 2 deletions

Docs/pages/02-filters.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,14 +468,17 @@ In addition to [access modifiers](#access-modifiers),
468468
| handler of exact type | `.OfExactType<T>()` | `.IsOfExactType<T>()` | `.AreOfExactType<T>()` |
469469
| abstract / sealed | `.WhichAreAbstract()` / `.WhichAreSealed()` | `.IsAbstract()` / `.IsSealed()` | `.AreAbstract()` / `.AreSealed()` |
470470
| static | `.WhichAreStatic()` | `.IsStatic()` | `.AreStatic()` |
471+
| virtual | `.WhichAreVirtual()` | `.IsVirtual()` | `.AreVirtual()` |
472+
| override | `.WhichOverride()` | `.Overrides()` | `.Override()` |
471473

472474
The `OfType` / `IsOfType` / `AreOfType` filters and assertions match the event's handler type (its
473475
`EventHandlerType`, e.g. `EventHandler<T>`); the `…ExactType` variants match only the exact handler type.
474476
Use `OrOfType<T>()` / `OrOfExactType<T>()` to allow several handler types.
475477

476478
:::note[Negation]
477-
The `abstract`, `sealed` and `static` rows have a negated form: `WhichAreNot…` on filters and
478-
`IsNot…` / `AreNot…` on assertions (e.g. `WhichAreNotSealed()`, `IsNotSealed()`, `AreNotSealed()`).
479+
The `abstract`, `sealed`, `static` and `virtual` rows have a negated form: `WhichAreNot…` on filters and
480+
`IsNot…` / `AreNot…` on assertions (e.g. `WhichAreNotSealed()`, `IsNotSealed()`, `AreNotSealed()`);
481+
`override` uses `WhichDoNotOverride()` / `DoesNotOverride()` / `DoNotOverride()`.
479482
:::
480483

481484
```csharp
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Reflection;
2+
using aweXpect.Reflection.Collections;
3+
using aweXpect.Reflection.Helpers;
4+
5+
namespace aweXpect.Reflection;
6+
7+
public static partial class EventFilters
8+
{
9+
/// <summary>
10+
/// Filters for events that are virtual.
11+
/// </summary>
12+
public static Filtered.Events WhichAreVirtual(this Filtered.Events @this)
13+
=> @this.Which(Filter.Prefix<EventInfo>(
14+
eventInfo => eventInfo.IsReallyVirtual(),
15+
"virtual "));
16+
17+
/// <summary>
18+
/// Filters for events that are not virtual.
19+
/// </summary>
20+
public static Filtered.Events WhichAreNotVirtual(this Filtered.Events @this)
21+
=> @this.Which(Filter.Prefix<EventInfo>(
22+
eventInfo => !eventInfo.IsReallyVirtual(),
23+
"non-virtual "));
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Reflection;
2+
using aweXpect.Reflection.Collections;
3+
using aweXpect.Reflection.Helpers;
4+
5+
namespace aweXpect.Reflection;
6+
7+
public static partial class EventFilters
8+
{
9+
/// <summary>
10+
/// Filters for events that override a base class event.
11+
/// </summary>
12+
public static Filtered.Events WhichOverride(this Filtered.Events @this)
13+
=> @this.Which(Filter.Suffix<EventInfo>(
14+
eventInfo => eventInfo.IsOverride(),
15+
"which override a base event "));
16+
17+
/// <summary>
18+
/// Filters for events that do not override a base class event.
19+
/// </summary>
20+
public static Filtered.Events WhichDoNotOverride(this Filtered.Events @this)
21+
=> @this.Which(Filter.Suffix<EventInfo>(
22+
eventInfo => !eventInfo.IsOverride(),
23+
"which do not override a base event "));
24+
}

Source/aweXpect.Reflection/Helpers/EventInfoHelpers.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,22 @@ public static bool IsReallyAbstract(this EventInfo? eventInfo)
102102
public static bool IsReallySealed(this EventInfo? eventInfo)
103103
=> eventInfo?.AddMethod?.IsFinal == true;
104104

105+
/// <summary>
106+
/// Checks if the <paramref name="eventInfo" /> is virtual (based on its accessor methods).
107+
/// </summary>
108+
/// <param name="eventInfo">The <see cref="EventInfo" /> to check.</param>
109+
/// <returns><see langword="true" /> if the event is virtual; otherwise, <see langword="false" />.</returns>
110+
public static bool IsReallyVirtual(this EventInfo? eventInfo)
111+
=> eventInfo?.AddMethod?.IsVirtual == true;
112+
113+
/// <summary>
114+
/// Checks if the <paramref name="eventInfo" /> overrides a base class event (based on its accessor methods).
115+
/// </summary>
116+
/// <param name="eventInfo">The <see cref="EventInfo" /> to check.</param>
117+
/// <returns><see langword="true" /> if the event overrides a base class event; otherwise, <see langword="false" />.</returns>
118+
public static bool IsOverride(this EventInfo? eventInfo)
119+
=> eventInfo?.AddMethod.IsOverride() == true;
120+
105121
/// <summary>
106122
/// Checks if the <paramref name="eventInfo" /> is static (based on its accessor methods).
107123
/// </summary>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System.Reflection;
2+
using System.Text;
3+
using aweXpect.Core;
4+
using aweXpect.Core.Constraints;
5+
using aweXpect.Reflection.Helpers;
6+
using aweXpect.Results;
7+
8+
namespace aweXpect.Reflection;
9+
10+
public static partial class ThatEvent
11+
{
12+
/// <summary>
13+
/// Verifies that the <see cref="EventInfo" /> is virtual.
14+
/// </summary>
15+
public static AndOrResult<EventInfo?, IThat<EventInfo?>> IsVirtual(
16+
this IThat<EventInfo?> subject)
17+
=> new(subject.Get().ExpectationBuilder.AddConstraint((it, grammars)
18+
=> new IsVirtualConstraint(it, grammars)),
19+
subject);
20+
21+
/// <summary>
22+
/// Verifies that the <see cref="EventInfo" /> is not virtual.
23+
/// </summary>
24+
public static AndOrResult<EventInfo?, IThat<EventInfo?>> IsNotVirtual(
25+
this IThat<EventInfo?> subject)
26+
=> new(subject.Get().ExpectationBuilder.AddConstraint((it, grammars)
27+
=> new IsVirtualConstraint(it, grammars).Invert()),
28+
subject);
29+
30+
private sealed class IsVirtualConstraint(string it, ExpectationGrammars grammars)
31+
: ConstraintResult.WithNotNullValue<EventInfo?>(it, grammars),
32+
IValueConstraint<EventInfo?>
33+
{
34+
public ConstraintResult IsMetBy(EventInfo? actual)
35+
{
36+
Actual = actual;
37+
Outcome = actual.IsReallyVirtual() ? Outcome.Success : Outcome.Failure;
38+
return this;
39+
}
40+
41+
protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
42+
=> stringBuilder.Append("is virtual");
43+
44+
protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
45+
{
46+
stringBuilder.Append(It).Append(" was non-virtual ");
47+
Formatter.Format(stringBuilder, Actual);
48+
}
49+
50+
protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
51+
=> stringBuilder.Append("is not virtual");
52+
53+
protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
54+
{
55+
stringBuilder.Append(It).Append(" was virtual ");
56+
Formatter.Format(stringBuilder, Actual);
57+
}
58+
}
59+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System.Reflection;
2+
using System.Text;
3+
using aweXpect.Core;
4+
using aweXpect.Core.Constraints;
5+
using aweXpect.Reflection.Helpers;
6+
using aweXpect.Results;
7+
8+
namespace aweXpect.Reflection;
9+
10+
public static partial class ThatEvent
11+
{
12+
/// <summary>
13+
/// Verifies that the <see cref="EventInfo" /> overrides a base class event.
14+
/// </summary>
15+
public static AndOrResult<EventInfo?, IThat<EventInfo?>> Overrides(
16+
this IThat<EventInfo?> subject)
17+
=> new(subject.Get().ExpectationBuilder.AddConstraint((it, grammars)
18+
=> new OverridesConstraint(it, grammars)),
19+
subject);
20+
21+
/// <summary>
22+
/// Verifies that the <see cref="EventInfo" /> does not override a base class event.
23+
/// </summary>
24+
public static AndOrResult<EventInfo?, IThat<EventInfo?>> DoesNotOverride(
25+
this IThat<EventInfo?> subject)
26+
=> new(subject.Get().ExpectationBuilder.AddConstraint((it, grammars)
27+
=> new OverridesConstraint(it, grammars).Invert()),
28+
subject);
29+
30+
private sealed class OverridesConstraint(string it, ExpectationGrammars grammars)
31+
: ConstraintResult.WithNotNullValue<EventInfo?>(it, grammars),
32+
IValueConstraint<EventInfo?>
33+
{
34+
public ConstraintResult IsMetBy(EventInfo? actual)
35+
{
36+
Actual = actual;
37+
Outcome = actual.IsOverride() ? Outcome.Success : Outcome.Failure;
38+
return this;
39+
}
40+
41+
protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
42+
=> stringBuilder.Append("overrides a base event");
43+
44+
protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
45+
{
46+
stringBuilder.Append(It).Append(" did not override a base event ");
47+
Formatter.Format(stringBuilder, Actual);
48+
}
49+
50+
protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
51+
=> stringBuilder.Append("does not override a base event");
52+
53+
protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
54+
{
55+
stringBuilder.Append(It).Append(" did override a base event ");
56+
Formatter.Format(stringBuilder, Actual);
57+
}
58+
}
59+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
using System.Collections.Generic;
2+
using System.Reflection;
3+
using System.Text;
4+
using aweXpect.Core;
5+
using aweXpect.Core.Constraints;
6+
using aweXpect.Reflection.Helpers;
7+
using aweXpect.Results;
8+
#if NET8_0_OR_GREATER
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
#endif
12+
13+
// ReSharper disable PossibleMultipleEnumeration
14+
15+
namespace aweXpect.Reflection;
16+
17+
public static partial class ThatEvents
18+
{
19+
/// <summary>
20+
/// Verifies that all items in the filtered collection of <see cref="EventInfo" /> are virtual.
21+
/// </summary>
22+
public static AndOrResult<IEnumerable<EventInfo?>, IThat<IEnumerable<EventInfo?>>> AreVirtual(
23+
this IThat<IEnumerable<EventInfo?>> subject)
24+
=> new(subject.Get().ExpectationBuilder.AddConstraint<IEnumerable<EventInfo?>>((it, grammars)
25+
=> new AreVirtualConstraint(it, grammars)),
26+
subject);
27+
28+
#if NET8_0_OR_GREATER
29+
/// <summary>
30+
/// Verifies that all items in the filtered collection of <see cref="EventInfo" /> are virtual.
31+
/// </summary>
32+
public static AndOrResult<IAsyncEnumerable<EventInfo?>, IThat<IAsyncEnumerable<EventInfo?>>> AreVirtual(
33+
this IThat<IAsyncEnumerable<EventInfo?>> subject)
34+
=> new(subject.Get().ExpectationBuilder.AddConstraint<IAsyncEnumerable<EventInfo?>>((it, grammars)
35+
=> new AreVirtualConstraint(it, grammars)),
36+
subject);
37+
#endif
38+
39+
/// <summary>
40+
/// Verifies that all items in the filtered collection of <see cref="EventInfo" /> are not virtual.
41+
/// </summary>
42+
public static AndOrResult<IEnumerable<EventInfo?>, IThat<IEnumerable<EventInfo?>>> AreNotVirtual(
43+
this IThat<IEnumerable<EventInfo?>> subject)
44+
=> new(subject.Get().ExpectationBuilder.AddConstraint<IEnumerable<EventInfo?>>((it, grammars)
45+
=> new AreNotVirtualConstraint(it, grammars)),
46+
subject);
47+
48+
#if NET8_0_OR_GREATER
49+
/// <summary>
50+
/// Verifies that all items in the filtered collection of <see cref="EventInfo" /> are not virtual.
51+
/// </summary>
52+
public static AndOrResult<IAsyncEnumerable<EventInfo?>, IThat<IAsyncEnumerable<EventInfo?>>> AreNotVirtual(
53+
this IThat<IAsyncEnumerable<EventInfo?>> subject)
54+
=> new(subject.Get().ExpectationBuilder.AddConstraint<IAsyncEnumerable<EventInfo?>>((it, grammars)
55+
=> new AreNotVirtualConstraint(it, grammars)),
56+
subject);
57+
#endif
58+
59+
private sealed class AreVirtualConstraint(string it, ExpectationGrammars grammars)
60+
: CollectionConstraintResult<EventInfo?>(grammars),
61+
IValueConstraint<IEnumerable<EventInfo?>>
62+
#if NET8_0_OR_GREATER
63+
, IAsyncConstraint<IAsyncEnumerable<EventInfo?>>
64+
#endif
65+
{
66+
#if NET8_0_OR_GREATER
67+
public async Task<ConstraintResult> IsMetBy(IAsyncEnumerable<EventInfo?> actual,
68+
CancellationToken cancellationToken)
69+
=> await SetAsyncValue(actual, @event => @event.IsReallyVirtual());
70+
#endif
71+
72+
public ConstraintResult IsMetBy(IEnumerable<EventInfo?> actual)
73+
=> SetValue(actual, @event => @event.IsReallyVirtual());
74+
75+
protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
76+
=> stringBuilder.Append("are all virtual");
77+
78+
protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
79+
{
80+
stringBuilder.Append(it).Append(" contained non-virtual events ");
81+
Formatter.Format(stringBuilder, NotMatching, FormattingOptions.Indented(indentation));
82+
}
83+
84+
protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
85+
=> stringBuilder.Append("are not all virtual");
86+
87+
protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
88+
{
89+
stringBuilder.Append(it).Append(" only contained virtual events ");
90+
Formatter.Format(stringBuilder, Matching, FormattingOptions.Indented(indentation));
91+
}
92+
}
93+
94+
private sealed class AreNotVirtualConstraint(string it, ExpectationGrammars grammars)
95+
: CollectionConstraintResult<EventInfo?>(grammars),
96+
IValueConstraint<IEnumerable<EventInfo?>>
97+
#if NET8_0_OR_GREATER
98+
, IAsyncConstraint<IAsyncEnumerable<EventInfo?>>
99+
#endif
100+
{
101+
#if NET8_0_OR_GREATER
102+
public async Task<ConstraintResult> IsMetBy(IAsyncEnumerable<EventInfo?> actual,
103+
CancellationToken cancellationToken)
104+
=> await SetAsyncValue(actual, @event => !@event.IsReallyVirtual());
105+
#endif
106+
107+
public ConstraintResult IsMetBy(IEnumerable<EventInfo?> actual)
108+
=> SetValue(actual, @event => !@event.IsReallyVirtual());
109+
110+
protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
111+
=> stringBuilder.Append("are all not virtual");
112+
113+
protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
114+
{
115+
stringBuilder.Append(it).Append(" contained virtual events ");
116+
Formatter.Format(stringBuilder, NotMatching, FormattingOptions.Indented(indentation));
117+
}
118+
119+
protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
120+
=> stringBuilder.Append("also contain a virtual event");
121+
122+
protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
123+
{
124+
stringBuilder.Append(it).Append(" only contained non-virtual events ");
125+
Formatter.Format(stringBuilder, Matching, FormattingOptions.Indented(indentation));
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)