Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow IAsyncEnumerable for ValueSource/TestCaseSource #637

Merged
merged 1 commit into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 10 additions & 10 deletions documentation/NUnit1015.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NUnit1015

## The source type does not implement IEnumerable
## The source type does not implement I(Async)Enumerable

| Topic | Value
| :-- | :--
Expand All @@ -12,7 +12,7 @@

## Description

The source type must implement IEnumerable in order to provide test cases.
The source type must implement I(Async)Enumerable in order to provide test cases.

## Motivation

Expand Down Expand Up @@ -45,13 +45,13 @@ class DivideCases

### Explanation

In the sample above, the class `DivideCases` does not implement `IEnumerable`.
In the sample above, the class `DivideCases` does not implement `IEnumerable` nor `IAsyncEnumerable`

However, source types specified by `TestCaseSource` [must implement `IEnumerable`](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute).
However, source types specified by `TestCaseSource` [must implement `IEnumerable` or `IAsyncEnumerable`](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute).

### Fix

Make the source type implement `IEnumerable`:
Make the source type implement `IEnumerable` or `IAsyncEnumerable`

```csharp
public class MyTestClass
Expand Down Expand Up @@ -84,7 +84,7 @@ Configure the severity per project, for more info see [MSDN](https://learn.micro
### Via .editorconfig file

```ini
# NUnit1015: The source type does not implement IEnumerable
# NUnit1015: The source type does not implement I(Async)Enumerable
dotnet_diagnostic.NUnit1015.severity = chosenSeverity
```

Expand All @@ -93,22 +93,22 @@ where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`,
### Via #pragma directive

```csharp
#pragma warning disable NUnit1015 // The source type does not implement IEnumerable
#pragma warning disable NUnit1015 // The source type does not implement I(Async)Enumerable
Code violating the rule here
#pragma warning restore NUnit1015 // The source type does not implement IEnumerable
#pragma warning restore NUnit1015 // The source type does not implement I(Async)Enumerable
```

Or put this at the top of the file to disable all instances.

```csharp
#pragma warning disable NUnit1015 // The source type does not implement IEnumerable
#pragma warning disable NUnit1015 // The source type does not implement I(Async)Enumerable
```

### Via attribute `[SuppressMessage]`

```csharp
[System.Diagnostics.CodeAnalysis.SuppressMessage("Structure",
"NUnit1015:The source type does not implement IEnumerable",
"NUnit1015:The source type does not implement I(Async)Enumerable",
Justification = "Reason...")]
```
<!-- end generated config severity -->
20 changes: 10 additions & 10 deletions documentation/NUnit1019.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NUnit1019

## The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable
## The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable

| Topic | Value
| :-- | :--
Expand All @@ -12,7 +12,7 @@

## Description

The source specified by the TestCaseSource must return an IEnumerable or a type that implements IEnumerable.
The source specified by the TestCaseSource must return an I(Async)Enumerable or a type that implements I(Async)Enumerable.

## Motivation

Expand All @@ -36,14 +36,14 @@ public class AnalyzeWhenSourceDoesProvideIEnumerable

### Explanation

In the sample above, the source specified by `TestCaseSource` - the field `testCases` - does not return an `IEnumerable` or a type that implements `IEnumerable`,
In the sample above, the source specified by `TestCaseSource` - the field `testCases` - does not return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`,
instead it returns an `int`.

However, sources specified by `TestCaseSource` [must return an `IEnumerable` or a type that implements `IEnumerable`.](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute).
However, sources specified by `TestCaseSource` [must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute).

### Fix

Change `testCases` to return an `IEnumerable` or a type that implements `IEnumerable`:
Change `testCases` to return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`:

```csharp
public class AnalyzeWhenSourceDoesProvideIEnumerable
Expand All @@ -67,7 +67,7 @@ Configure the severity per project, for more info see [MSDN](https://learn.micro
### Via .editorconfig file

```ini
# NUnit1019: The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable
# NUnit1019: The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
dotnet_diagnostic.NUnit1019.severity = chosenSeverity
```

Expand All @@ -76,22 +76,22 @@ where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`,
### Via #pragma directive

```csharp
#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable
#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
Code violating the rule here
#pragma warning restore NUnit1019 // The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable
#pragma warning restore NUnit1019 // The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
```

Or put this at the top of the file to disable all instances.

```csharp
#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable
#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
```

### Via attribute `[SuppressMessage]`

```csharp
[System.Diagnostics.CodeAnalysis.SuppressMessage("Structure",
"NUnit1019:The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable",
"NUnit1019:The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable",
Justification = "Reason...")]
```
<!-- end generated config severity -->
20 changes: 10 additions & 10 deletions documentation/NUnit1024.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NUnit1024

