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

Introduce new assertions on ParameterInfo #2385

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

0xced
Copy link
Contributor

@0xced 0xced commented Oct 14, 2023

All the (Not)BeDecoratedWith methods were implemented for MemberInfo but implementing them for ICustomAttributeProvider instead allows for a free ParameterInfoAssertions implementation.

IMPORTANT

  • If the PR touches the public API, the changes have been approved in a separate issue with the "api-approved" label.
  • The code complies with the Coding Guidelines for C#.
  • The changes are covered by unit tests which follow the Arrange-Act-Assert syntax and the naming conventions such as is used in these tests.
  • If the PR adds a feature or fixes a bug, please update the release notes with a functional description that explains what the change means to consumers of this library, which are published on the website.
  • If the PR changes the public API the changes needs to be included by running AcceptApiChanges.ps1 or AcceptApiChanges.sh.
  • If the PR affects the documentation, please include your changes in this pull request so the documentation will appear on the website.
    • Please also run ./build.sh --target spellcheck or .\build.ps1 --target spellcheck before pushing and check the good outcome

@github-actions
Copy link

github-actions bot commented Oct 14, 2023

Qodana for .NET

9 new problems were found

Inspection name Severity Problems
Possible multiple enumeration 🔶 Warning 8
Redundant using directive 🔶 Warning 1

💡 Qodana analysis was run in the pull request mode: only the changed files were checked

View the detailed Qodana report

To be able to view the detailed Qodana report, you can either:

  1. Register at Qodana Cloud and configure the action
  2. Use GitHub Code Scanning with Qodana
  3. Host Qodana report at GitHub Pages
  4. Inspect and use qodana.sarif.json (see the Qodana SARIF format for details)

To get *.log files or any other Qodana artifacts, run the action with upload-result option set to true,
so that the action will upload the files as the job artifacts:

      - name: 'Qodana Scan'
        uses: JetBrains/qodana-action@v2023.2.8
        with:
          upload-result: true
Contact Qodana team

Contact us at qodana-support@jetbrains.com

@coveralls
Copy link

coveralls commented Oct 14, 2023

Pull Request Test Coverage Report for Build 6676121464

  • 84 of 159 (52.83%) changed or added relevant lines in 10 files are covered.
  • 4 unchanged lines in 2 files lost coverage.
  • Overall coverage decreased (-0.6%) to 96.8%

Changes Missing Coverage Covered Lines Changed/Added Lines %
Src/FluentAssertions/Specialized/AssemblyAssertions.cs 0 1 0.0%
Src/FluentAssertions/Types/MemberInfoAssertions.cs 0 1 0.0%
Src/FluentAssertions/Types/ParameterInfoAssertions.cs 4 5 80.0%
Src/FluentAssertions/AssertionExtensions.cs 1 4 25.0%
Src/FluentAssertions/TypeExtensions.cs 0 4 0.0%
Src/FluentAssertions/Types/ParameterInfoSelectorAssertions.cs 0 29 0.0%
Src/FluentAssertions/Types/ParameterInfoSelector.cs 0 36 0.0%
Files with Coverage Reduction New Missed Lines %
Src/FluentAssertions/Types/MemberInfoAssertions.cs 1 50.0%
Src/FluentAssertions/Common/TypeExtensions.cs 3 96.86%
Totals Coverage Status
Change from base Build 6663997299: -0.6%
Covered Lines: 11698
Relevant Lines: 11965

💛 - Coveralls

Copy link
Member

@dennisdoomen dennisdoomen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 We now miss a Parameters() extension method like we have in our Reflection API

/// Contains a number of methods to assert that a <see cref="ICustomAttributeProvider"/> is in the expected state.
/// </summary>
[DebuggerNonUserCode]
public abstract class CustomAttributeProviderAssertions<TSubject, TAssertions> : ReferenceTypeAssertions<TSubject, TAssertions>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Given that this type may contain other assertions that are reusable across the reflection assertions, I think we should name this something like ReflectionAssertions
🌱 I hope using inheritance isn't going to cause issues later on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently our inheritance hierarchy of type-related assertions follows the inheritance hierarchy of the corresponding BCL types.
I notice that e.g. Assembly also implements ICustomAttributeProvider.

How difficult would it be to implement this feature without changing our inheritance hierarchy?

It's not show stopper for me, it just feels a little odd to me to mix these concepts.


public class ParameterInfoAssertionSpecs
{
public class BeDecortatedWithOfT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Typo

public class BeDecortatedWithOfT
{
[Fact]
public void When_asserting_a_parameter_is_decorated_with_attribute_and_it_is_it_succeeds()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 We have been adopting a more fact-based naming convention for new tests, e.g. A_parameter_that_is_decorated_with_the_expected_attribute_is_valid or Succeeds_for_a_parameter_with_the_expected_attribute. For example, It's completely clear from the parent class that every test is about checking whether a parameter is decorated with an attribute, so you can leave that out. Same for the other tests.

}

[Fact]
public void When_subject_is_null_be_decorated_withOfT_should_fail()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 Stay away from technical terms like OfT

}
}

