From 55f16ba2c1e8adf09f107bcfa1d1fac2e5b5bf45 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Sun, 19 Apr 2026 17:15:50 +0200 Subject: [PATCH 1/4] fix: resolve CodeQL warnings across Core and test projects - Combine nested if statements in IgnoreVirtualMembersSpecimenBuilder - Add ToString() override to AbstractTestClass in AutoDataAdapterAttributeTests - Use object instead of var to fix incomparable-types Equals in ValuesRequestTests - Remove redundant GetHashCode() call on int in ValuesRequestTests - Refactor CustomizeFixture foreach to use Select projection in AutoDataAdapterAttribute - Replace as+Assert.NotNull with Assert.IsType in Assert section in CustomizeWithAttributeTests and AutoDataAttributeProviderTests Co-Authored-By: Claude Sonnet 4.6 --- .../tasks/task-16 - Fix-CodeQL-warnings.md | 24 + .../AutoDataAdapterAttributeTests.cs | 344 ++++++------ .../Attributes/CustomizeWithAttributeTests.cs | 504 +++++++++--------- .../AutoDataAttributeProviderTests.cs | 4 +- .../Requests/ValuesRequestTests.cs | 498 ++++++++--------- .../Attributes/AutoDataAdapterAttribute.cs | 127 +++-- .../IgnoreVirtualMembersSpecimenBuilder.cs | 73 ++- 7 files changed, 795 insertions(+), 779 deletions(-) create mode 100644 .backlog/tasks/task-16 - Fix-CodeQL-warnings.md diff --git a/.backlog/tasks/task-16 - Fix-CodeQL-warnings.md b/.backlog/tasks/task-16 - Fix-CodeQL-warnings.md new file mode 100644 index 00000000..93589a0c --- /dev/null +++ b/.backlog/tasks/task-16 - Fix-CodeQL-warnings.md @@ -0,0 +1,24 @@ +--- +id: TASK-16 +title: Fix CodeQL warnings +status: Done +assignee: + - piotrzajac + - claude +created_date: '2026-04-19 14:23' +updated_date: '2026-04-19 15:13' +labels: [] +dependencies: [] +priority: medium +--- + +## Acceptance Criteria + +- [x] #1 Collapsible if statements combined (IgnoreVirtualMembersSpecimenBuilder) +- [x] #2 Default ToString() override added (AbstractTestClass in AutoDataAdapterAttributeTests) +- [x] #3 Change var to object to resolve incomparable-types Equals warning (ValuesRequestTests) +- [x] #4 Useless call to GetHashCode() as value is its own hash (ValuesRequestTests) +- [x] #5 AutoDataAdapterAttribute CustomizeFixture: foreach refactored to use Select projection. +- [x] #6 CustomizeWithAttributeTests: as+Assert.NotNull replaced with Assert.IsType in Assert section (lines 136, 157) +- [x] #7 AutoDataAttributeProviderTests: same pattern applied (line 22) + diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs index 0fd8b588..8983f90e 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs @@ -1,172 +1,172 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Attributes -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Reflection; - - using global::AutoFixture; - using global::AutoFixture.Kernel; - using global::AutoFixture.Xunit2; - using Objectivity.AutoFixture.XUnit2.Core.Attributes; - - using Xunit; - - [Collection("AutoDataAdapterAttribute")] - [Trait("Category", "DataAttribute")] - [SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Test objects")] - public class AutoDataAdapterAttributeTests - { - [AutoData] - [Theory(DisplayName = "GIVEN fixture WHEN constructor is invoked THEN passed fixture is being adapted and inline values collection is empty")] - public void GivenFixture_WhenConstructorIsInvoked_ThenPassedFixtureIsBeingAdaptedAndInlineValuesCollectionIsEmpty(Fixture fixture) - { - // Arrange - // Act - var attribute = new AutoDataAdapterAttribute(fixture, null); - - // Assert - Assert.Equal(fixture, attribute.AdaptedFixture); - Assert.Empty(attribute.InlineValues); - } - - [Fact(DisplayName = "GIVEN uninitialized fixture WHEN constructor is invoked THEN exception is thrown")] - public void GivenUninitializedFixture_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - // Act - static object Act() => new AutoDataAdapterAttribute(null); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("fixture", exception.ParamName); - } - - [AutoData] - [Theory(DisplayName = "GIVEN uninitialized method info WHEN GetData is invoked THEN exception is thrown")] - public void GivenUninitializedMethodInfo_WhenConstructorIsInvoked_ThenExceptionIsThrown(Fixture fixture) - { - // Arrange - var attribute = new AutoDataAdapterAttribute(fixture); - - // Act - object Act() => attribute.GetData(null); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("testMethod", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN test data with instance WHEN GetData called THEN auto data generation skipped")] - public void GivenTestDataWithInstance_WhenGetDataCalled_ThenAutoDataGenerationSkipped() - { - // Arrange - IFixture fixture = new Fixture(); - var attribute = new AutoDataAdapterAttribute(fixture, SpecificTestClass.Create()); - var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithAbstractTestClass), BindingFlags.Instance | BindingFlags.NonPublic); - var expectedNumberOfParameters = methodInfo.GetParameters().Length; - - // Act - var data = attribute.GetData(methodInfo).ToArray(); - - // Assert - Assert.Single(data); - Assert.Equal(expectedNumberOfParameters, data.First().Length); - Assert.All(data.First(), Assert.NotNull); - Assert.All(data.First().Skip(1), item => Assert.Equal(data.First().Last(), item)); - } - - [Fact(DisplayName = "GIVEN empty test data WHEN GetData called THEN auto data generation throws exception")] - public void GivenEmptyTestData_WhenGetDataCalled_ThenAutoDataGenerationSkipped() - { - // Arrange - IFixture fixture = new Fixture(); - var attribute = new AutoDataAdapterAttribute(fixture); - var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithAbstractTestClass), BindingFlags.Instance | BindingFlags.NonPublic); - - // Act - void Act() => attribute.GetData(methodInfo); - - // Assert - Assert.ThrowsAny(Act); - } - - [Fact(DisplayName = "GIVEN test method with Frozen customization after others WHEN GetData called THEN ensure parameter is frozen on the end")] - public void GivenTestMethodWithFrozenCustomizationAfterOthers_WhenGetDataCalled_ThenEnsureParameterIsFrozenOnTheEnd() - { - // Arrange - IFixture fixture = new Fixture(); - var attribute = new AutoDataAdapterAttribute(fixture, null); - var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithFrozenCustomizationBeforeOthers), BindingFlags.Instance | BindingFlags.NonPublic); - var expectedNumberOfParameters = methodInfo.GetParameters().Length; - - // Act - var data = attribute.GetData(methodInfo).ToArray(); - - // Assert - Assert.Single(data); - Assert.Equal(expectedNumberOfParameters, data.First().Length); - Assert.All(data.First(), Assert.NotNull); - Assert.All( - data.First().Skip(1), - x => - { - var parameters = data.First(); - Assert.Equal(parameters.Last(), x); - Assert.NotEqual(parameters.First(), x); - Assert.Contains(StopStringCustomization.Value, x as string); - }); - } - - protected string TestMethodWithAbstractTestClass( - SpecificTestClass instance, - [Frozen] string text, - string message) - { - return $"{instance}: {text}, {message}"; - } - - protected string TestMethodWithFrozenCustomizationBeforeOthers( - string first, - [Frozen][StopString] string second, - string third) - { - return $"{first}: {second}, {third}"; - } - - [SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty", Justification = "Test class")] - protected abstract class AbstractTestClass - { - } - - protected sealed class SpecificTestClass : AbstractTestClass - { - private SpecificTestClass() - { - } - - public static AbstractTestClass Create() - { - return new SpecificTestClass(); - } - } - - protected sealed class StopStringAttribute : CustomizeWithAttribute - { - } - - [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "This class is instantiated indirectly.")] - protected class StopStringCustomization : ICustomization - { - public const string Value = "STOP"; - - public void Customize(IFixture fixture) - { - fixture.Customizations.Add( - new FilteringSpecimenBuilder( - new FixedBuilder(Value), - new ExactTypeSpecification(Value.GetType()))); - } - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Attributes +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Reflection; + + using global::AutoFixture; + using global::AutoFixture.Kernel; + using global::AutoFixture.Xunit2; + using Objectivity.AutoFixture.XUnit2.Core.Attributes; + + using Xunit; + + [Collection("AutoDataAdapterAttribute")] + [Trait("Category", "DataAttribute")] + [SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Test objects")] + public class AutoDataAdapterAttributeTests + { + [AutoData] + [Theory(DisplayName = "GIVEN fixture WHEN constructor is invoked THEN passed fixture is being adapted and inline values collection is empty")] + public void GivenFixture_WhenConstructorIsInvoked_ThenPassedFixtureIsBeingAdaptedAndInlineValuesCollectionIsEmpty(Fixture fixture) + { + // Arrange + // Act + var attribute = new AutoDataAdapterAttribute(fixture, null); + + // Assert + Assert.Equal(fixture, attribute.AdaptedFixture); + Assert.Empty(attribute.InlineValues); + } + + [Fact(DisplayName = "GIVEN uninitialized fixture WHEN constructor is invoked THEN exception is thrown")] + public void GivenUninitializedFixture_WhenConstructorIsInvoked_ThenExceptionIsThrown() + { + // Arrange + // Act + static object Act() => new AutoDataAdapterAttribute(null); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("fixture", exception.ParamName); + } + + [AutoData] + [Theory(DisplayName = "GIVEN uninitialized method info WHEN GetData is invoked THEN exception is thrown")] + public void GivenUninitializedMethodInfo_WhenConstructorIsInvoked_ThenExceptionIsThrown(Fixture fixture) + { + // Arrange + var attribute = new AutoDataAdapterAttribute(fixture); + + // Act + object Act() => attribute.GetData(null); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("testMethod", exception.ParamName); + } + + [Fact(DisplayName = "GIVEN test data with instance WHEN GetData called THEN auto data generation skipped")] + public void GivenTestDataWithInstance_WhenGetDataCalled_ThenAutoDataGenerationSkipped() + { + // Arrange + IFixture fixture = new Fixture(); + var attribute = new AutoDataAdapterAttribute(fixture, SpecificTestClass.Create()); + var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithAbstractTestClass), BindingFlags.Instance | BindingFlags.NonPublic); + var expectedNumberOfParameters = methodInfo.GetParameters().Length; + + // Act + var data = attribute.GetData(methodInfo).ToArray(); + + // Assert + Assert.Single(data); + Assert.Equal(expectedNumberOfParameters, data.First().Length); + Assert.All(data.First(), Assert.NotNull); + Assert.All(data.First().Skip(1), item => Assert.Equal(data.First().Last(), item)); + } + + [Fact(DisplayName = "GIVEN empty test data WHEN GetData called THEN auto data generation throws exception")] + public void GivenEmptyTestData_WhenGetDataCalled_ThenAutoDataGenerationSkipped() + { + // Arrange + IFixture fixture = new Fixture(); + var attribute = new AutoDataAdapterAttribute(fixture); + var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithAbstractTestClass), BindingFlags.Instance | BindingFlags.NonPublic); + + // Act + void Act() => attribute.GetData(methodInfo); + + // Assert + Assert.ThrowsAny(Act); + } + + [Fact(DisplayName = "GIVEN test method with Frozen customization after others WHEN GetData called THEN ensure parameter is frozen on the end")] + public void GivenTestMethodWithFrozenCustomizationAfterOthers_WhenGetDataCalled_ThenEnsureParameterIsFrozenOnTheEnd() + { + // Arrange + IFixture fixture = new Fixture(); + var attribute = new AutoDataAdapterAttribute(fixture, null); + var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithFrozenCustomizationBeforeOthers), BindingFlags.Instance | BindingFlags.NonPublic); + var expectedNumberOfParameters = methodInfo.GetParameters().Length; + + // Act + var data = attribute.GetData(methodInfo).ToArray(); + + // Assert + Assert.Single(data); + Assert.Equal(expectedNumberOfParameters, data.First().Length); + Assert.All(data.First(), Assert.NotNull); + Assert.All( + data.First().Skip(1), + x => + { + var parameters = data.First(); + Assert.Equal(parameters.Last(), x); + Assert.NotEqual(parameters.First(), x); + Assert.Contains(StopStringCustomization.Value, x as string); + }); + } + + protected string TestMethodWithAbstractTestClass( + SpecificTestClass instance, + [Frozen] string text, + string message) + { + return $"{instance}: {text}, {message}"; + } + + protected string TestMethodWithFrozenCustomizationBeforeOthers( + string first, + [Frozen][StopString] string second, + string third) + { + return $"{first}: {second}, {third}"; + } + + protected abstract class AbstractTestClass + { + public override string ToString() => this.GetType().Name; + } + + protected sealed class SpecificTestClass : AbstractTestClass + { + private SpecificTestClass() + { + } + + public static AbstractTestClass Create() + { + return new SpecificTestClass(); + } + } + + protected sealed class StopStringAttribute : CustomizeWithAttribute + { + } + + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "This class is instantiated indirectly.")] + protected class StopStringCustomization : ICustomization + { + public const string Value = "STOP"; + + public void Customize(IFixture fixture) + { + fixture.Customizations.Add( + new FilteringSpecimenBuilder( + new FixedBuilder(Value), + new ExactTypeSpecification(Value.GetType()))); + } + } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs index 8994768b..a3eab524 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs @@ -1,253 +1,251 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Attributes -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Reflection; - - using global::AutoFixture; - using global::AutoFixture.Kernel; - using global::AutoFixture.Xunit2; - - using Objectivity.AutoFixture.XUnit2.Core.Attributes; - using Objectivity.AutoFixture.XUnit2.Core.Customizations; - - using Xunit; - - [Collection("CustomizeWithAttribute")] - [Trait("Category", "CustomizeAttribute")] - public class CustomizeWithAttributeTests - { - public static TheoryData ArgumentsDiscoveryCustomizationTestData { get; } = new() - { - { false, null, 0 }, - { false, Array.Empty(), 0 }, - { false, new object[] { bool.TrueString }, 1 }, - { true, null, 1 }, - { true, Array.Empty(), 1 }, - { true, new object[] { bool.TrueString }, 2 }, - }; - - [Fact(DisplayName = "GIVEN customization type with no arguments WHEN GetCustomization is invoked THEN customization instance is returned")] - public void GivenCustomizationTypeWithNoArguments_WhenGetCustomizationIsInvoked_ThenCustomizationInstanceIsReturned() - { - // Arrange - var customizationType = typeof(DoNotThrowOnRecursionCustomization); - var customizeAttribute = new CustomizeWithAttribute(customizationType); - - // Act - var customization = customizeAttribute.GetCustomization(null); - - // Assert - Assert.NotNull(customization); - Assert.IsType(customizationType, customization); - } - - [Fact(DisplayName = "GIVEN customization type with arguments WHEN GetCustomization is invoked THEN customization instance is returned")] - public void GivenCustomizationTypeWithArguments_WhenGetCustomizationIsInvoked_ThenCustomizationInstanceIsReturned() - { - // Arrange - var customizationType = typeof(AutoDataCommonCustomization); - var customizeAttribute = new CustomizeWithAttribute(customizationType, true); - - // Act - var customization = customizeAttribute.GetCustomization(null); - - // Assert - Assert.NotNull(customization); - Assert.IsType(customizationType, customization); - } - - [Fact(DisplayName = "GIVEN customization type requiring arguments without any WHEN GetCustomization is invoked THEN exception is thrown")] - public void GivenCustomizationTypeRequiringArgumentsWithoutAny_WhenGetCustomizationIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var customizationType = typeof(AutoDataCommonCustomization); - var customizeAttribute = new CustomizeWithAttribute(customizationType); - - // Act - object Act() => customizeAttribute.GetCustomization(null); - - // Assert - Assert.Throws(Act); - } - - [Fact(DisplayName = "GIVEN CustomizeWithAttribute with IncludeParameterType set WHEN GetCustomization without ParameterInfo is invoked THEN exception is thrown")] - public void GivenAttributeWithIncludeParameterTypeSet_WhenGetCustomizationWithoutParameterInfoIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var customizationType = typeof(DoNotThrowOnRecursionCustomization); - var customizeAttribute = new CustomizeWithAttribute(customizationType) { IncludeParameterType = true }; - - // Act - object Act() => customizeAttribute.GetCustomization(null); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("parameter", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN uninitialized type WHEN constructor is invoked THEN exception is thrown")] - public void GivenUninitializedType_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - const Type customizationType = null; - - // Act - static object Act() => new CustomizeWithAttribute(customizationType); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("type", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN unsupported type WHEN constructor is invoked THEN exception is thrown")] - public void GivenUnsupportedType_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var customizationType = typeof(string); - - // Act - object Act() => new CustomizeWithAttribute(customizationType); - - // Assert - var exception = Assert.Throws(Act); - Assert.NotNull(exception.Message); - Assert.NotEmpty(exception.Message); - Assert.Contains(nameof(ICustomization), exception.Message); - } - - [Fact(DisplayName = "GIVEN CustomizeWith attribute with IncludeParameterType set WHEN GetCustomization is invoked THEN customization with expected type is returned")] - public void GivenCustomizeWithAttributeWithIncludeParameterTypeSet_WhenGetCustomizationIsInvoked_ThenCustomizationWithExpectedTypeIsReturned() - { - // Arrange - var customizationType = typeof(ArgumentsDiscoveryCustomization); - var customizeAttribute = new CustomizeWithAttribute(customizationType) { IncludeParameterType = true }; - var parameter = typeof(CustomizeWithAttributeTests) - .GetMethod(nameof(this.MethodUnderTest), BindingFlags.Instance | BindingFlags.NonPublic) - .GetParameters() - .First(); - - // Act - var customization = customizeAttribute.GetCustomization(parameter) as ArgumentsDiscoveryCustomization; - - // Assert - Assert.NotNull(customization); - Assert.IsType(customizationType, customization); - Assert.Single(customization.Args); - Assert.Equal(parameter.ParameterType, customization.Args.First()); - } - - [MemberData(nameof(ArgumentsDiscoveryCustomizationTestData))] - [Theory(DisplayName = "GIVEN CustomizeWith attribute with arguments WHEN GetCustomization is invoked THEN expected customization is returned with expected number of arguments")] - public void GivenCustomizeWithAttributeWithArguments_WhenGetCustomizationIsInvoked_ThenExpectedCustomizationIsReturnedWithExpectedNumberOfArguments(bool includeParameterType, object[] args, int expectedNumberOfArguments) - { - // Arrange - var customizationType = typeof(ArgumentsDiscoveryCustomization); - var customizeAttribute = new CustomizeWithAttribute(customizationType, args) { IncludeParameterType = includeParameterType }; - var parameter = typeof(CustomizeWithAttributeTests) - .GetMethod(nameof(this.MethodUnderTest), BindingFlags.Instance | BindingFlags.NonPublic) - .GetParameters() - .First(); - - // Act - var customization = customizeAttribute.GetCustomization(parameter) as ArgumentsDiscoveryCustomization; - - // Assert - Assert.NotNull(customization); - Assert.IsType(customizationType, customization); - Assert.Equal(expectedNumberOfArguments, customization.Args.Count); - } - - [AutoData] - [Theory(DisplayName = "GIVEN CustomizeWith applied to the second argument WHEN data populated THEN only second one has customization")] - public void GivenCustomizeWithAppliedToTheSecondArgument_WhenDataPopulated_ThenOnlySecondOneHasCustomization( - IList instanceWithoutCustomization, - [EmptyCollection] IList instanceWithCustomization) - { - // Arrange - // Act - // Assert - Assert.NotEmpty(instanceWithoutCustomization); - Assert.Empty(instanceWithCustomization); - } - - [AutoData] - [Theory(DisplayName = "GIVEN CustomizeWith applied to the first argument WHEN data populated THEN all arguments has customization")] - public void GivenCustomizeWithAppliedToTheFirstArgument_WhenDataPopulated_ThenAllArgumentsHasCustomization( - [CustomizeWith(typeof(EmptyCollectionCustomization), typeof(IList))] IList instanceWithCustomization, - IList instanceWithoutCustomization) - { - // Arrange - // Act - // Assert - Assert.Empty(instanceWithoutCustomization); - Assert.Empty(instanceWithCustomization); - } - - [AutoData] - [Theory(DisplayName = "GIVEN CustomizeWith applied to the first argument of a certain type WHEN data populated THEN only the first one has customization")] - public void GivenCustomizeWithAppliedToTheFirstArgumentOfACertainType_WhenDataPopulated_ThenOnlyTheFirstOneHasCustomization( - [EmptyCollection] IList instanceWithCustomization, - IList instanceOfDifferentTypeWithoutCustomization) - { - // Arrange - // Act - // Assert - Assert.Empty(instanceWithCustomization); - Assert.NotEmpty(instanceOfDifferentTypeWithoutCustomization); - } - - [SuppressMessage("Roslynator", "RCS1163:Unused parameter.", Justification = "Required for test.")] - [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required for test.")] - [SuppressMessage("ReSharper", "UnusedParameter.Global", Justification = "Required for test.")] - protected void MethodUnderTest(bool parameter) - { - // Empty method under test - } - - protected sealed class EmptyCollectionAttribute : CustomizeWithAttribute - { - public EmptyCollectionAttribute() - { - this.IncludeParameterType = true; - } - } - - protected class EmptyCollectionCustomization : ICustomization - { - public EmptyCollectionCustomization(Type reflectedType) - { - this.ReflectedType = reflectedType; - } - - public Type ReflectedType { get; } - - public void Customize(IFixture fixture) - { - var emptyArray = Array.CreateInstance(this.ReflectedType.GenericTypeArguments.Single(), 0); - - fixture.Customizations.Add( - new FilteringSpecimenBuilder( - new FixedBuilder(emptyArray), - new ExactTypeSpecification(this.ReflectedType))); - } - } - - protected class ArgumentsDiscoveryCustomization : ICustomization - { - public ArgumentsDiscoveryCustomization(params object[] args) - { - this.Args = args; - } - - public IReadOnlyCollection Args { get; } - - public void Customize(IFixture fixture) - { - // Method intentionally left empty. - } - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Attributes +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Reflection; + + using global::AutoFixture; + using global::AutoFixture.Kernel; + using global::AutoFixture.Xunit2; + + using Objectivity.AutoFixture.XUnit2.Core.Attributes; + using Objectivity.AutoFixture.XUnit2.Core.Customizations; + + using Xunit; + + [Collection("CustomizeWithAttribute")] + [Trait("Category", "CustomizeAttribute")] + public class CustomizeWithAttributeTests + { + public static TheoryData ArgumentsDiscoveryCustomizationTestData { get; } = new() + { + { false, null, 0 }, + { false, Array.Empty(), 0 }, + { false, new object[] { bool.TrueString }, 1 }, + { true, null, 1 }, + { true, Array.Empty(), 1 }, + { true, new object[] { bool.TrueString }, 2 }, + }; + + [Fact(DisplayName = "GIVEN customization type with no arguments WHEN GetCustomization is invoked THEN customization instance is returned")] + public void GivenCustomizationTypeWithNoArguments_WhenGetCustomizationIsInvoked_ThenCustomizationInstanceIsReturned() + { + // Arrange + var customizationType = typeof(DoNotThrowOnRecursionCustomization); + var customizeAttribute = new CustomizeWithAttribute(customizationType); + + // Act + var customization = customizeAttribute.GetCustomization(null); + + // Assert + Assert.NotNull(customization); + Assert.IsType(customizationType, customization); + } + + [Fact(DisplayName = "GIVEN customization type with arguments WHEN GetCustomization is invoked THEN customization instance is returned")] + public void GivenCustomizationTypeWithArguments_WhenGetCustomizationIsInvoked_ThenCustomizationInstanceIsReturned() + { + // Arrange + var customizationType = typeof(AutoDataCommonCustomization); + var customizeAttribute = new CustomizeWithAttribute(customizationType, true); + + // Act + var customization = customizeAttribute.GetCustomization(null); + + // Assert + Assert.NotNull(customization); + Assert.IsType(customizationType, customization); + } + + [Fact(DisplayName = "GIVEN customization type requiring arguments without any WHEN GetCustomization is invoked THEN exception is thrown")] + public void GivenCustomizationTypeRequiringArgumentsWithoutAny_WhenGetCustomizationIsInvoked_ThenExceptionIsThrown() + { + // Arrange + var customizationType = typeof(AutoDataCommonCustomization); + var customizeAttribute = new CustomizeWithAttribute(customizationType); + + // Act + object Act() => customizeAttribute.GetCustomization(null); + + // Assert + Assert.Throws(Act); + } + + [Fact(DisplayName = "GIVEN CustomizeWithAttribute with IncludeParameterType set WHEN GetCustomization without ParameterInfo is invoked THEN exception is thrown")] + public void GivenAttributeWithIncludeParameterTypeSet_WhenGetCustomizationWithoutParameterInfoIsInvoked_ThenExceptionIsThrown() + { + // Arrange + var customizationType = typeof(DoNotThrowOnRecursionCustomization); + var customizeAttribute = new CustomizeWithAttribute(customizationType) { IncludeParameterType = true }; + + // Act + object Act() => customizeAttribute.GetCustomization(null); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("parameter", exception.ParamName); + } + + [Fact(DisplayName = "GIVEN uninitialized type WHEN constructor is invoked THEN exception is thrown")] + public void GivenUninitializedType_WhenConstructorIsInvoked_ThenExceptionIsThrown() + { + // Arrange + const Type customizationType = null; + + // Act + static object Act() => new CustomizeWithAttribute(customizationType); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("type", exception.ParamName); + } + + [Fact(DisplayName = "GIVEN unsupported type WHEN constructor is invoked THEN exception is thrown")] + public void GivenUnsupportedType_WhenConstructorIsInvoked_ThenExceptionIsThrown() + { + // Arrange + var customizationType = typeof(string); + + // Act + object Act() => new CustomizeWithAttribute(customizationType); + + // Assert + var exception = Assert.Throws(Act); + Assert.NotNull(exception.Message); + Assert.NotEmpty(exception.Message); + Assert.Contains(nameof(ICustomization), exception.Message); + } + + [Fact(DisplayName = "GIVEN CustomizeWith attribute with IncludeParameterType set WHEN GetCustomization is invoked THEN customization with expected type is returned")] + public void GivenCustomizeWithAttributeWithIncludeParameterTypeSet_WhenGetCustomizationIsInvoked_ThenCustomizationWithExpectedTypeIsReturned() + { + // Arrange + var customizationType = typeof(ArgumentsDiscoveryCustomization); + var customizeAttribute = new CustomizeWithAttribute(customizationType) { IncludeParameterType = true }; + var parameter = typeof(CustomizeWithAttributeTests) + .GetMethod(nameof(this.MethodUnderTest), BindingFlags.Instance | BindingFlags.NonPublic) + .GetParameters() + .First(); + + // Act + var customization = customizeAttribute.GetCustomization(parameter) as ArgumentsDiscoveryCustomization; + + // Assert + var typed = Assert.IsType(customization); + Assert.Single(typed.Args); + Assert.Equal(parameter.ParameterType, typed.Args.First()); + } + + [MemberData(nameof(ArgumentsDiscoveryCustomizationTestData))] + [Theory(DisplayName = "GIVEN CustomizeWith attribute with arguments WHEN GetCustomization is invoked THEN expected customization is returned with expected number of arguments")] + public void GivenCustomizeWithAttributeWithArguments_WhenGetCustomizationIsInvoked_ThenExpectedCustomizationIsReturnedWithExpectedNumberOfArguments(bool includeParameterType, object[] args, int expectedNumberOfArguments) + { + // Arrange + var customizationType = typeof(ArgumentsDiscoveryCustomization); + var customizeAttribute = new CustomizeWithAttribute(customizationType, args) { IncludeParameterType = includeParameterType }; + var parameter = typeof(CustomizeWithAttributeTests) + .GetMethod(nameof(this.MethodUnderTest), BindingFlags.Instance | BindingFlags.NonPublic) + .GetParameters() + .First(); + + // Act + var customization = customizeAttribute.GetCustomization(parameter) as ArgumentsDiscoveryCustomization; + + // Assert + var typed = Assert.IsType(customization); + Assert.Equal(expectedNumberOfArguments, typed.Args.Count); + } + + [AutoData] + [Theory(DisplayName = "GIVEN CustomizeWith applied to the second argument WHEN data populated THEN only second one has customization")] + public void GivenCustomizeWithAppliedToTheSecondArgument_WhenDataPopulated_ThenOnlySecondOneHasCustomization( + IList instanceWithoutCustomization, + [EmptyCollection] IList instanceWithCustomization) + { + // Arrange + // Act + // Assert + Assert.NotEmpty(instanceWithoutCustomization); + Assert.Empty(instanceWithCustomization); + } + + [AutoData] + [Theory(DisplayName = "GIVEN CustomizeWith applied to the first argument WHEN data populated THEN all arguments has customization")] + public void GivenCustomizeWithAppliedToTheFirstArgument_WhenDataPopulated_ThenAllArgumentsHasCustomization( + [CustomizeWith(typeof(EmptyCollectionCustomization), typeof(IList))] IList instanceWithCustomization, + IList instanceWithoutCustomization) + { + // Arrange + // Act + // Assert + Assert.Empty(instanceWithoutCustomization); + Assert.Empty(instanceWithCustomization); + } + + [AutoData] + [Theory(DisplayName = "GIVEN CustomizeWith applied to the first argument of a certain type WHEN data populated THEN only the first one has customization")] + public void GivenCustomizeWithAppliedToTheFirstArgumentOfACertainType_WhenDataPopulated_ThenOnlyTheFirstOneHasCustomization( + [EmptyCollection] IList instanceWithCustomization, + IList instanceOfDifferentTypeWithoutCustomization) + { + // Arrange + // Act + // Assert + Assert.Empty(instanceWithCustomization); + Assert.NotEmpty(instanceOfDifferentTypeWithoutCustomization); + } + + [SuppressMessage("Roslynator", "RCS1163:Unused parameter.", Justification = "Required for test.")] + [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required for test.")] + [SuppressMessage("ReSharper", "UnusedParameter.Global", Justification = "Required for test.")] + protected void MethodUnderTest(bool parameter) + { + // Empty method under test + } + + protected sealed class EmptyCollectionAttribute : CustomizeWithAttribute + { + public EmptyCollectionAttribute() + { + this.IncludeParameterType = true; + } + } + + protected class EmptyCollectionCustomization : ICustomization + { + public EmptyCollectionCustomization(Type reflectedType) + { + this.ReflectedType = reflectedType; + } + + public Type ReflectedType { get; } + + public void Customize(IFixture fixture) + { + var emptyArray = Array.CreateInstance(this.ReflectedType.GenericTypeArguments.Single(), 0); + + fixture.Customizations.Add( + new FilteringSpecimenBuilder( + new FixedBuilder(emptyArray), + new ExactTypeSpecification(this.ReflectedType))); + } + } + + protected class ArgumentsDiscoveryCustomization : ICustomization + { + public ArgumentsDiscoveryCustomization(params object[] args) + { + this.Args = args; + } + + public IReadOnlyCollection Args { get; } + + public void Customize(IFixture fixture) + { + // Method intentionally left empty. + } + } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs index 4029bbc2..79ce945d 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs @@ -22,8 +22,8 @@ public void GivenInitializedFixture_WhenGetAttributeIsInvoked_ThenAttributeWithS var dataAttribute = provider.GetAttribute(fixture) as AutoDataAdapterAttribute; // Assert - Assert.NotNull(dataAttribute); - Assert.Equal(fixture, dataAttribute.AdaptedFixture); + var typed = Assert.IsType(dataAttribute); + Assert.Equal(fixture, typed.AdaptedFixture); } } } diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs index 59803ec6..5e95b8ad 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs @@ -1,249 +1,249 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Requests -{ - using System; - using System.Collections; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Linq; - using global::AutoFixture.Xunit2; - using Objectivity.AutoFixture.XUnit2.Core.Requests; - - using Xunit; - - [Collection("ValuesRequest")] - [Trait("Category", "Requests")] - public class ValuesRequestTests - { - public static TheoryData ComparisonTestData { get; } = new() - { - { typeof(int), new[] { 1 }, typeof(int), new[] { 1 }, true }, - { typeof(int), new[] { 1, 2, 3 }, typeof(int), new[] { 3, 2, 1 }, true }, - { typeof(int?), new[] { 1, (int?)null }, typeof(int?), new[] { 1, null, (int?)null }, true }, - { typeof(int), new[] { 1 }, typeof(long), new[] { 1 }, false }, - { typeof(int), new[] { 1 }, typeof(int), new[] { 2 }, false }, - { typeof(int), new[] { 1, 2 }, typeof(int), new[] { 2 }, false }, - }; - - [Fact(DisplayName = "GIVEN uninitialized type argument WHEN constructor is invoked THEN exception is thrown")] - public void GivenUninitializedTypeArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - Type type = null; - object[] values = null; - - // Act - object Act() => new FixedValuesRequest(type, values); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("operandType", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN uninitialized values argument WHEN constructor is invoked THEN exception is thrown")] - public void GivenUninitializedValuesArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var type = typeof(int); - object[] values = null; - - // Act - object Act() => new FixedValuesRequest(type, values); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("values", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN empty values argument WHEN constructor is invoked THEN exception is thrown")] - public void GivenEmptyValuesArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var type = typeof(int); - var values = Array.Empty(); - - // Act - object Act() => new FixedValuesRequest(type, values); - - // Assert - var exception = Assert.Throws(Act); - Assert.NotNull(exception.Message); - Assert.NotEmpty(exception.Message); - Assert.Contains("At least one value", exception.Message); - } - - [InlineData(typeof(int), 2)] - [InlineData(1, typeof(int))] - [Theory(DisplayName = "GIVEN different type arguments WHEN constructor is invoked THEN parameters are properly assigned")] - public void GivenDifferentTypeArguments_WhenConstructorIsInvoked_ThenParametersAreProperlyAssigned( - params object[] values) - { - // Arrange - var type = typeof(int); - - // Act - var attribute = new FixedValuesRequest(type, values); - - // Assert - Assert.Equal(2, attribute.Values.Count); - Assert.Equal(values, attribute.Values); - } - - [Fact(DisplayName = "GIVEN valid arguments WHEN ToString is invoked THEN text conteins necessary information")] - public void GivenValidArguments_WhenToStringIsInvoked_ThenTextConteinsNecessaryInformation() - { - // Arrange - var type = typeof(float); - const int first = int.MaxValue; - long? second = long.MinValue; - byte? third = null; - var attribute = new FixedValuesRequest(type, first, second, third); - var textParts = new[] - { - nameof(FixedValuesRequest), - type.Name, - "[Int32]", - $"{first.ToString(CultureInfo.InvariantCulture)},", - "[Int64]", - $"{second.Value.ToString(CultureInfo.InvariantCulture)},", - "[Object]", - "null", - }; - - // Act - var text = attribute.ToString(); - - // Assert - Assert.All(textParts, textPart => Assert.Contains(textPart, text)); - } - - [MemberData(nameof(ComparisonTestData))] - [Theory(DisplayName = "GIVEN two requests WHEN Equals is invoked THEN expected value is returned")] - public void GivenTwoRequests_WhenEqualsIsInvoked_ThenExpectedValueIsReturned( - Type typeA, - IEnumerable valuesA, - Type typeB, - IEnumerable valuesB, - bool expectedResult) - { - // Arrange - var a = new FixedValuesRequest(typeA, valuesA.Cast().ToArray()); - var b = new FixedValuesRequest(typeB, valuesB.Cast().ToArray()); - - // Act - var result = Equals(a, b); - - // Assert - Assert.Equal(expectedResult, result); - } - - [MemberData(nameof(ComparisonTestData))] - [Theory(DisplayName = "GIVEN two requests WHEN hashcodes are compared THEN expected value is returned")] - public void GivenTwoRequests_WhenHashCodesAreCompared_ThenExpectedValueIsReturned( - Type typeA, - IEnumerable valuesA, - Type typeB, - IEnumerable valuesB, - bool expectedResult) - { - // Arrange - var a = new FixedValuesRequest(typeA, valuesA.Cast().ToArray()); - var b = new FixedValuesRequest(typeB, valuesB.Cast().ToArray()); - - // Act - var hashA = a.GetHashCode(); - var hashB = b.GetHashCode(); - var result = hashA == hashB; - - // Assert - Assert.Equal(expectedResult, result); - } - - [Fact(DisplayName = "GIVEN uninitialized request WHEN Equals is invoked THEN False is returned")] - [SuppressMessage("Maintainability", "CA1508:Avoid dead conditional code", Justification = "Required to test the logic")] - public void GivenUninitializedRequest_WhenEqualsIsInvoked_ThenFalseIsReturned() - { - // Arrange - var initialized = new FixedValuesRequest(typeof(int), 1); - FixedValuesRequest uninitialized = null; - - // Act - var result = initialized.Equals(uninitialized); - - // Assert - Assert.False(result); - } - - [Fact(DisplayName = "GIVEN different type of object WHEN Equals is invoked THEN False is returned")] - [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global", Justification = "This is expected comparioson to test logic.")] - public void GivenDifferentTypeOfObject_WhenEqualsIsInvoked_ThenFalseIsReturned() - { - // Arrange - var request = new FixedValuesRequest(typeof(int), 1); - var differentObject = typeof(int); - - // Act - var result = request.Equals(differentObject); - - // Assert - Assert.False(result); - } - - [AutoData] - [Theory(DisplayName = "GIVEN different type of ValuesRequest WHEN Equals is invoked THEN False is returned")] - [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global", Justification = "This is expected comparioson to test logic.")] - public void GivenDifferentTypeOfValuesRequest_WhenEqualsIsInvoked_ThenFalseIsReturned( - int value) - { - // Arrange - var type = value.GetType(); - var fixedRequest = new FixedValuesRequest(type, value); - var exceptRequest = new ExceptValuesRequest(type, value); - - // Act - var result = fixedRequest.Equals(exceptRequest); - var text = fixedRequest.ToString(); - - // Assert - Assert.False(result); - Assert.NotNull(text); - } - - [AutoData] - [Theory(DisplayName = "GIVEN ValuesRequests of different type WHEN hashcodes are compared THEN hashcodes are not equal")] - public void GivenValuesRequestsOfDifferentType_WhenHashCodesAreCompared_ThenHashCodesAreNotEqual( - int value) - { - // Arrange - var type = value.GetType(); - var fixedRequest = new FixedValuesRequest(type, value); - var exceptRequest = new ExceptValuesRequest(type, value); - - // Act - var hashA = fixedRequest.GetHashCode(); - var hashB = exceptRequest.GetHashCode(); - var result = hashA == hashB; - - // Assert - Assert.False(result); - } - - [InlineAutoData(typeof(ExceptValuesRequest))] - [InlineAutoData(typeof(FixedValuesRequest))] - [Theory(DisplayName = "GIVEN ValuesRequest with single value WHEN GetHashCode is invoked THEN expected value is returned")] - public void GivenValuesRequestWithSingleValue_WhenGetHashCodeIsInvoked_ThenExpectedValueIsReturned( - Type requestType, - int value) - { - // Arrange - var valueType = value.GetType(); - var request = Activator.CreateInstance(requestType, valueType, value); - var expectedHashCode = valueType.GetHashCode() ^ requestType.GetHashCode() ^ value.GetHashCode(); - - // Act - var actualHashCode = request.GetHashCode(); - - // Assert - Assert.Equal(expectedHashCode, actualHashCode); - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Requests +{ + using System; + using System.Collections; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using global::AutoFixture.Xunit2; + using Objectivity.AutoFixture.XUnit2.Core.Requests; + + using Xunit; + + [Collection("ValuesRequest")] + [Trait("Category", "Requests")] + public class ValuesRequestTests + { + public static TheoryData ComparisonTestData { get; } = new() + { + { typeof(int), new[] { 1 }, typeof(int), new[] { 1 }, true }, + { typeof(int), new[] { 1, 2, 3 }, typeof(int), new[] { 3, 2, 1 }, true }, + { typeof(int?), new[] { 1, (int?)null }, typeof(int?), new[] { 1, null, (int?)null }, true }, + { typeof(int), new[] { 1 }, typeof(long), new[] { 1 }, false }, + { typeof(int), new[] { 1 }, typeof(int), new[] { 2 }, false }, + { typeof(int), new[] { 1, 2 }, typeof(int), new[] { 2 }, false }, + }; + + [Fact(DisplayName = "GIVEN uninitialized type argument WHEN constructor is invoked THEN exception is thrown")] + public void GivenUninitializedTypeArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() + { + // Arrange + Type type = null; + object[] values = null; + + // Act + object Act() => new FixedValuesRequest(type, values); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("operandType", exception.ParamName); + } + + [Fact(DisplayName = "GIVEN uninitialized values argument WHEN constructor is invoked THEN exception is thrown")] + public void GivenUninitializedValuesArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() + { + // Arrange + var type = typeof(int); + object[] values = null; + + // Act + object Act() => new FixedValuesRequest(type, values); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("values", exception.ParamName); + } + + [Fact(DisplayName = "GIVEN empty values argument WHEN constructor is invoked THEN exception is thrown")] + public void GivenEmptyValuesArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() + { + // Arrange + var type = typeof(int); + var values = Array.Empty(); + + // Act + object Act() => new FixedValuesRequest(type, values); + + // Assert + var exception = Assert.Throws(Act); + Assert.NotNull(exception.Message); + Assert.NotEmpty(exception.Message); + Assert.Contains("At least one value", exception.Message); + } + + [InlineData(typeof(int), 2)] + [InlineData(1, typeof(int))] + [Theory(DisplayName = "GIVEN different type arguments WHEN constructor is invoked THEN parameters are properly assigned")] + public void GivenDifferentTypeArguments_WhenConstructorIsInvoked_ThenParametersAreProperlyAssigned( + params object[] values) + { + // Arrange + var type = typeof(int); + + // Act + var attribute = new FixedValuesRequest(type, values); + + // Assert + Assert.Equal(2, attribute.Values.Count); + Assert.Equal(values, attribute.Values); + } + + [Fact(DisplayName = "GIVEN valid arguments WHEN ToString is invoked THEN text conteins necessary information")] + public void GivenValidArguments_WhenToStringIsInvoked_ThenTextConteinsNecessaryInformation() + { + // Arrange + var type = typeof(float); + const int first = int.MaxValue; + long? second = long.MinValue; + byte? third = null; + var attribute = new FixedValuesRequest(type, first, second, third); + var textParts = new[] + { + nameof(FixedValuesRequest), + type.Name, + "[Int32]", + $"{first.ToString(CultureInfo.InvariantCulture)},", + "[Int64]", + $"{second.Value.ToString(CultureInfo.InvariantCulture)},", + "[Object]", + "null", + }; + + // Act + var text = attribute.ToString(); + + // Assert + Assert.All(textParts, textPart => Assert.Contains(textPart, text)); + } + + [MemberData(nameof(ComparisonTestData))] + [Theory(DisplayName = "GIVEN two requests WHEN Equals is invoked THEN expected value is returned")] + public void GivenTwoRequests_WhenEqualsIsInvoked_ThenExpectedValueIsReturned( + Type typeA, + IEnumerable valuesA, + Type typeB, + IEnumerable valuesB, + bool expectedResult) + { + // Arrange + var a = new FixedValuesRequest(typeA, valuesA.Cast().ToArray()); + var b = new FixedValuesRequest(typeB, valuesB.Cast().ToArray()); + + // Act + var result = Equals(a, b); + + // Assert + Assert.Equal(expectedResult, result); + } + + [MemberData(nameof(ComparisonTestData))] + [Theory(DisplayName = "GIVEN two requests WHEN hashcodes are compared THEN expected value is returned")] + public void GivenTwoRequests_WhenHashCodesAreCompared_ThenExpectedValueIsReturned( + Type typeA, + IEnumerable valuesA, + Type typeB, + IEnumerable valuesB, + bool expectedResult) + { + // Arrange + var a = new FixedValuesRequest(typeA, valuesA.Cast().ToArray()); + var b = new FixedValuesRequest(typeB, valuesB.Cast().ToArray()); + + // Act + var hashA = a.GetHashCode(); + var hashB = b.GetHashCode(); + var result = hashA == hashB; + + // Assert + Assert.Equal(expectedResult, result); + } + + [Fact(DisplayName = "GIVEN uninitialized request WHEN Equals is invoked THEN False is returned")] + [SuppressMessage("Maintainability", "CA1508:Avoid dead conditional code", Justification = "Required to test the logic")] + public void GivenUninitializedRequest_WhenEqualsIsInvoked_ThenFalseIsReturned() + { + // Arrange + var initialized = new FixedValuesRequest(typeof(int), 1); + FixedValuesRequest uninitialized = null; + + // Act + var result = initialized.Equals(uninitialized); + + // Assert + Assert.False(result); + } + + [Fact(DisplayName = "GIVEN different type of object WHEN Equals is invoked THEN False is returned")] + [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global", Justification = "This is expected comparison to test logic.")] + public void GivenDifferentTypeOfObject_WhenEqualsIsInvoked_ThenFalseIsReturned() + { + // Arrange + var request = new FixedValuesRequest(typeof(int), 1); + object differentObject = typeof(int); + + // Act + var result = request.Equals(differentObject); + + // Assert + Assert.False(result); + } + + [AutoData] + [Theory(DisplayName = "GIVEN different type of ValuesRequest WHEN Equals is invoked THEN False is returned")] + [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global", Justification = "This is expected comparison to test logic.")] + public void GivenDifferentTypeOfValuesRequest_WhenEqualsIsInvoked_ThenFalseIsReturned( + int value) + { + // Arrange + var type = value.GetType(); + var fixedRequest = new FixedValuesRequest(type, value); + var exceptRequest = new ExceptValuesRequest(type, value); + + // Act + var result = fixedRequest.Equals(exceptRequest); + var text = fixedRequest.ToString(); + + // Assert + Assert.False(result); + Assert.NotNull(text); + } + + [AutoData] + [Theory(DisplayName = "GIVEN ValuesRequests of different type WHEN hashcodes are compared THEN hashcodes are not equal")] + public void GivenValuesRequestsOfDifferentType_WhenHashCodesAreCompared_ThenHashCodesAreNotEqual( + int value) + { + // Arrange + var type = value.GetType(); + var fixedRequest = new FixedValuesRequest(type, value); + var exceptRequest = new ExceptValuesRequest(type, value); + + // Act + var hashA = fixedRequest.GetHashCode(); + var hashB = exceptRequest.GetHashCode(); + var result = hashA == hashB; + + // Assert + Assert.False(result); + } + + [InlineAutoData(typeof(ExceptValuesRequest))] + [InlineAutoData(typeof(FixedValuesRequest))] + [Theory(DisplayName = "GIVEN ValuesRequest with single value WHEN GetHashCode is invoked THEN expected value is returned")] + public void GivenValuesRequestWithSingleValue_WhenGetHashCodeIsInvoked_ThenExpectedValueIsReturned( + Type requestType, + int value) + { + // Arrange + var valueType = value.GetType(); + var request = Activator.CreateInstance(requestType, valueType, value); + var expectedHashCode = valueType.GetHashCode() ^ requestType.GetHashCode() ^ value; + + // Act + var actualHashCode = request.GetHashCode(); + + // Assert + Assert.Equal(expectedHashCode, actualHashCode); + } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.Core/Attributes/AutoDataAdapterAttribute.cs b/src/Objectivity.AutoFixture.XUnit2.Core/Attributes/AutoDataAdapterAttribute.cs index 83cad92e..65612c24 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core/Attributes/AutoDataAdapterAttribute.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core/Attributes/AutoDataAdapterAttribute.cs @@ -1,64 +1,63 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Attributes -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - - using global::AutoFixture; - using global::AutoFixture.Kernel; - using global::AutoFixture.Xunit2; - using Objectivity.AutoFixture.XUnit2.Core.Common; - using Objectivity.AutoFixture.XUnit2.Core.Comparers; - - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - internal sealed class AutoDataAdapterAttribute : AutoDataAttribute - { - public AutoDataAdapterAttribute(IFixture fixture, params object[] inlineValues) - : base(() => fixture) - { - this.AdaptedFixture = fixture.NotNull(nameof(fixture)); - this.InlineValues = Array.AsReadOnly(inlineValues ?? Array.Empty()); - } - - public IFixture AdaptedFixture { get; } - - public IReadOnlyCollection InlineValues { get; } - - public override IEnumerable GetData(MethodInfo testMethod) - { - // This is an extension of AutoDataAttribute.GetData method - // with the ability to skip already provided inline values - var parameters = testMethod.NotNull(nameof(testMethod)).GetParameters(); - var specimens = new List(this.InlineValues); - foreach (var p in parameters.Skip(this.InlineValues.Count)) - { - this.CustomizeFixture(p); - - var specimen = this.Resolve(p); - specimens.Add(specimen); - } - - return new[] { specimens.ToArray() }; - } - - private void CustomizeFixture(ParameterInfo p) - { - var customizeAttributes = p.GetCustomAttributes() - .OfType() - .OrderBy(x => x, new CustomizeAttributeComparer()); - - foreach (var ca in customizeAttributes) - { - var c = ca.GetCustomization(p); - this.AdaptedFixture.Customize(c); - } - } - - private object Resolve(ParameterInfo p) - { - var context = new SpecimenContext(this.AdaptedFixture); - return context.Resolve(p); - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Attributes +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + using global::AutoFixture; + using global::AutoFixture.Kernel; + using global::AutoFixture.Xunit2; + using Objectivity.AutoFixture.XUnit2.Core.Common; + using Objectivity.AutoFixture.XUnit2.Core.Comparers; + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + internal sealed class AutoDataAdapterAttribute : AutoDataAttribute + { + public AutoDataAdapterAttribute(IFixture fixture, params object[] inlineValues) + : base(() => fixture) + { + this.AdaptedFixture = fixture.NotNull(nameof(fixture)); + this.InlineValues = Array.AsReadOnly(inlineValues ?? Array.Empty()); + } + + public IFixture AdaptedFixture { get; } + + public IReadOnlyCollection InlineValues { get; } + + public override IEnumerable GetData(MethodInfo testMethod) + { + // This is an extension of AutoDataAttribute.GetData method + // with the ability to skip already provided inline values + var parameters = testMethod.NotNull(nameof(testMethod)).GetParameters(); + var specimens = new List(this.InlineValues); + foreach (var p in parameters.Skip(this.InlineValues.Count)) + { + this.CustomizeFixture(p); + + var specimen = this.Resolve(p); + specimens.Add(specimen); + } + + return new[] { specimens.ToArray() }; + } + + private void CustomizeFixture(ParameterInfo p) + { + var customizeAttributes = p.GetCustomAttributes() + .OfType() + .OrderBy(x => x, new CustomizeAttributeComparer()); + + foreach (var c in customizeAttributes.Select(ca => ca.GetCustomization(p))) + { + this.AdaptedFixture.Customize(c); + } + } + + private object Resolve(ParameterInfo p) + { + var context = new SpecimenContext(this.AdaptedFixture); + return context.Resolve(p); + } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.Core/SpecimenBuilders/IgnoreVirtualMembersSpecimenBuilder.cs b/src/Objectivity.AutoFixture.XUnit2.Core/SpecimenBuilders/IgnoreVirtualMembersSpecimenBuilder.cs index 05036d37..63c28ae0 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core/SpecimenBuilders/IgnoreVirtualMembersSpecimenBuilder.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core/SpecimenBuilders/IgnoreVirtualMembersSpecimenBuilder.cs @@ -1,39 +1,34 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders -{ - using System; - using System.Reflection; - using global::AutoFixture.Kernel; - - public class IgnoreVirtualMembersSpecimenBuilder : ISpecimenBuilder - { - public IgnoreVirtualMembersSpecimenBuilder() - : this(null) - { - } - - public IgnoreVirtualMembersSpecimenBuilder(Type reflectedType) - { - this.ReflectedType = reflectedType; - } - - public Type ReflectedType { get; } - - public object Create(object request, ISpecimenContext context) - { - if (request is PropertyInfo pi) //// is a property - { - if (this.ReflectedType is null //// is hosted anywhere - || - this.ReflectedType == pi.ReflectedType) //// is hosted in defined type - { - if (pi.GetGetMethod().IsVirtual) - { - return new OmitSpecimen(); - } - } - } - - return new NoSpecimen(); - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders +{ + using System; + using System.Reflection; + using global::AutoFixture.Kernel; + + public class IgnoreVirtualMembersSpecimenBuilder : ISpecimenBuilder + { + public IgnoreVirtualMembersSpecimenBuilder() + : this(null) + { + } + + public IgnoreVirtualMembersSpecimenBuilder(Type reflectedType) + { + this.ReflectedType = reflectedType; + } + + public Type ReflectedType { get; } + + public object Create(object request, ISpecimenContext context) + { + if (request is PropertyInfo pi //// is a property + && (this.ReflectedType is null //// is hosted anywhere + || this.ReflectedType == pi.ReflectedType) //// is hosted in defined type + && pi.GetGetMethod().IsVirtual) + { + return new OmitSpecimen(); + } + + return new NoSpecimen(); + } + } +} From 8673b6407cd08582ccb5237aeafaf9bbc3e13641 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Sun, 19 Apr 2026 17:28:32 +0200 Subject: [PATCH 2/4] fix: resolve incomparable-types Equals warning in ValuesRequestTests line 203 Co-Authored-By: Claude Sonnet 4.6 --- .../Requests/ValuesRequestTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs index 5e95b8ad..c878f078 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs @@ -197,7 +197,7 @@ public void GivenDifferentTypeOfValuesRequest_WhenEqualsIsInvoked_ThenFalseIsRet // Arrange var type = value.GetType(); var fixedRequest = new FixedValuesRequest(type, value); - var exceptRequest = new ExceptValuesRequest(type, value); + object exceptRequest = new ExceptValuesRequest(type, value); // Act var result = fixedRequest.Equals(exceptRequest); From bc39cd71044ed4faa4835ccc6ff6197b077684b5 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Sun, 19 Apr 2026 17:37:46 +0200 Subject: [PATCH 3/4] chore(coderabbit): disable docstrings pre-merge check --- .coderabbit.yaml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index bd6c9d5e..e5384192 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,9 +1,3 @@ -# CodeRabbit configuration for AutoFixture.XUnit2.AutoMock -# https://docs.coderabbit.ai/getting-started/configure-coderabbit -# -# One-time prerequisite: install the CodeRabbit GitHub App at the org/repo level. -# https://github.com/apps/coderabbit-ai - language: "en-US" reviews: @@ -16,6 +10,9 @@ reviews: auto_review: enabled: true drafts: false - + pre_merge_checks: + docstrings: + mode: off + chat: auto_reply: true From cd3f7d42eba9af311d381961fc23770edcf9982c Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Sun, 19 Apr 2026 17:42:47 +0200 Subject: [PATCH 4/4] fix(test): No need for `as` cast --- .../Attributes/CustomizeWithAttributeTests.cs | 4 ++-- .../Providers/AutoDataAttributeProviderTests.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs index a3eab524..d735c606 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs @@ -130,7 +130,7 @@ public void GivenCustomizeWithAttributeWithIncludeParameterTypeSet_WhenGetCustom .First(); // Act - var customization = customizeAttribute.GetCustomization(parameter) as ArgumentsDiscoveryCustomization; + var customization = customizeAttribute.GetCustomization(parameter); // Assert var typed = Assert.IsType(customization); @@ -151,7 +151,7 @@ public void GivenCustomizeWithAttributeWithArguments_WhenGetCustomizationIsInvok .First(); // Act - var customization = customizeAttribute.GetCustomization(parameter) as ArgumentsDiscoveryCustomization; + var customization = customizeAttribute.GetCustomization(parameter); // Assert var typed = Assert.IsType(customization); diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs index 79ce945d..7dafc906 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Providers/AutoDataAttributeProviderTests.cs @@ -19,7 +19,7 @@ public void GivenInitializedFixture_WhenGetAttributeIsInvoked_ThenAttributeWithS var provider = new AutoDataAttributeProvider(); // Act - var dataAttribute = provider.GetAttribute(fixture) as AutoDataAdapterAttribute; + var dataAttribute = provider.GetAttribute(fixture); // Assert var typed = Assert.IsType(dataAttribute);