## The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable
## The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable

| Topic | Value
| :-- | :--
Expand All @@ -12,7 +12,7 @@

## Description

The source specified by the ValueSource must return an IEnumerable or a type that implements IEnumerable.
The source specified by the ValueSource must return an I(Async)Enumerable or a type that implements I(Async)Enumerable.

## Motivation

Expand All @@ -36,14 +36,14 @@ public class AnalyzeWhenSourceDoesProvideIEnumerable

### Explanation

In the sample above, the source specified by `ValueSource` - the field `testCases` - does not return an `IEnumerable` or a type that implements `IEnumerable`,
In the sample above, the source specified by `ValueSource` - the field `testCases` - does not return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`,
instead it returns an `int`.

However, sources specified by `ValueSource` [must return an `IEnumerable` or a type that implements `IEnumerable`.](https://github.com/nunit/docs/wiki/ValueSource-Attribute).
However, sources specified by `ValueSource` [must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](https://github.com/nunit/docs/wiki/ValueSource-Attribute).

### Fix

Change `testCases` to return an `IEnumerable` or a type that implements `IEnumerable`:
Change `testCases` to return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`:

```csharp
public class AnalyzeWhenSourceDoesProvideIEnumerable
Expand All @@ -67,7 +67,7 @@ Configure the severity per project, for more info see [MSDN](https://learn.micro
### Via .editorconfig file

```ini
# NUnit1024: The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable
# NUnit1024: The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
dotnet_diagnostic.NUnit1024.severity = chosenSeverity
```

Expand All @@ -76,22 +76,22 @@ where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`,
### Via #pragma directive

```csharp
#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable
#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
Code violating the rule here
#pragma warning restore NUnit1024 // The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable
#pragma warning restore NUnit1024 // The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
```

Or put this at the top of the file to disable all instances.

```csharp
#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable
#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable
```

### Via attribute `[SuppressMessage]`

```csharp
[System.Diagnostics.CodeAnalysis.SuppressMessage("Structure",
"NUnit1024:The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable",
"NUnit1024:The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable",
Justification = "Reason...")]
```
<!-- end generated config severity -->
6 changes: 3 additions & 3 deletions documentation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ Rules which enforce structural requirements on the test code.
| [NUnit1012](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1012.md) | The async test method must have a non-void return type | :white_check_mark: | :exclamation: | :x: |
| [NUnit1013](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1013.md) | The async test method must have a non-generic Task return type when no result is expected | :white_check_mark: | :exclamation: | :x: |
| [NUnit1014](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1014.md) | The async test method must have a Task\<T> return type when a result is expected | :white_check_mark: | :exclamation: | :x: |
| [NUnit1015](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1015.md) | The source type does not implement IEnumerable | :white_check_mark: | :exclamation: | :x: |
| [NUnit1015](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1015.md) | The source type does not implement I(Async)Enumerable | :white_check_mark: | :exclamation: | :x: |
| [NUnit1016](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1016.md) | The source type does not have a default constructor | :white_check_mark: | :exclamation: | :x: |
| [NUnit1017](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1017.md) | The specified source is not static | :white_check_mark: | :exclamation: | :x: |
| [NUnit1018](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1018.md) | The number of parameters provided by the TestCaseSource does not match the number of parameters in the target method | :white_check_mark: | :exclamation: | :x: |
| [NUnit1019](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1019.md) | The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable | :white_check_mark: | :exclamation: | :x: |
| [NUnit1019](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1019.md) | The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable | :white_check_mark: | :exclamation: | :x: |
| [NUnit1020](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1020.md) | The TestCaseSource provides parameters to a source - field or property - that expects no parameters | :white_check_mark: | :exclamation: | :x: |
| [NUnit1021](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1021.md) | The ValueSource should use nameof operator to specify target | :white_check_mark: | :warning: | :white_check_mark: |
| [NUnit1022](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1022.md) | The specified source is not static | :white_check_mark: | :exclamation: | :x: |
| [NUnit1023](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1023.md) | The target method expects parameters which cannot be supplied by the ValueSource | :white_check_mark: | :exclamation: | :x: |
| [NUnit1024](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1024.md) | The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable | :white_check_mark: | :exclamation: | :x: |
| [NUnit1024](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1024.md) | The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable | :white_check_mark: | :exclamation: | :x: |
| [NUnit1025](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1025.md) | The ValueSource argument does not specify an existing member | :white_check_mark: | :exclamation: | :x: |
| [NUnit1026](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1026.md) | The test or setup/teardown method is not public | :white_check_mark: | :exclamation: | :white_check_mark: |
| [NUnit1027](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1027.md) | The test method has parameters, but no arguments are supplied by attributes | :white_check_mark: | :exclamation: | :x: |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Gu.Roslyn.Asserts;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Analyzers.Constants;
using NUnit.Analyzers.TestCaseSourceUsage;
Expand Down Expand Up @@ -51,10 +54,37 @@ class MyTests