#region Internal classes used in unit tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 We prefer not to use regions anymore.

/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndWhichConstraint<CustomAttributeProviderAssertions<TSubject, TAssertions>, TAttribute> BeDecoratedWith<TAttribute>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return AndWhichConstraint<TAssertions, TAttribute> such that one continue chaining with methods defined on a derived type.

/// Contains a number of methods to assert that a <see cref="ICustomAttributeProvider"/> is in the expected state.
/// </summary>
[DebuggerNonUserCode]
public abstract class CustomAttributeProviderAssertions<TSubject, TAssertions> : ReferenceTypeAssertions<TSubject, TAssertions>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently our inheritance hierarchy of type-related assertions follows the inheritance hierarchy of the corresponding BCL types.
I notice that e.g. Assembly also implements ICustomAttributeProvider.

How difficult would it be to implement this feature without changing our inheritance hierarchy?

It's not show stopper for me, it just feels a little odd to me to mix these concepts.

// Do not use MemberInfo.IsDefined
// There is an issue with PropertyInfo and EventInfo preventing the inherit option to work.
// https://github.com/dotnet/runtime/issues/30219
return Attribute.IsDefined(type, typeof(TAttribute), inherit: false);
return type.IsDefined(typeof(TAttribute), inherit: false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this will be calling the MemberInfo.IsDefined that the comment said not to use.
I dug into this and the related PRs are #748 and #1095.

I checked out develop at #748 and used the prohibited method.
I don't have net45 or netcoreapp1.2 installed, but for the remaining TFMs no tests failed.
Since we no longer support these TFMs directly or indirectly, I think it's safe to use the ICustomAttributeProvider.IsDefined.

@0xced
Copy link
Contributor Author

0xced commented Oct 15, 2023

I won't be able to address all the feedback in the coming weeks so please don't hesitate to adapt everything that needs to be adapted if have the time to.

Also, note that all the specs were mostly copy/pasted from PropertyInfoAssertionSpecs.

@0xced
Copy link
Contributor Author

0xced commented Oct 26, 2023

Wow, I did not realize that this pull request was opening Pandora's box! 😅

Indeed a new Parameters() extension method would be warranted. This also implies designing all the properties that would make sense on a ParameterInfoSelector (e.g. ThatAreInput, ThatHaveDefaultValue etc.) Would you prefer me to add it as part of this pull request or in a follow-up pull request?

And yes, Assembly also implements ICustomAttributeProvider, so does Type! 😁

I'm currently reading Multiple Inheritance in C# on Stack Overflow to see if I can come up with an elegant solution that does not involve changing the inheritance hierarchy.

In the end, that's quite some work ahead for something that was supposed to just be some support for #2380. 😂

@0xced 0xced force-pushed the ParameterInfoAssertions branch 3 times, most recently from 3a11e43 to 17d9331 Compare October 26, 2023 17:20
@dennisdoomen
Copy link
Member

Wow, I did not realize that this pull request was opening Pandora's box!

Sorry about that. This tends to happen with an open-source library that has close to 300 million downloads ;-).

In the end, that's quite some work ahead for something that was supposed to just be some support for #2380. 😂

You could still decouple #2380 from this one.

@0xced 0xced force-pushed the ParameterInfoAssertions branch 3 times, most recently from 4c33b31 to 632a02a Compare October 28, 2023 09:34
@IT-VBFK
Copy link
Contributor

IT-VBFK commented Oct 28, 2023

Think about coverage and qodana scan, if you can :)

@dennisdoomen
Copy link
Member

@0xced did you give up on this?

@0xced
Copy link
Contributor Author

0xced commented Jan 13, 2024

I did not give up on this but since that was just a side effect of #2380 and not something I actually need I did not gave it a high priority.

Copy link

github-actions bot commented Jan 13, 2024

Qodana for .NET

10 new problems were found

Inspection name Severity Problems
Possible multiple enumeration 🔶 Warning 8
Redundant using directive 🔶 Warning 2

💡 Qodana analysis was run in the pull request mode: only the changed files were checked
☁️ View the detailed Qodana report

Contact Qodana team

Contact us at qodana-support@jetbrains.com

All the (Not)BeDecoratedWith(OrInherit) methods are now available on all types that support attributes, i.e. `Assembly`, `MemberInfo`, `ParameterInfo` and `Type`.
TODO: ThatAre... (IsLcid, IsOptional, etc.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants