Skip to content

Commit bb9f3f0

Browse files
[RGen] Bump tests to use xunit 3 to improve the devloop
Port the rgen tests to xunit 3 to improve the devloop by allowing to perform several asserts in a single exception. This allows to be able to assert all the fields of a struct making working with failed asserts in complex structs much easier. To achieve that we had to: 1. Port to xunit3 to be able to access the test current context to deal with multiple asserts. This includes updating our custom attributes. 2. Update all async methods to use the test context cancelation token, should $make tests more reliable when we are using async. The xunit analyzer was screaming at us. 3. Add some methods to test complex structs (more to come). This will make working with the rgen tests a lot easier when they fail.
1 parent dc79f3e commit bb9f3f0

File tree

59 files changed

+444
-200
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+444
-200
lines changed

src/rgen/Microsoft.Macios.Generator/Availability/PlatformAvailability.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ public bool Equals (PlatformAvailability other)
150150
var obsoleteComparer = new DictionaryComparer<Version, (string?, string?)> ();
151151
var unsupportedComparer = new DictionaryComparer<Version, string?> ();
152152

153-
var x = Equals (SupportedVersion, other.SupportedVersion);
154153
return Platform == other.Platform &&
155154
Equals (SupportedVersion, other.SupportedVersion) &&
156155
unsupportedComparer.Equals (unsupported, other.unsupported) &&