var expectedDiagnostic = ExpectedDiagnostic
.Create(AnalyzerIdentifiers.TestCaseSourceSourceTypeNotIEnumerable)
.WithMessage("The source type 'MyTests' does not implement IEnumerable");
.WithMessage("The source type 'MyTests' does not implement I(Async)Enumerable");
RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeWhenTypeOfSourceImplementsIAsyncEnumerable()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
public class AnalyzeWhenTypeOfSourceImplementsIAsyncEnumerable
{
[TestCaseSource(typeof(MyTests))]
public void Test(int i)
{
}
}

public sealed class MyTests : IAsyncEnumerable<int>
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
}",
additionalUsings: "using System.Collections.Generic;using System.Threading;");

IEnumerable<MetadataReference> asyncEnumerableMetadata = MetadataReferences.Transitive(typeof(IAsyncEnumerable<>));
IEnumerable<MetadataReference> metadataReferences = (Settings.Default.MetadataReferences ?? Enumerable.Empty<MetadataReference>()).Concat(asyncEnumerableMetadata);

RoslynAssert.Valid(analyzer, testCode, Settings.Default.WithMetadataReferences(metadataReferences));
}

[Test]
public void AnalyzeWhenTypeOfSourceNoDefaultConstructor()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ public void Test()

var expectedDiagnostic = ExpectedDiagnostic
.Create(AnalyzerIdentifiers.TestCaseSourceDoesNotReturnIEnumerable)
.WithMessage($"The TestCaseSource does not return an IEnumerable or a type that implements IEnumerable. Instead it returns a '{returnType}'.");
.WithMessage($"The TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable. Instead it returns a '{returnType}'.");
RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Gu.Roslyn.Asserts;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
Expand Down Expand Up @@ -199,7 +201,8 @@ public static IEnumerable<int> TestData(string dummy, int anotherDummy)
[TestCase("private static TestCaseData[] TestCases => new TestCaseData[0];")]
[TestCase("private static TestCaseData[] TestCases() => new TestCaseData[0];")]
[TestCase("private static Task<TestCaseData[]> TestCases() => Task.FromResult(new TestCaseData[0]);")]
public void AnalyzeWhenSourceDoesProvideIEnumerable(string testCaseMember)
[TestCase("private static async IAsyncEnumerable<int> TestCases() { foreach (var value in new[] { 0 }) { yield return value; await Task.Yield(); } }", "using System.Collections.Generic;")]
public void AnalyzeWhenSourceDoesProvideIEnumerable(string testCaseMember, string? additionalUsings = null)
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@"
public class AnalyzeWhenSourceDoesProvideIEnumerable
Expand All @@ -210,9 +213,12 @@ public class AnalyzeWhenSourceDoesProvideIEnumerable
public void Test([ValueSource(nameof(TestCases))] int number)
{{
}}
}}");
}}", additionalUsings);

RoslynAssert.Valid(analyzer, testCode);
IEnumerable<MetadataReference> asyncEnumerableMetadata = MetadataReferences.Transitive(typeof(IAsyncEnumerable<>));
IEnumerable<MetadataReference> metadataReferences = (Settings.Default.MetadataReferences ?? Enumerable.Empty<MetadataReference>()).Concat(asyncEnumerableMetadata);

RoslynAssert.Valid(analyzer, testCode, Settings.Default.WithMetadataReferences(metadataReferences));
}

[TestCase("private static readonly object? TestCases = null;", "object?")]
Expand All @@ -230,7 +236,7 @@ public void Test([ValueSource(nameof(TestCases))] int number)
public void AnalyzeWhenSourceDoesNotProvideIEnumerable(string testCaseMember, string returnType)
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@"
public class AnalyzeWhenSourceDoesProvideIEnumerable
public class AnalyzeWhenSourceDoesNotProvideIEnumerable
{{
#pragma warning disable CS0414 // Consider changing the field to a 'const'
{testCaseMember}
Expand All @@ -244,7 +250,7 @@ public void Test([ValueSource(nameof(TestCases))] int number)

var expectedDiagnostic = ExpectedDiagnostic
.Create(AnalyzerIdentifiers.ValueSourceDoesNotReturnIEnumerable)
.WithMessage($"The ValueSource does not return an IEnumerable or a type that implements IEnumerable. Instead it returns a '{returnType}'.");
.WithMessage($"The ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable. Instead it returns a '{returnType}'.");
RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

Expand Down