src/rgen/Microsoft.Macios.Generator/Availability/SymbolAvailability.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Microsoft.Macios.Generator.Availability;
1212
/// Readonly structure that describes the availability of a symbol in the supported platforms of the SDK.
1313
/// </summary>
1414
readonly partial struct SymbolAvailability : IEquatable<SymbolAvailability> {
15-
static readonly HashSet<ApplePlatform> supportedPlatforms =
15+
public static readonly HashSet<ApplePlatform> SupportedPlatforms =
1616
[ApplePlatform.iOS, ApplePlatform.TVOS, ApplePlatform.MacOSX, ApplePlatform.MacCatalyst];
1717

1818
readonly SortedDictionary<ApplePlatform, PlatformAvailability?> availabilities = new ();
@@ -122,7 +122,7 @@ public SymbolAvailability MergeWithParent (SymbolAvailability? parent)
122122

123123
// create the key value pairs for the supported platforms
124124
var merged = new List<KeyValuePair<ApplePlatform, PlatformAvailability?>> ();
125-
foreach (var platform in supportedPlatforms) {
125+
foreach (var platform in SupportedPlatforms) {
126126
merged.Add (new (platform, Merge (this [platform], parent.Value [platform])));
127127
}
128128

@@ -134,7 +134,7 @@ public bool Equals (SymbolAvailability other)
134134
{
135135
// loop over the supported platforms and ensure that the availabilities are the
136136
// same
137-
foreach (var platform in supportedPlatforms) {
137+
foreach (var platform in SupportedPlatforms) {
138138
if (this [platform] != other [platform]) {
139139
return false;
140140
}
@@ -153,7 +153,7 @@ public override bool Equals (object? obj)
153153
public override int GetHashCode ()
154154
{
155155
var hashCode = new HashCode ();
156-
foreach (var platform in supportedPlatforms) {
156+
foreach (var platform in SupportedPlatforms) {
157157
hashCode.Add (this [platform]);
158158
}
159159

src/rgen/Microsoft.Macios.Generator/Availability/SymbolAvailabilityBuilder.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ PlatformAvailability.Builder GetBuilder (ApplePlatform platform)
5959
/// <param name="url">Optional documentation url.</param>
6060
internal void AddObsoletedVersion (ApplePlatform platform, Version version, string? message, string? url)
6161
{
62-
if (!supportedPlatforms.Contains (platform))
62+
if (!SupportedPlatforms.Contains (platform))
6363
return;
6464

6565
var builder = GetBuilder (platform);
@@ -72,7 +72,7 @@ internal void AddObsoletedVersion (ApplePlatform platform, Version version, stri
7272
/// <param name="obsoletedOsPlatform">The data of a ObsoleteOSPlatformAttribute.</param>
7373
public void Add (ObsoletedOSPlatformData obsoletedOsPlatform)
7474
{
75-
if (!supportedPlatforms.Contains (obsoletedOsPlatform.Platform))
75+
if (!SupportedPlatforms.Contains (obsoletedOsPlatform.Platform))
7676
return;
7777

7878
var builder = GetBuilder (obsoletedOsPlatform.Platform);
@@ -87,7 +87,7 @@ public void Add (ObsoletedOSPlatformData obsoletedOsPlatform)
8787
/// <param name="version">The supported versions to add.</param>
8888
internal void AddSupportedVersion (ApplePlatform platform, Version version)
8989
{
90-
if (!supportedPlatforms.Contains (platform))
90+
if (!SupportedPlatforms.Contains (platform))
9191
return;
9292
var builder = GetBuilder (platform);
9393
builder.AddSupportedVersion (new (version, SupportKind.Explicit));
@@ -99,7 +99,7 @@ internal void AddSupportedVersion (ApplePlatform platform, Version version)
9999
/// <param name="supportedPlatform">The data of a SupportedOSPlatformAttribute.</param>
100100
public void Add (SupportedOSPlatformData supportedPlatform)
101101
{
102-
if (!supportedPlatforms.Contains (supportedPlatform.Platform))
102+
if (!SupportedPlatforms.Contains (supportedPlatform.Platform))
103103
return;
104104

105105
var builder = GetBuilder (supportedPlatform.Platform);
@@ -114,7 +114,7 @@ public void Add (SupportedOSPlatformData supportedPlatform)
114114
/// <param name="message">The optional message of the unsupported version.</param>
115115
internal void AddUnsupportedVersion (ApplePlatform platform, Version version, string? message)
116116
{
117-
if (!supportedPlatforms.Contains (platform))
117+
if (!SupportedPlatforms.Contains (platform))
118118
return;
119119

120120
var builder = GetBuilder (platform);
@@ -127,7 +127,7 @@ internal void AddUnsupportedVersion (ApplePlatform platform, Version version, st
127127
/// <param name="unsupportedPlatform">The data of a UnsupportedOSPlatformAttribute.</param>
128128
public void Add (UnsupportedOSPlatformData unsupportedPlatform)
129129
{
130-
if (!supportedPlatforms.Contains (unsupportedPlatform.Platform))
130+
if (!SupportedPlatforms.Contains (unsupportedPlatform.Platform))
131131
return;
132132

133133
var builder = GetBuilder (unsupportedPlatform.Platform);
@@ -146,7 +146,7 @@ public void Add (UnsupportedOSPlatformData unsupportedPlatform)
146146
public SymbolAvailability ToImmutable ()
147147
{
148148
var dict = new Dictionary<ApplePlatform, PlatformAvailability?> ();
149-
foreach (var platform in supportedPlatforms) {
149+
foreach (var platform in SupportedPlatforms) {
150150
dict [platform] = platforms.ContainsKey (platform)
151151
? platforms [platform].ToImmutable ()
152152
: null;

src/rgen/Microsoft.Macios.Generator/DictionaryComparer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
namespace Microsoft.Macios.Generator;
88

99
public class DictionaryComparer<TKey, TValue> (IEqualityComparer<TValue>? valueComparer = null)
10-
: EqualityComparer<IDictionary<TKey, TValue>>
10+
: EqualityComparer<IReadOnlyDictionary<TKey, TValue>>
1111
where TKey : notnull {
1212
readonly IEqualityComparer<TValue> valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
1313

1414
/// <inheritdoc/>
15-
public override bool Equals (IDictionary<TKey, TValue>? x, IDictionary<TKey, TValue>? y)
15+
public override bool Equals (IReadOnlyDictionary<TKey, TValue>? x, IReadOnlyDictionary<TKey, TValue>? y)
1616
{
1717
if (x is null && y is null)
1818
return true;
@@ -28,7 +28,7 @@ public override bool Equals (IDictionary<TKey, TValue>? x, IDictionary<TKey, TVa
2828
}
2929

3030
/// <inheritdoc/>
31-
public override int GetHashCode (IDictionary<TKey, TValue> obj)
31+
public override int GetHashCode (IReadOnlyDictionary<TKey, TValue> obj)
3232
{
3333
var hash = new HashCode ();
3434
foreach (var (key, value) in obj) {

src/rsp/dotnet/ios-defines-dotnet.rsp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
-d:HAS_AVROUTING
2020
-d:HAS_BACKGROUNDASSETS
2121
-d:HAS_BACKGROUNDTASKS
22+
-d:HAS_BROWSERENGINECORE
2223
-d:HAS_BROWSERENGINEKIT
2324
-d:HAS_BUSINESSCHAT
2425
-d:HAS_CALLKIT
@@ -64,6 +65,7 @@
6465
-d:HAS_GAMECONTROLLER
6566
-d:HAS_GAMEKIT
6667
-d:HAS_GAMEPLAYKIT
68+
-d:HAS_GAMESAVE
6769
-d:HAS_GLKIT
6870
-d:HAS_HEALTHKIT
6971
-d:HAS_HEALTHKITUI
@@ -132,6 +134,7 @@
132134
-d:HAS_SYMBOLS
133135
-d:HAS_SYSTEMCONFIGURATION
134136
-d:HAS_THREADNETWORK
137+
-d:HAS_TOUCHCONTROLLER
135138
-d:HAS_TWITTER
136139
-d:HAS_UIKIT
137140
-d:HAS_UNIFORMTYPEIDENTIFIERS

src/rsp/dotnet/maccatalyst-defines-dotnet.rsp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
-d:HAS_GAMECONTROLLER
6060
-d:HAS_GAMEKIT
6161
-d:HAS_GAMEPLAYKIT
62+
-d:HAS_GAMESAVE
6263
-d:HAS_HEALTHKIT
6364
-d:HAS_HEALTHKITUI
6465
-d:HAS_HOMEKIT

src/rsp/dotnet/macos-defines-dotnet.rsp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
-d:HAS_GAMECONTROLLER
5959
-d:HAS_GAMEKIT
6060
-d:HAS_GAMEPLAYKIT
61+
-d:HAS_GAMESAVE
6162
-d:HAS_GLKIT
6263
-d:HAS_HEALTHKIT
6364
-d:HAS_IMAGECAPTURECORE

tests/common/ConfigurationXUnit.cs

Lines changed: 102 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
5+
using System.Globalization;
46
using System.Reflection;
7+
using System.Reflection.Emit;
8+
using System.Threading.Tasks;
59
using Xamarin.Utils;
10+
using Xunit;
11+
using Xunit.Internal;
612
using Xunit.Sdk;
13+
using Xunit.v3;
714

815
namespace Xamarin.Tests {
916

10-
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
17+
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true)]
1118
public sealed class PlatformInlineDataAttribute : DataAttribute {
12-
readonly object [] dataValues;
19+
readonly object [] data;
1320
public PlatformInlineDataAttribute (ApplePlatform platform, params object [] parameters)
1421
{
1522
// data values are the join of the platform and all other values passed to the attr
16-
dataValues = parameters.Prepend (platform).ToArray ();
23+
data = parameters.Prepend (platform).ToArray ();
1724
// based on the passed platform and the configuration, decide if we skip the test
1825
switch (platform) {
1926
case ApplePlatform.iOS:
@@ -37,17 +44,33 @@ public PlatformInlineDataAttribute (ApplePlatform platform, params object [] par
3744
}
3845
}
3946

40-
public object [] DataValues {
41-
get { return dataValues; }
47+
public override bool SupportsDiscoveryEnumeration () => true;
48+
49+
public object [] Data {
50+
get { return data; }
4251
}
4352

44-
public override IEnumerable<object []> GetData (MethodInfo testMethod)
53+
public override ValueTask<IReadOnlyCollection<ITheoryDataRow>> GetData (MethodInfo testMethod, DisposalTracker disposalTracker)
4554
{
46-
yield return dataValues;
55+
var traits = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
56+
TestIntrospectionHelper.MergeTraitsInto(traits, Traits);
57+
return new([
58+
new TheoryDataRow (Data)
59+
{
60+
Explicit = ExplicitAsNullable,
61+
Label = Label,
62+
Skip = Skip,
63+
TestDisplayName = TestDisplayName,
64+
Timeout = TimeoutAsNullable,
65+
Traits = traits,
66+
}
67+
]);
68+
4769
}
70+
4871
}
4972

50-
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
73+
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true)]
5174
public sealed class AllSupportedPlatformsAttribute : DataAttribute {
5275

5376
readonly object [] dataValues;
@@ -56,40 +79,95 @@ public AllSupportedPlatformsAttribute (params object [] parameters)
5679
{
5780
dataValues = parameters;
5881
}
59-
60-
public override IEnumerable<object []> GetData (MethodInfo testMethod)
82+
83+
public override bool SupportsDiscoveryEnumeration () => true;
84+
85+
public override ValueTask<IReadOnlyCollection<ITheoryDataRow>> GetData(
86+
MethodInfo testMethod,
87+
DisposalTracker disposalTracker)
6188
{
62-
return Configuration.
63-
GetIncludedPlatforms ().
64-
Select (platform => dataValues.Prepend (platform).ToArray ());
89+
var result = new List<ITheoryDataRow>();
90+
91+
foreach (var platform in Configuration.GetIncludedPlatforms ()) {
92+
var row = dataValues.Prepend (platform).ToArray ();
93+
result.Add (ConvertDataRow (row));
94+
}
95+
96+
return ValueTask.FromResult(result.CastOrToReadOnlyCollection());
6597
}
6698
}
6799

68-
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
100+
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true)]
69101
public sealed class AllSupportedPlatformsClassDataAttribute<T> : DataAttribute where T : IEnumerable<object []> {
70102
readonly Type dataAttributeType;
71103

72104
public AllSupportedPlatformsClassDataAttribute ()
73105
{
74106
dataAttributeType = typeof (T);
75107
}
108+
109+
public override bool SupportsDiscoveryEnumeration() =>
110+
!typeof(IDisposable).IsAssignableFrom(dataAttributeType) && !typeof(IAsyncDisposable).IsAssignableFrom(dataAttributeType);
111+
112+
/// <inheritdoc/>
113+
protected override ITheoryDataRow ConvertDataRow(object dataRow)
114+
{
115+
Guard.ArgumentNotNull(dataRow);
76116

77-
public override IEnumerable<object []> GetData (MethodInfo testMethod)
117+
try
118+
{
119+
return base.ConvertDataRow(dataRow);
120+
}
121+
catch (ArgumentException)
122+
{
123+
throw new ArgumentException(
124+
string.Format(
125+
CultureInfo.CurrentCulture,
126+
"Class '{0}' yielded an item of type '{1}' which is not an 'object?[]', 'Xunit.ITheoryDataRow' or 'System.Runtime.CompilerServices.ITuple'",
127+
dataAttributeType.FullName,
128+
dataRow?.GetType().SafeName()
129+
),
130+
nameof(dataRow)
131+
);
132+
}
133+
}
134+
135+
public override async ValueTask<IReadOnlyCollection<ITheoryDataRow>> GetData(
136+
MethodInfo testMethod,
137+
DisposalTracker disposalTracker)
78138
{
79-
// we are going to get the instance of the IEnumerable, loop through it and yield the parameters
80-
// per platform
81-
var enumerable = Activator.CreateInstance (dataAttributeType) as IEnumerable<object []>;
82-
if (enumerable is null)
83-
yield break;
84-
foreach (var platform in Configuration.GetIncludedPlatforms ()) {
85-
foreach (var parameters in enumerable) {
86-
yield return parameters.Prepend (platform).ToArray ();
139+
var classInstance = Activator.CreateInstance (dataAttributeType);
140+
disposalTracker.Add(classInstance);
141+
142+
if (classInstance is IAsyncLifetime classLifetime)
143+
await classLifetime.InitializeAsync();
144+
145+
if (classInstance is IEnumerable<object[]> dataItems)
146+
{
147+
var result = new List<ITheoryDataRow>();
148+
149+
foreach (var platform in Configuration.GetIncludedPlatforms ()) {
150+
foreach (var row in dataItems) {
151+
152+
var platformRow = row.Prepend (platform).ToArray ();
153+
result.Add (ConvertDataRow (platformRow));
154+
}
87155
}
156+
157+
return result.CastOrToReadOnlyCollection();
88158
}
159+
160+
throw new ArgumentException(
161+
string.Format(
162+
CultureInfo.CurrentCulture,
163+
"'{0}' must implement one of the following interfaces to be used as ClassData:{1}- IEnumerable<object[]>{1}",
164+
dataAttributeType.FullName,
165+
Environment.NewLine
166+
)
167+
);
89168
}
90169
}
91170

92-
93171
public partial class Configuration {
94172
static string TestAssemblyDirectory {
95173
get {

tests/rgen/Directory.Build.props

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project>
2+
3+
<!-- Common package versions -->
4+
<PropertyGroup>
5+
<Version_Microsoft_NET_Test_Sdk>17.13.0</Version_Microsoft_NET_Test_Sdk>
6+
<Version_xunit_runner_visualstudio>3.1.1</Version_xunit_runner_visualstudio>
7+
<Version_xunit_analyzers>1.23.0-pre.3</Version_xunit_analyzers>
8+
<Version_xunit_v3>3.0.0-pre.25</Version_xunit_v3>
9+
</PropertyGroup>
10+
11+
<PropertyGroup>
12+
<LangVersion>13.0</LangVersion>
13+
<Nullable>enable</Nullable>
14+
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<Content Include="$(MSBuildThisFileDirectory)xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
19+
</ItemGroup>
20+
21+
</Project>

tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Microsoft.Macios.Bindings.Analyzer.Tests.csproj

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,9 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.3-beta1.24352.1"/>
1312
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"/>
14-
<PackageReference Include="xunit" Version="2.4.2"/>
15-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
16-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
17-
<PrivateAssets>all</PrivateAssets>
18-
</PackageReference>
13+
<PackageReference Include="xunit.v3" Version="$(Version_xunit_v3)" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="$(Version_xunit_runner_visualstudio)" PrivateAssets="all" />
1915
</ItemGroup>
2016

2117
<ItemGroup>

0 commit comments

Comments
 (0)