diff --git a/.editorconfig b/.editorconfig index 69ef33e..d627e2a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,16 +1,18 @@ root = true -[*.{cs,fs,fsx}] -indent_size = 4 -indent_style = space - [*.{sln,csproj,fsproj,config,xml,props}] indent_size = 2 indent_style = space [*.cs] +indent_size = 4 +indent_style = space + # Require "this." keyword qualification in code dotnet_style_qualification_for_field = true:suggestion dotnet_style_qualification_for_property = true:suggestion dotnet_style_qualification_for_method = true:suggestion dotnet_style_qualification_for_event = true:suggestion + +dotnet_diagnostic.CA1510.severity = suggestion +dotnet_diagnostic.CA1859.severity = suggestion diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 75d776d..ec3e4c8 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -130,7 +130,7 @@ }, "Solution": { "type": "string", - "description": "Path to a solution file that is automatically loaded" + "description": "Path to a solution file that is automatically loaded. Default is AutoFixture.TUnit.sln" } } }, diff --git a/AutoFixture.Tests.globalconfig b/AutoFixture.TUnit.Tests.globalconfig similarity index 100% rename from AutoFixture.Tests.globalconfig rename to AutoFixture.TUnit.Tests.globalconfig diff --git a/AutoFixture.globalconfig b/AutoFixture.TUnit.globalconfig similarity index 95% rename from AutoFixture.globalconfig rename to AutoFixture.TUnit.globalconfig index 297b7df..3f911a7 100644 --- a/AutoFixture.globalconfig +++ b/AutoFixture.TUnit.globalconfig @@ -26,6 +26,7 @@ dotnet_diagnostic.CA5394.severity = none # CA5394: Do not use insecure randomnes ## StyleCop Analyzers ## +dotnet_diagnostic.SA1101.severity = none # SA1101: Prefix local calls with this. dotnet_diagnostic.SA1116.severity = none # SA1116: The parameters should begin on the line after the declaration, whenever the parameter span across multiple line dotnet_diagnostic.SA1117.severity = none # SA1117: The parameters should all be placed on the same line or each parameter should be placed on its own line. dotnet_diagnostic.SA1118.severity = none # SA1118: The parameter spans multiple lines @@ -35,6 +36,7 @@ dotnet_diagnostic.SA1202.severity = none # SA1202: 'public' members should come dotnet_diagnostic.SA1203.severity = none # SA1203: Constant fields should appear before non-constant fields dotnet_diagnostic.SA1204.severity = none # SA1204: Static members should appear before non-static members dotnet_diagnostic.SA1214.severity = none # SA1214: Readonly fields should appear before non-readonly fields +dotnet_diagnostic.SA1309.severity = none # SA1309: Field names should not begin with an underscore dotnet_diagnostic.SA1413.severity = none # SA1413: Use trailing comma in multi-line initializers dotnet_diagnostic.SA1501.severity = none # SA1501: Statement should not be on a single line dotnet_diagnostic.SA1503.severity = none # SA1503: Braces should not be omitted diff --git a/AutoFixture.TUnit.sln b/AutoFixture.TUnit.sln index 92436f4..1d4c05c 100644 --- a/AutoFixture.TUnit.sln +++ b/AutoFixture.TUnit.sln @@ -17,8 +17,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Common.props = Common.props Common.Test.props = Common.Test.props LICENCE.txt = LICENCE.txt + AutoFixture.TUnit.globalconfig = AutoFixture.TUnit.globalconfig + AutoFixture.TUnit.sln.DotSettings = AutoFixture.TUnit.sln.DotSettings + AutoFixture.TUnit.Tests.globalconfig = AutoFixture.TUnit.Tests.globalconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestTypeFoundation", "tests\TestTypeFoundation\TestTypeFoundation.csproj", "{5F812210-5B61-4CBF-A033-4C33067A5DE9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +46,12 @@ Global {4056BD12-5FA8-4D2E-274A-7E85E858A5FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4056BD12-5FA8-4D2E-274A-7E85E858A5FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {4056BD12-5FA8-4D2E-274A-7E85E858A5FC}.Verify|Any CPU.ActiveCfg = Verify|Any CPU + {5F812210-5B61-4CBF-A033-4C33067A5DE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F812210-5B61-4CBF-A033-4C33067A5DE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F812210-5B61-4CBF-A033-4C33067A5DE9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F812210-5B61-4CBF-A033-4C33067A5DE9}.Release|Any CPU.Build.0 = Release|Any CPU + {5F812210-5B61-4CBF-A033-4C33067A5DE9}.Verify|Any CPU.ActiveCfg = Verify|Any CPU + {5F812210-5B61-4CBF-A033-4C33067A5DE9}.Verify|Any CPU.Build.0 = Verify|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AutoFixture.sln.DotSettings b/AutoFixture.TUnit.sln.DotSettings similarity index 100% rename from AutoFixture.sln.DotSettings rename to AutoFixture.TUnit.sln.DotSettings diff --git a/Common.Test.props b/Common.Test.props index cba4f60..3dd7f43 100644 --- a/Common.Test.props +++ b/Common.Test.props @@ -34,6 +34,6 @@ - + diff --git a/Common.props b/Common.props index 9b95ccc..7b011a9 100644 --- a/Common.props +++ b/Common.props @@ -38,7 +38,7 @@ - + diff --git a/build/.editorconfig b/build/.editorconfig index 31e43dc..8cddf83 100644 --- a/build/.editorconfig +++ b/build/.editorconfig @@ -9,3 +9,5 @@ csharp_style_expression_bodied_methods = true:silent csharp_style_expression_bodied_properties = true:warning csharp_style_expression_bodied_indexers = true:warning csharp_style_expression_bodied_accessors = true:warning + +dotnet_naming_style.field_style.capitalization = pascal_case \ No newline at end of file diff --git a/build/Build.cs b/build/Build.cs index 763f543..e209985 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -20,20 +20,20 @@ "continuous", GitHubActionsImage.WindowsLatest, AutoGenerate = false, - OnPullRequestBranches = new[] { MasterBranch, ReleaseBranch }, + OnPullRequestBranches = [MasterBranch, ReleaseBranch], PublishArtifacts = false, - InvokedTargets = new[] { nameof(Verify), nameof(Cover), nameof(Pack) }, + InvokedTargets = [nameof(Verify), nameof(Cover), nameof(Pack)], EnableGitHubToken = true)] [GitHubActions( "release", GitHubActionsImage.WindowsLatest, AutoGenerate = false, - OnPushTags = new[] { "v*" }, + OnPushTags = ["v*"], PublishArtifacts = true, - InvokedTargets = new[] { nameof(Verify), nameof(Cover), nameof(Publish) }, + InvokedTargets = [nameof(Verify), nameof(Cover), nameof(Publish)], EnableGitHubToken = true, - ImportSecrets = new[] { Secrets.NuGetApiKey })] -partial class Build : NukeBuild + ImportSecrets = [Secrets.NuGetApiKey])] +class Build : NukeBuild { public static int Main() => Execute(x => x.Compile); @@ -43,7 +43,7 @@ partial class Build : NukeBuild [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; - [Solution] readonly Solution Solution; + [Solution("AutoFixture.TUnit.sln")] readonly Solution Solution; [GitRepository] readonly GitRepository GitRepository; [GitVersion] readonly GitVersion GitVersion; [CI] readonly GitHubActions GitHubActions; @@ -51,14 +51,14 @@ partial class Build : NukeBuild [Parameter("GitHub auth token", Name = "github-token"), Secret] readonly string GitHubToken; [Parameter("Forces the continuous integration build flag")] readonly bool CI; - [Secret] [Parameter("NuGet API Key (secret)", Name = Secrets.NuGetApiKey)] readonly string NuGetApiKey; + [Secret][Parameter("NuGet API Key (secret)", Name = Secrets.NuGetApiKey)] readonly string NuGetApiKey; readonly string NuGetSource = "https://api.nuget.org/v3/index.json"; - IEnumerable Excluded => new[] - { + IEnumerable Excluded => + [ Solution.GetProject("_build"), Solution.GetProject("TestTypeFoundation") - }; + ]; IEnumerable TestProjects => Solution.GetAllProjects("*Tests"); IEnumerable Libraries => Solution.Projects.Except(TestProjects).Except(Excluded); @@ -147,7 +147,7 @@ partial class Build : NukeBuild .Executes(() => { ReportGenerator(_ => _ - .SetFramework("net5.0") + .SetFramework("net8.0") .SetAssemblyFilters("-TestTypeFoundation*") .SetReports(TestResultsDirectory / "**" / "coverage.cobertura.xml") .SetTargetDirectory(ReportsDirectory) diff --git a/build/Configuration.cs b/build/Configuration.cs index 88c405b..171ec56 100644 --- a/build/Configuration.cs +++ b/build/Configuration.cs @@ -4,9 +4,9 @@ [TypeConverter(typeof(TypeConverter))] public class Configuration : Enumeration { - public static Configuration Debug = new() { Value = nameof(Debug) }; - public static Configuration Release = new() { Value = nameof(Release) }; - public static Configuration Verify = new() { Value = nameof(Verify) }; + public static readonly Configuration Debug = new() { Value = nameof(Debug) }; + public static readonly Configuration Release = new() { Value = nameof(Release) }; + public static readonly Configuration Verify = new() { Value = nameof(Verify) }; public static implicit operator string(Configuration configuration) { diff --git a/build/GitHubActionsExtensions.cs b/build/GitHubActionsExtensions.cs index 3c811e3..cf74351 100644 --- a/build/GitHubActionsExtensions.cs +++ b/build/GitHubActionsExtensions.cs @@ -1,15 +1,16 @@ using System.Text.RegularExpressions; +using Nuke.Common.CI.GitHubActions; -namespace Nuke.Common.CI.GitHubActions +public static partial class GitHubActionsExtensions { - public static class GitHubActionsExtensions - { - private static readonly Regex SemVerRef = new(@"^refs\/tags\/v(?\d+\.\d+\.\d+)", RegexOptions.Compiled); + private static readonly Regex SemVerRef = GetSemVerRegex(); - public static bool IsOnSemVerTag(this GitHubActions source) - { - return !string.IsNullOrWhiteSpace(source?.Ref) - && SemVerRef.IsMatch(source.Ref); - } + public static bool IsOnSemVerTag(this GitHubActions source) + { + return !string.IsNullOrWhiteSpace(source?.Ref) + && SemVerRef.IsMatch(source.Ref); } + + [GeneratedRegex(@"^refs\/tags\/v(?\d+\.\d+\.\d+)", RegexOptions.Compiled)] + private static partial Regex GetSemVerRegex(); } \ No newline at end of file diff --git a/build/_build.csproj b/build/_build.csproj index 40a4f34..5e49830 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -12,6 +12,7 @@ $(NoWarn);NETSDK1138;NU1902;NU1903 Debug;Release;Verify + false diff --git a/src/AutoFixture.TUnit/ArgumentsAutoDataAttribute.cs b/src/AutoFixture.TUnit/ArgumentsAutoDataAttribute.cs new file mode 100644 index 0000000..efe3543 --- /dev/null +++ b/src/AutoFixture.TUnit/ArgumentsAutoDataAttribute.cs @@ -0,0 +1,52 @@ +using System.Diagnostics.CodeAnalysis; +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit; + +/// +/// Provides a data source for a data theory, with the data coming from inline +/// values combined with auto-generated data specimens generated by AutoFixture. +/// +[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", + Justification = "This attribute is the root of a potential attribute hierarchy.")] +public class ArgumentsAutoDataAttribute : AutoFixtureDataSourceAttribute +{ + /// + /// Initializes a new instance of the class. + /// + /// The data values to pass to the theory. + public ArgumentsAutoDataAttribute(params object?[] values) + : this(() => new Fixture(), values) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The fixture factory. + /// The data values to pass to the theory. + /// + protected ArgumentsAutoDataAttribute(Func fixtureFactory, params object?[]? values) + { + this.FixtureFactory = fixtureFactory ?? throw new ArgumentNullException(nameof(fixtureFactory)); + this.Values = values ?? [null]; + } + + /// + /// Gets the fixture factory. + /// + public Func FixtureFactory { get; } + + /// + /// Gets the data values to pass to the theory. + /// + public object?[] Values { get; } + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + return new AutoDataSource(this.FixtureFactory, new InlineDataSource(this.Values)) + .GenerateDataSources(dataGeneratorMetadata) + .Select(x => x()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/AutoDataAttribute.cs b/src/AutoFixture.TUnit/AutoDataAttribute.cs new file mode 100644 index 0000000..b22d20a --- /dev/null +++ b/src/AutoFixture.TUnit/AutoDataAttribute.cs @@ -0,0 +1,51 @@ +using System.Diagnostics.CodeAnalysis; +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit; + +/// +/// Provides auto-generated data specimens generated by AutoFixture as an extension to +/// xUnit.net's Theory attribute. +/// +[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", + Justification = "This attribute is the root of a potential attribute hierarchy.")] +public class AutoDataAttribute : AutoFixtureDataSourceAttribute +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// This constructor overload initializes the to an instance of + /// . + /// + /// + public AutoDataAttribute() + : this(() => new Fixture()) + { + } + + /// + /// Initializes a new instance of the class + /// with the supplied . Fixture will be created + /// on demand using the provided factory. + /// + /// The fixture factory used to construct the fixture. + protected AutoDataAttribute(Func fixtureFactory) + { + this.FixtureFactory = fixtureFactory ?? throw new ArgumentNullException(nameof(fixtureFactory)); + } + + /// + /// Gets the fixture factory. + /// + public Func FixtureFactory { get; } + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var source = new AutoDataSource(this.FixtureFactory); + + return source.GenerateDataSources(dataGeneratorMetadata).Select(x => x()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/AutoFixtureDataSourceAttribute.cs b/src/AutoFixture.TUnit/AutoFixtureDataSourceAttribute.cs new file mode 100644 index 0000000..b0fa8f7 --- /dev/null +++ b/src/AutoFixture.TUnit/AutoFixtureDataSourceAttribute.cs @@ -0,0 +1,56 @@ +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit; + +/// +/// Base class for data sources that provide AutoFixture test data for TUnit data driven tests. +/// +public abstract class AutoFixtureDataSourceAttribute : NonTypedDataSourceGeneratorAttribute, IDataSource +{ + /// + /// Returns the test data provided by the source. + /// + /// + /// + public abstract IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata); + + /// + public override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata) + { + if (dataGeneratorMetadata is null) + { + throw new ArgumentNullException(nameof(dataGeneratorMetadata)); + } + + return GetTestDataEnumerable(); + + IEnumerable> GetTestDataEnumerable() + { + var parameters = dataGeneratorMetadata.MembersToGenerate; + if (parameters.Length == 0) + { + // If the method has no parameters, a single test run is enough. + yield return () => []; + yield break; + } + + var enumerable = this.GetData(dataGeneratorMetadata) + ?? throw new InvalidOperationException("The source member yielded no test data."); + + foreach (var testData in enumerable) + { + if (testData is null) + { + throw new InvalidOperationException("The source member yielded a null test data."); + } + + if (testData.Length > parameters.Length) + { + throw new InvalidOperationException("The number of arguments provided exceeds the number of parameters."); + } + + yield return () => testData; + } + } + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/AutoFixtureLogo200x200.png b/src/AutoFixture.TUnit/AutoFixtureLogo200x200.png new file mode 100644 index 0000000..32a82c5 Binary files /dev/null and b/src/AutoFixture.TUnit/AutoFixtureLogo200x200.png differ diff --git a/src/AutoFixture.TUnit/Class1.cs b/src/AutoFixture.TUnit/Class1.cs deleted file mode 100644 index f2d7cbe..0000000 --- a/src/AutoFixture.TUnit/Class1.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace AutoFixture.TUnit; - -/// -/// This is a placeholder class for the AutoFixture.TUnit library. -/// -public class Class1 -{ - /// - /// Initializes a new instance of the class. - /// - public Class1() - { - } -} diff --git a/src/AutoFixture.TUnit/ClassAutoDataAttribute.cs b/src/AutoFixture.TUnit/ClassAutoDataAttribute.cs new file mode 100644 index 0000000..b9f367e --- /dev/null +++ b/src/AutoFixture.TUnit/ClassAutoDataAttribute.cs @@ -0,0 +1,93 @@ +using System.Diagnostics.CodeAnalysis; +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit; + +/// +/// Provides a data source for a data theory, with the data coming from a class +/// which must implement IEnumerable<object?[]>, +/// combined with auto-generated data specimens generated by AutoFixture. +/// +[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", + Justification = "This attribute is the root of a potential attribute hierarchy.")] +public class ClassAutoDataAttribute : AutoFixtureDataSourceAttribute +{ + /// + /// Initializes a new instance of the class. + /// + /// The type of the class that provides the data. + /// The parameters passed to the data provider class constructor. + public ClassAutoDataAttribute(Type sourceType, params object?[] parameters) + : this(() => new Fixture(), sourceType, parameters) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The fixture factory that provides missing data from . + /// The type of the class that provides the data. + /// The parameters passed to the data provider class constructor. + /// + /// This constructor overload exists to enable a derived attribute to + /// supply a custom fixture factory that again may contain custom behavior. + /// + /// + /// In the following example MyTestData is a class that provides test data, + /// that would be complicated or probably impossible to provide using other options. + /// The missing arguments for the test are being supplied from the Fixture instance. + /// + /// [Test] + /// [ClassAutoData(typeof(MyTestData))] + /// public void ClassDataSuppliesExtraValues(int sum, int[] numbers, Person client) + /// { + /// var actual = numbers.Sum(x => x); + /// + /// Assert.Equal(sum, actual); + /// Assert.NotNull(client); + /// } + /// + /// private class MyTestData : IEnumerable<object?[]> + /// { + /// public IEnumerator<object?[]> GetEnumerator() + /// { + /// yield return new object?[] { 0, new int[0] }; + /// yield return new object?[] { 4, new int[] { 1, 2, 1} }; + /// yield return new object?[] { 23, new int [] { 0, 13, 15, -5 } }; + /// } + /// + /// IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + /// } + /// + /// + protected ClassAutoDataAttribute(Func fixtureFactory, Type sourceType, params object?[] parameters) + { + this.FixtureFactory = fixtureFactory ?? throw new ArgumentNullException(nameof(fixtureFactory)); + this.SourceType = sourceType ?? throw new ArgumentNullException(nameof(sourceType)); + this.Parameters = parameters ?? [null]; + } + + /// + /// Gets the fixture factory that provides the missing data from . + /// + public Func FixtureFactory { get; } + + /// + /// Gets the type of the class that provides the data. + /// + public Type SourceType { get; } + + /// + /// Gets the constructor parameters for . + /// + public object?[] Parameters { get; } + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var source = new AutoDataSource(this.FixtureFactory, + new ClassDataSource(this.SourceType, this.Parameters)); + + return source.GenerateDataSources(dataGeneratorMetadata).Select(x => x()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/CompositeDataAttribute.cs b/src/AutoFixture.TUnit/CompositeDataAttribute.cs new file mode 100644 index 0000000..c9128c0 --- /dev/null +++ b/src/AutoFixture.TUnit/CompositeDataAttribute.cs @@ -0,0 +1,57 @@ +using System.Diagnostics.CodeAnalysis; +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit; + +/// +/// An implementation of DataAttribute that composes other DataAttribute instances. +/// +[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", + Justification = "This attribute is the root of a potential attribute hierarchy.")] +public class CompositeDataAttribute : AutoFixtureDataSourceAttribute +{ + private readonly AutoFixtureDataSourceAttribute[] attributes; + + /// + /// Initializes a new instance of the class. + /// + /// The attributes representing a data source for a data theory. + public CompositeDataAttribute(IEnumerable attributes) + : this(attributes as AutoFixtureDataSourceAttribute[] ?? attributes.ToArray()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The attributes representing a data source for a data theory. + public CompositeDataAttribute(params AutoFixtureDataSourceAttribute[] attributes) + { + this.attributes = attributes ?? throw new ArgumentNullException(nameof(attributes)); + } + + /// + /// Gets the attributes supplied through one of the constructors. + /// + public IReadOnlyList Attributes => Array.AsReadOnly(this.attributes); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + if (dataGeneratorMetadata is null) + { + throw new ArgumentNullException(nameof(dataGeneratorMetadata)); + } + + var results = this.attributes + .Select(attr => attr.GenerateDataSources(dataGeneratorMetadata)) + .ToArray(); + + var theoryRows = results + .Select(x => x.Select(y => y())) + .Zip(dataSets => dataSets.Collapse().ToArray()) + .ToArray(); + + return theoryRows; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/CustomizeAttribute.cs b/src/AutoFixture.TUnit/CustomizeAttribute.cs new file mode 100644 index 0000000..450faa3 --- /dev/null +++ b/src/AutoFixture.TUnit/CustomizeAttribute.cs @@ -0,0 +1,18 @@ +using System.Reflection; + +namespace AutoFixture.TUnit; + +/// +/// Base class for customizing parameters in methods decorated with +/// . +/// +[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] +public abstract class CustomizeAttribute : Attribute, IParameterCustomizationSource +{ + /// + /// Gets a customization for a parameter. + /// + /// The parameter for which the customization is requested. + /// A customization for the parameter. + public abstract ICustomization GetCustomization(ParameterInfo parameter); +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/FavorArraysAttribute.cs b/src/AutoFixture.TUnit/FavorArraysAttribute.cs new file mode 100644 index 0000000..8038009 --- /dev/null +++ b/src/AutoFixture.TUnit/FavorArraysAttribute.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit; + +/// +/// An attribute that can be applied to parameters in an -driven +/// Theory to indicate that the parameter value should be created using a constructor with one +/// or more array arguments, if applicable. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public sealed class FavorArraysAttribute : CustomizeAttribute +{ + /// + /// Gets a customization that associates a with + /// the of the parameter. + /// + /// The parameter for which the customization is requested. + /// + /// A customization that associates a with the + /// of the parameter. + /// + public override ICustomization GetCustomization(ParameterInfo parameter) + { + if (parameter is null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + return new ConstructorCustomization(parameter.ParameterType, new ArrayFavoringConstructorQuery()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/FavorEnumerablesAttribute.cs b/src/AutoFixture.TUnit/FavorEnumerablesAttribute.cs new file mode 100644 index 0000000..6564e47 --- /dev/null +++ b/src/AutoFixture.TUnit/FavorEnumerablesAttribute.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit; + +/// +/// An attribute that can be applied to parameters in an -driven +/// Theory to indicate that the parameter value should be created using a constructor with one +/// or more arguments, if applicable. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public sealed class FavorEnumerablesAttribute : CustomizeAttribute +{ + /// + /// Gets a customization that associates a + /// with the of the parameter. + /// + /// The parameter for which the customization is requested. + /// + /// A customization that associates a with + /// the of the parameter. + /// + public override ICustomization GetCustomization(ParameterInfo parameter) + { + if (parameter == null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + return new ConstructorCustomization(parameter.ParameterType, new EnumerableFavoringConstructorQuery()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/FavorListsAttribute.cs b/src/AutoFixture.TUnit/FavorListsAttribute.cs new file mode 100644 index 0000000..3a9399f --- /dev/null +++ b/src/AutoFixture.TUnit/FavorListsAttribute.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit; + +/// +/// An attribute that can be applied to parameters in an -driven +/// Theory to indicate that the parameter value should be created using a constructor with one +/// or more arguments, if applicable. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public sealed class FavorListsAttribute : CustomizeAttribute +{ + /// + /// Gets a customization that associates a with + /// the of the parameter. + /// + /// The parameter for which the customization is requested. + /// + /// A customization that associates a with the + /// of the parameter. + /// + public override ICustomization GetCustomization(ParameterInfo parameter) + { + if (parameter == null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + return new ConstructorCustomization(parameter.ParameterType, new ListFavoringConstructorQuery()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/FrozenAttribute.cs b/src/AutoFixture.TUnit/FrozenAttribute.cs new file mode 100644 index 0000000..4b50c8f --- /dev/null +++ b/src/AutoFixture.TUnit/FrozenAttribute.cs @@ -0,0 +1,72 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit; + +/// +/// An attribute that can be applied to parameters in an -driven +/// Theory to indicate that the parameter value should be frozen so that the same instance is +/// returned every time the creates an instance of that type. +/// +[AttributeUsage(AttributeTargets.Parameter)] +[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", + Justification = "This attribute is the root of a potential attribute hierarchy.")] +public class FrozenAttribute : CustomizeAttribute +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// The criteria used to determine + /// which requests will be satisfied by the frozen parameter value + /// is . + /// + public FrozenAttribute() + : this(Matching.ExactType) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The criteria used to determine + /// which requests will be satisfied by the frozen parameter value. + /// + public FrozenAttribute(Matching by) + { + this.By = by; + } + + /// + /// Gets the criteria used to determine + /// which requests will be satisfied by the frozen parameter value. + /// + public Matching By { get; } + + /// + /// Gets a configured + /// to match requests based on the and optionally + /// the name of the parameter. + /// + /// + /// The parameter for which the customization is requested. + /// + /// + /// A configured + /// to match requests based on the and optionally + /// the name of the parameter. + /// + public override ICustomization GetCustomization(ParameterInfo parameter) + { + if (parameter is null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + var matcher = new ParameterMatcherBuilder(parameter).SetFlags(this.By).Build(); + + return new FreezeOnMatchCustomization(parameter, matcher); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/GreedyAttribute.cs b/src/AutoFixture.TUnit/GreedyAttribute.cs new file mode 100644 index 0000000..876420c --- /dev/null +++ b/src/AutoFixture.TUnit/GreedyAttribute.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit; + +/// +/// An attribute that can be applied to parameters in an -driven +/// Theory to indicate that the parameter value should be created using the most greedy +/// constructor that can be satisfied by an . +/// +[AttributeUsage(AttributeTargets.Parameter)] +public sealed class GreedyAttribute : CustomizeAttribute +{ + /// + /// Gets a customization that associates a with the + /// of the parameter. + /// + /// The parameter for which the customization is requested. + /// + /// A customization that associates a with the + /// of the parameter. + /// + public override ICustomization GetCustomization(ParameterInfo parameter) + { + if (parameter is null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + return new ConstructorCustomization(parameter.ParameterType, new GreedyConstructorQuery()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/Argument.cs b/src/AutoFixture.TUnit/Internal/Argument.cs new file mode 100644 index 0000000..6cf7fd7 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/Argument.cs @@ -0,0 +1,16 @@ +namespace AutoFixture.TUnit.Internal; + +internal sealed class Argument +{ + public Argument(TestParameter parameter, object? value) + { + this.Parameter = parameter ?? throw new ArgumentNullException(nameof(parameter)); + this.Value = value; + } + + public TestParameter Parameter { get; } + + public object? Value { get; } + + public ICustomization GetCustomization() => this.Parameter.GetCustomization(this.Value); +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/AutoDataSource.cs b/src/AutoFixture.TUnit/Internal/AutoDataSource.cs new file mode 100644 index 0000000..c853b6f --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/AutoDataSource.cs @@ -0,0 +1,90 @@ +namespace AutoFixture.TUnit.Internal; + +/// +/// Combines the values from a source with auto-generated values. +/// +public class AutoDataSource : DataSource +{ + /// + /// Initializes a new instance of the class. + /// + /// The factory method for creating a fixture. + /// The source of test data to combine with auto-generated values. + /// + /// Thrown when is . + /// + public AutoDataSource(Func createFixture, IDataSource? source = default) + { + this.CreateFixture = createFixture ?? throw new ArgumentNullException(nameof(createFixture)); + this.Source = source; + } + + /// + /// Gets the factory method for creating a fixture. + /// + public Func CreateFixture { get; } + + /// + /// Gets the source of test data to combine with auto-generated values. + /// + public IDataSource? Source { get; } + + /// + /// Returns the combined test data provided by the source and auto-generated values. + /// + /// The target method for which to provide the arguments. + /// Returns a sequence of argument collections. + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + return this.Source is null + ? this.GenerateValues(dataGeneratorMetadata) + : this.CombineValues(dataGeneratorMetadata, this.Source); + } + + private IEnumerable GenerateValues(DataGeneratorMetadata metadata) + { + var parameters = Array.ConvertAll(metadata.GetMethod().GetParameters(), TestParameter.From); + var fixture = this.CreateFixture(); + yield return Array.ConvertAll(parameters, parameter => GenerateAutoValue(parameter, fixture)); + } + + private IEnumerable CombineValues(DataGeneratorMetadata metadata, IDataSource source) + { + var method = metadata.GetMethod(); + + var parameters = Array.ConvertAll(method.GetParameters(), TestParameter.From); + + foreach (var testData in source.GetData(metadata)) + { + var customizations = parameters.Take(testData!.Length) + .Zip(testData, (parameter, value) => new Argument(parameter, value)) + .Select(argument => argument.GetCustomization()) + .Where(x => x is not NullCustomization); + + var fixture = this.CreateFixture(); + + foreach (var customization in customizations) + { + fixture.Customize(customization); + } + + var missingValues = parameters.Skip(testData.Length) + .Select(parameter => GenerateAutoValue(parameter, fixture)) + .ToArray(); + + yield return testData.Concat(missingValues).ToArray(); + } + } + + private static object GenerateAutoValue(TestParameter parameter, IFixture fixture) + { + var customization = parameter.GetCustomization(); + + if (customization is not NullCustomization) + { + fixture.Customize(customization); + } + + return fixture.Resolve(parameter.ParameterInfo); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/ClassDataSource.cs b/src/AutoFixture.TUnit/Internal/ClassDataSource.cs new file mode 100644 index 0000000..60c670d --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/ClassDataSource.cs @@ -0,0 +1,48 @@ +using System.Diagnostics.CodeAnalysis; + +namespace AutoFixture.TUnit.Internal; + +/// +/// Encapsulates the access to a test data source type. +/// +[SuppressMessage("Design", "CA1010:Generic interface should also be implemented", + Justification = "Type is not a collection.")] +public class ClassDataSource : DataSource +{ + private readonly object?[] parameters; + + /// + /// Creates an instance of type . + /// + /// The test data source type. + /// Constructor arguments for the source type. + /// Thrown when arguments are . + public ClassDataSource(Type type, params object?[] parameters) + { + this.Type = type ?? throw new ArgumentNullException(nameof(type)); + this.parameters = parameters ?? throw new ArgumentNullException(nameof(parameters)); + } + + /// + /// Gets the test data source type. + /// + public Type Type { get; } + + /// + /// Gets the constructor parameters for test data source type. + /// + public IReadOnlyList Parameters => Array.AsReadOnly(this.parameters); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var instance = Activator.CreateInstance(type: this.Type, args: this.parameters); + + if (instance is not IEnumerable enumerable) + { + throw new InvalidOperationException($"Data source type \"{this.Type}\" should implement the \"{typeof(IEnumerable)}\" interface."); + } + + return enumerable; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/CustomizationExtensions.cs b/src/AutoFixture.TUnit/Internal/CustomizationExtensions.cs new file mode 100644 index 0000000..b447722 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/CustomizationExtensions.cs @@ -0,0 +1,9 @@ +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Internal; + +internal static class CustomizationExtensions +{ + public static object Resolve(this IFixture source, object request) + => new SpecimenContext(source).Resolve(request); +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/CustomizeAttributeComparer.cs b/src/AutoFixture.TUnit/Internal/CustomizeAttributeComparer.cs new file mode 100644 index 0000000..0303179 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/CustomizeAttributeComparer.cs @@ -0,0 +1,14 @@ +namespace AutoFixture.TUnit.Internal; + +internal sealed class CustomizeAttributeComparer : Comparer +{ + public override int Compare(IParameterCustomizationSource? x, IParameterCustomizationSource? y) + { + return (x is FrozenAttribute, y is FrozenAttribute) switch + { + (true, false) => 1, + (false, true) => -1, + _ => 0 + }; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/DataGeneratorMetadataExtensions.cs b/src/AutoFixture.TUnit/Internal/DataGeneratorMetadataExtensions.cs new file mode 100644 index 0000000..e56c9eb --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/DataGeneratorMetadataExtensions.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using TUnit.Core.Enums; + +namespace AutoFixture.TUnit.Internal; + +internal static class DataGeneratorMetadataExtensions +{ + public static MethodBase GetMethod(this DataGeneratorMetadata dataGeneratorMetadata) + { + if (dataGeneratorMetadata.Type == DataGeneratorType.ClassParameters) + { + return dataGeneratorMetadata.TestClassType.GetConstructors().First(); + } + + return dataGeneratorMetadata.TestInformation.ReflectionInformation; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/DataSource.cs b/src/AutoFixture.TUnit/Internal/DataSource.cs new file mode 100644 index 0000000..5d9560c --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/DataSource.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + +namespace AutoFixture.TUnit.Internal; + +/// +/// The base class for test case sources. +/// +[SuppressMessage("Design", "CA1010:Generic interface should also be implemented", + Justification = "The type is not a collection.")] +[SuppressMessage("Naming", "CA1710:Identifiers should have correct suffix", + Justification = "The type is not a collection.")] +public abstract class DataSource : AutoFixtureDataSourceAttribute, IDataSource; \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/EnumerableExtensions.cs b/src/AutoFixture.TUnit/Internal/EnumerableExtensions.cs new file mode 100644 index 0000000..f05c768 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/EnumerableExtensions.cs @@ -0,0 +1,43 @@ +namespace AutoFixture.TUnit.Internal; + +internal static class EnumerableExtensions +{ + /// + /// Applies a specified function to the corresponding elements of any number of sequences. + /// + /// The type of the elements of the input sequences. + /// The type of the elements of the result sequence. + /// The input sequences. + /// A function that specifies how to combine the corresponding elements of the sequences. + /// An that contains elements of the input sequences, combined by resultSelector. + internal static IEnumerable Zip(this IEnumerable> sequences, + Func, TResult> resultSelector) + { + var enumerators = sequences.Select(s => s.GetEnumerator()).ToList(); + while (enumerators.TrueForAll(e => e.MoveNext())) + { + yield return resultSelector(enumerators.Select(e => e.Current)); + } + } + + /// + /// Collapses a series of sequences down by using items from the first sequence until it finishes, + /// then continuing from the same index through the second sequence, and so on until all sequences + /// have been exhausted. + /// + /// The type of the elements of the input sequences. + /// The input sequences. + /// Items from each sequence in turn, yielding those from the first sequence first. + internal static IEnumerable Collapse(this IEnumerable> sequences) + { + var position = 0; + foreach (var sequence in sequences) + { + foreach (var item in sequence.Skip(position)) + { + position++; + yield return item; + } + } + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/FieldDataSource.cs b/src/AutoFixture.TUnit/Internal/FieldDataSource.cs new file mode 100644 index 0000000..708d86c --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/FieldDataSource.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace AutoFixture.TUnit.Internal; + +/// +/// Encapsulates access to a field that provides test data. +/// +[SuppressMessage("Design", "CA1010:Generic interface should also be implemented", + Justification = "Type is not a collection.")] +public class FieldDataSource : DataSource +{ + /// + /// Creates an instance of type . + /// + /// The source field info. + /// + /// Thrown when is . + /// + public FieldDataSource(FieldInfo fieldInfo) + { + this.FieldInfo = fieldInfo ?? throw new ArgumentNullException(nameof(fieldInfo)); + } + + /// + /// Gets the source field. + /// + public FieldInfo FieldInfo { get; } + + /// + /// Gets the test data from the source field. + /// + /// Returns a sequence of argument collections. + /// + /// Thrown when the field does not return an enumerable value. + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var value = this.FieldInfo.GetValue(null); + if (value is not IEnumerable enumerable) + { + throw new InvalidCastException("Member does not return an enumerable value."); + } + + return enumerable; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/FrozenValueCustomization.cs b/src/AutoFixture.TUnit/Internal/FrozenValueCustomization.cs new file mode 100644 index 0000000..4717352 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/FrozenValueCustomization.cs @@ -0,0 +1,24 @@ +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Internal; + +internal sealed class FrozenValueCustomization : ICustomization +{ + private readonly IRequestSpecification specification; + private readonly object? value; + + public FrozenValueCustomization(IRequestSpecification specification, object? value) + { + this.value = value; + this.specification = specification ?? throw new ArgumentNullException(nameof(specification)); + } + + public void Customize(IFixture fixture) + { + var builder = new FilteringSpecimenBuilder( + builder: new FixedBuilder(this.value), + specification: this.specification); + + fixture.Customizations.Insert(0, builder); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/IDataSource.cs b/src/AutoFixture.TUnit/Internal/IDataSource.cs new file mode 100644 index 0000000..7fa5279 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/IDataSource.cs @@ -0,0 +1,14 @@ +namespace AutoFixture.TUnit.Internal; + +/// +/// Exposes the factory method for a sequence of test data. +/// +public interface IDataSource +{ + /// + /// Returns the test data provided by the source. + /// + /// The target method for which to provide the arguments. + /// Returns a sequence of argument collections. + IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata); +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/InlineDataSource.cs b/src/AutoFixture.TUnit/Internal/InlineDataSource.cs new file mode 100644 index 0000000..cee650e --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/InlineDataSource.cs @@ -0,0 +1,48 @@ +using System.Diagnostics.CodeAnalysis; + +namespace AutoFixture.TUnit.Internal; + +/// +/// Provides test data from a predefined collection of values. +/// +[SuppressMessage("Design", "CA1010:Generic interface should also be implemented", + Justification = "Type is not a collection.")] +public sealed class InlineDataSource : AutoFixtureDataSourceAttribute +{ + private readonly object?[] values; + + /// + /// Creates an instance of type . + /// + /// The collection of inline values. + /// + /// Thrown when the values collection is . + /// + public InlineDataSource(object?[] values) + { + this.values = values ?? throw new ArgumentNullException(nameof(values)); + } + + /// + /// The collection of inline values. + /// + public IReadOnlyList Values => Array.AsReadOnly(this.values); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + if (dataGeneratorMetadata is null) + { + throw new ArgumentNullException(nameof(dataGeneratorMetadata)); + } + + var membersToGenerate = dataGeneratorMetadata.MembersToGenerate; + if (this.values.Length > membersToGenerate.Length) + { + throw new InvalidOperationException( + "The number of arguments provided exceeds the number of parameters."); + } + + yield return this.values; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/MemberDataSource.cs b/src/AutoFixture.TUnit/Internal/MemberDataSource.cs new file mode 100644 index 0000000..a97d2ff --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/MemberDataSource.cs @@ -0,0 +1,86 @@ +using System.Globalization; +using System.Reflection; + +namespace AutoFixture.TUnit.Internal; + +/// +/// Encapsulates access to a member that provides test data. +/// +public class MemberDataSource : IDataSource +{ + private readonly object?[] arguments; + + /// + /// Creates an instance of type . + /// + /// The containing type of the member. + /// The name of the member. + /// The arguments provided to the member. + /// Thrown when arguments are . + public MemberDataSource(Type type, string name, params object?[] arguments) + { + this.Type = type ?? throw new ArgumentNullException(nameof(type)); + this.Name = name ?? throw new ArgumentNullException(nameof(name)); + this.arguments = arguments ?? throw new ArgumentNullException(nameof(arguments)); + this.Source = this.GetTestDataSource(); + } + + /// + /// Gets the containing type of the member. + /// + public Type Type { get; } + + /// + /// Gets the name of the member. + /// + public string Name { get; } + + /// + /// Gets the arguments provided to the member. + /// + public IReadOnlyList Arguments => Array.AsReadOnly(this.arguments); + + /// + /// Gets the test data source. + /// + protected DataSource Source { get; } + + private DataSource GetTestDataSource() + { + var sourceMember = this.Type.GetMember(this.Name, + MemberTypes.Method | MemberTypes.Field | MemberTypes.Property, + BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy) + .FirstOrDefault(); + + if (sourceMember is null) + { + var message = string.Format( + CultureInfo.CurrentCulture, + "Could not find public static member (property, field, or method) named '{0}' on {1}", this.Name, this.Type.FullName); + throw new ArgumentException(message); + } + + var returnType = sourceMember.GetReturnType(); + if (!typeof(IEnumerable).IsAssignableFrom(returnType)) + { + var message = string.Format( + CultureInfo.CurrentCulture, + "Member {0} on {1} does not return IEnumerable", this.Name, this.Type.FullName); + throw new ArgumentException(message); + } + + return sourceMember switch + { + FieldInfo fieldInfo => new FieldDataSource(fieldInfo), + PropertyInfo propertyInfo => new PropertyDataSource(propertyInfo), + MethodInfo methodInfo => new MethodDataSource(methodInfo, this.arguments), + _ => throw new InvalidOperationException("Unsupported member type.") + }; + } + + /// + public IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + return this.Source.GenerateDataSources(dataGeneratorMetadata).Select(x => x()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/MethodDataSource.cs b/src/AutoFixture.TUnit/Internal/MethodDataSource.cs new file mode 100644 index 0000000..f896b64 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/MethodDataSource.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace AutoFixture.TUnit.Internal; + +/// +/// Encapsulates access to a method that provides test data. +/// +[SuppressMessage("Design", "CA1010:Generic interface should also be implemented", + Justification = "Type is not a collection.")] +public class MethodDataSource : DataSource +{ + private readonly object?[] arguments; + + /// + /// Creates an instance of type . + /// + /// The source method. + /// The source method arguments. + public MethodDataSource(MethodInfo methodInfo, params object?[] arguments) + { + this.MethodInfo = methodInfo ?? throw new ArgumentNullException(nameof(methodInfo)); + this.arguments = arguments ?? throw new ArgumentNullException(nameof(arguments)); + } + + /// + /// Gets the source method info. + /// + public MethodInfo MethodInfo { get; } + + /// + /// Gets the source method arguments. + /// + public IReadOnlyList Arguments => Array.AsReadOnly(this.arguments); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var value = this.MethodInfo.Invoke(null, this.arguments); + if (value is not IEnumerable enumerable) + { + throw new InvalidCastException("Member does not return an enumerable value."); + } + + return enumerable; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/NullCustomization.cs b/src/AutoFixture.TUnit/Internal/NullCustomization.cs new file mode 100644 index 0000000..18b2b09 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/NullCustomization.cs @@ -0,0 +1,19 @@ +namespace AutoFixture.TUnit.Internal; + +internal sealed class NullCustomization : ICustomization +{ + private NullCustomization() + { + // prevent external instantiation + } + + private static readonly Lazy LazyInstance = new( + () => new NullCustomization(), isThreadSafe: true); + + public static NullCustomization Instance => LazyInstance.Value; + + public void Customize(IFixture fixture) + { + // intentionally left blank + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/ParameterFilter.cs b/src/AutoFixture.TUnit/Internal/ParameterFilter.cs new file mode 100644 index 0000000..1a15d59 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/ParameterFilter.cs @@ -0,0 +1,42 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Internal; + +/// +/// Specification for identifying requests that match the test parameter by provided criteria. +/// +internal sealed class ParameterFilter : IRequestSpecification +{ + private readonly IRequestSpecification matcherSpecification; + + /// + /// Creates an instance of type . + /// + /// The parameter. + /// The flags specifying the matching criteria. + /// Thrown when is null. + public ParameterFilter(ParameterInfo parameterInfo, Matching flags) + { + this.ParameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo)); + this.Flags = flags; + this.matcherSpecification = new ParameterMatcherBuilder(this.ParameterInfo) + .SetFlags(this.Flags).Build(); + } + + /// + /// Gets the source parameter. + /// + public ParameterInfo ParameterInfo { get; } + + /// + /// Gets the matching flags. + /// + public Matching Flags { get; } + + /// + public bool IsSatisfiedBy(object request) + { + return this.matcherSpecification.IsSatisfiedBy(request); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/ParameterMatcherBuilder.cs b/src/AutoFixture.TUnit/Internal/ParameterMatcherBuilder.cs new file mode 100644 index 0000000..faf4e02 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/ParameterMatcherBuilder.cs @@ -0,0 +1,198 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Internal; + +/// +/// A builder type that creates a instance, +/// for a instance, based on the builder's matching configuration. +/// +public class ParameterMatcherBuilder +{ + private readonly ParameterInfo parameterInfo; + + /// + /// Creates an instance of type . + /// + /// The parameter info. + /// + /// Thrown when is . + /// + public ParameterMatcherBuilder(ParameterInfo parameterInfo) + { + this.parameterInfo = parameterInfo + ?? throw new ArgumentNullException(nameof(parameterInfo)); + } + + /// + /// Gets or sets a value indicating whether the exact parameter request should be matched. + /// Default is . + /// + public bool MatchExactRequest { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the exact parameter type should be matched. + /// + public bool MatchExactType { get; set; } + + /// + /// Gets or sets a value indicating whether the direct base type should be matched. + /// + public bool MatchDirectBaseType { get; set; } + + /// + /// Gets or sets a value indicating whether the interfaces implemented + /// by the parameter type should be matched. + /// + public bool MatchImplementedInterfaces { get; set; } + + /// + /// Gets or sets a value indicating whether the parameter type and name should be matched. + /// The name comparison is case-insensitive. + /// + public bool MatchParameter { get; set; } + + /// + /// Gets or sets a value indicating whether the property type and name should be matched. + /// The name comparison is case-insensitive. + /// + public bool MatchProperty { get; set; } + + /// + /// Gets or sets a value indicating whether the field type and name should be matched. + /// The name comparison is case-insensitive. + /// + public bool MatchField { get; set; } + + /// + /// Sets the matching flags. + /// + /// The matching flags. + /// The current instance. + public ParameterMatcherBuilder SetFlags(Matching flags) + { + this.MatchExactType = flags.HasFlag(Matching.ExactType); + this.MatchDirectBaseType = flags.HasFlag(Matching.DirectBaseType); + this.MatchImplementedInterfaces = flags.HasFlag(Matching.ImplementedInterfaces); + this.MatchParameter = flags.HasFlag(Matching.ParameterName); + this.MatchProperty = flags.HasFlag(Matching.PropertyName); + this.MatchField = flags.HasFlag(Matching.FieldName); + return this; + } + + /// + /// Builds the instance. + /// + /// + /// A new instance of , matching the configuration. + /// + public IRequestSpecification Build() + { + var specifications = new List(7); + if (this.MatchExactRequest) + { + specifications.Add(this.AsExactRequest()); + } + + if (this.MatchExactType) + { + specifications.Add(this.AsExactType()); + } + + if (this.MatchDirectBaseType) + { + specifications.Add(this.AsDirectBaseType()); + } + + if (this.MatchImplementedInterfaces) + { + specifications.Add(this.AsImplementedInterfaces()); + } + + if (this.MatchProperty) + { + specifications.Add(this.AsProperty()); + } + + if (this.MatchParameter) + { + specifications.Add(this.AsParameter()); + } + + if (this.MatchField) + { + specifications.Add(this.AsField()); + } + + return specifications.Count == 1 + ? specifications[0] + : new OrRequestSpecification(specifications); + } + + private IRequestSpecification AsExactRequest() + { + return new EqualRequestSpecification(this.parameterInfo); + } + + private IRequestSpecification AsExactType() + { + return new OrRequestSpecification( + new ExactTypeSpecification(this.parameterInfo.ParameterType), + new SeedRequestSpecification(this.parameterInfo.ParameterType)); + } + + private IRequestSpecification AsDirectBaseType() + { + return new AndRequestSpecification( + new InverseRequestSpecification( + new ExactTypeSpecification(this.parameterInfo.ParameterType)), + new DirectBaseTypeSpecification(this.parameterInfo.ParameterType)); + } + + private IRequestSpecification AsImplementedInterfaces() + { + return new AndRequestSpecification( + new InverseRequestSpecification( + new ExactTypeSpecification(this.parameterInfo.ParameterType)), + new ImplementedInterfaceSpecification(this.parameterInfo.ParameterType)); + } + + private IRequestSpecification AsParameter() + { + return new ParameterSpecification( + new ParameterTypeAndNameCriterion( + new Criterion(this.parameterInfo.ParameterType, new DerivesFromTypeComparer()), + new Criterion(this.parameterInfo.Name!, StringComparer.OrdinalIgnoreCase))); + } + + private IRequestSpecification AsProperty() + { + return new PropertySpecification( + new PropertyTypeAndNameCriterion( + new Criterion(this.parameterInfo.ParameterType, new DerivesFromTypeComparer()), + new Criterion(this.parameterInfo.Name!, StringComparer.OrdinalIgnoreCase))); + } + + private IRequestSpecification AsField() + { + return new FieldSpecification( + new FieldTypeAndNameCriterion( + new Criterion(this.parameterInfo.ParameterType, new DerivesFromTypeComparer()), + new Criterion(this.parameterInfo.Name!, StringComparer.OrdinalIgnoreCase))); + } + + private sealed class DerivesFromTypeComparer : IEqualityComparer + { + public bool Equals(Type? x, Type? y) + { + return y switch + { + null when x is null => true, + null => false, + _ => y.GetTypeInfo().IsAssignableFrom(x) + }; + } + + public int GetHashCode(Type obj) => 0; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/PropertyDataSource.cs b/src/AutoFixture.TUnit/Internal/PropertyDataSource.cs new file mode 100644 index 0000000..fad40b8 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/PropertyDataSource.cs @@ -0,0 +1,40 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace AutoFixture.TUnit.Internal; + +/// +/// Encapsulates access to a property that provides test data. +/// +[SuppressMessage("Design", "CA1010:Generic interface should also be implemented", + Justification = "Type is not a collection.")] +public class PropertyDataSource : DataSource +{ + /// + /// Creates an instance of type . + /// + /// + /// + public PropertyDataSource(PropertyInfo propertyInfo) + { + this.PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo)); + } + + /// + /// Gets the source property. + /// + public PropertyInfo PropertyInfo { get; } + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var value = this.PropertyInfo.GetValue(null); + + if (value is not IEnumerable enumerable) + { + throw new InvalidCastException("Member does not return an enumerable value."); + } + + return enumerable; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/ReflectionExtensions.cs b/src/AutoFixture.TUnit/Internal/ReflectionExtensions.cs new file mode 100644 index 0000000..213b173 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/ReflectionExtensions.cs @@ -0,0 +1,22 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Internal; + +internal static class ReflectionExtensions +{ + public static Type GetReturnType(this MemberInfo member) + { + if (member is null) + { + throw new ArgumentNullException(nameof(member)); + } + + return member switch + { + MethodInfo methodInfo => methodInfo.ReturnType, + PropertyInfo propertyInfo => propertyInfo.PropertyType, + FieldInfo fieldInfo => fieldInfo.FieldType, + _ => throw new ArgumentException("Member is not a method, property, or field.", nameof(member)) + }; + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/TestParameter.cs b/src/AutoFixture.TUnit/Internal/TestParameter.cs new file mode 100644 index 0000000..0509b0b --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/TestParameter.cs @@ -0,0 +1,55 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Internal; + +internal sealed class TestParameter +{ + private readonly Lazy lazyCustomization; + private readonly Lazy lazyFrozenAttribute; + + public TestParameter(ParameterInfo parameterInfo) + { + this.lazyCustomization = new Lazy( + () => GetCustomization(parameterInfo)); + this.lazyFrozenAttribute = new Lazy( + () => parameterInfo.GetCustomAttributes() + .OfType().FirstOrDefault()); + this.ParameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo)); + } + + public ParameterInfo ParameterInfo { get; } + + public ICustomization GetCustomization() => this.lazyCustomization.Value; + + public ICustomization GetCustomization(object? value) + { + var frozenAttribute = this.lazyFrozenAttribute.Value; + + if (frozenAttribute is null) + { + return NullCustomization.Instance; + } + + return new FrozenValueCustomization( + new ParameterFilter(this.ParameterInfo, frozenAttribute.By), + value); + } + + private static ICustomization GetCustomization(ParameterInfo parameter) + { + var customizations = parameter.GetCustomAttributes() + .OfType() + .OrderBy(x => x, new CustomizeAttributeComparer()) + .Select(x => x.GetCustomization(parameter)) + .ToArray(); + + return customizations switch + { + { Length: 0 } => NullCustomization.Instance, + { Length: 1 } => customizations[0], + _ => new CompositeCustomization(customizations), + }; + } + + public static TestParameter From(ParameterInfo parameterInfo) => new(parameterInfo); +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Internal/ValueTaskExtensions.cs b/src/AutoFixture.TUnit/Internal/ValueTaskExtensions.cs new file mode 100644 index 0000000..4c5aeec --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/ValueTaskExtensions.cs @@ -0,0 +1,20 @@ +namespace AutoFixture.TUnit.Internal; + +internal static class ValueTaskExtensions +{ + /// + /// Wraps the value in a . + /// + /// The value to be wrapped. + /// The generic type of the value task. + /// Returns a completed instance. + public static ValueTask ToValueTask(this T value) => new(value); + + /// + /// Wraps the task in a . + /// + /// The task to be wrapped. + /// The generic type of the value task. + /// Returns a instance. + public static ValueTask ToValueTask(this Task task) => new(task); +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/Matching.cs b/src/AutoFixture.TUnit/Matching.cs new file mode 100644 index 0000000..e284d4a --- /dev/null +++ b/src/AutoFixture.TUnit/Matching.cs @@ -0,0 +1,61 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace AutoFixture.TUnit; + +/// +/// The criteria used to determine which requests will be satisfied +/// by the frozen specimen created for a parameter +/// decorated with the attribute. +/// +[SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames", + Justification = "This enumeration is designed to be used together with an attribute and is named to improve readability.")] +[Flags] +public enum Matching +{ + /// + /// Matches requests for the exact same + /// as the type of the parameter. + /// + ExactType = 1, + + /// + /// Matches requests for a that is + /// a direct base of the type of the parameter. + /// + DirectBaseType = 2, + + /// + /// Matches requests for an interface that is + /// implemented by the type of the parameter. + /// + ImplementedInterfaces = 4, + + /// + /// Matches requests for a whose + /// is compatible with the type of the parameter + /// and has a specific name. + /// + ParameterName = 8, + + /// + /// Matches requests for a whose + /// is compatible with the type of the parameter + /// and has a specific name. + /// + PropertyName = 16, + + /// + /// Matches requests for a whose + /// is compatible with the type of the parameter + /// and has a specific name. + /// + FieldName = 32, + + /// + /// Matches requests for a parameter, property or field whose + /// is compatible with the type of the parameter + /// and has a specific name. + /// + MemberName = ParameterName | PropertyName | FieldName +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/MemberAutoDataAttribute.cs b/src/AutoFixture.TUnit/MemberAutoDataAttribute.cs new file mode 100644 index 0000000..9d43260 --- /dev/null +++ b/src/AutoFixture.TUnit/MemberAutoDataAttribute.cs @@ -0,0 +1,105 @@ +using System.Diagnostics.CodeAnalysis; +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit; + +/// +/// Provides a data source for a data theory, with the data coming from one of the following sources +/// and combined with auto-generated data specimens generated by AutoFixture: +/// 1. A static property +/// 2. A static field +/// 3. A static method (with parameters) +/// The member must return something compatible with IEnumerable<object?[]> with the test data. +/// +[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", + Justification = "This attribute is the root of a potential attribute hierarchy.")] +public class MemberAutoDataAttribute : AutoFixtureDataSourceAttribute +{ + /// + /// Initializes a new instance of the class. + /// + /// The name of the public static member on the test class that will provide the test data. + /// The parameters for the member (only supported for methods; ignored for everything else). + public MemberAutoDataAttribute(string memberName, params object?[] parameters) + : this(() => new Fixture(), memberType: null, memberName, parameters) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The type declaring the source member. + /// The name of the public static member on the test class that will provide the test data. + /// The parameters for the member (only supported for methods; ignored for everything else). + public MemberAutoDataAttribute(Type? memberType, string memberName, params object?[] parameters) + : this(() => new Fixture(), memberType, memberName, parameters) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The fixture factory delegate. + /// The name of the public static member on the test class that will provide the test data. + /// The parameters for the member (only supported for methods; ignored for everything else). + protected MemberAutoDataAttribute(Func fixtureFactory, string memberName, params object?[] parameters) + : this(fixtureFactory, memberType: null, memberName, parameters) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The fixture factory delegate. + /// The type declaring the source member. + /// The name of the public static member on the test class that will provide the test data. + /// The parameters for the member (only supported for methods; ignored for everything else). + /// Thrown when arguments are null. + protected MemberAutoDataAttribute(Func fixtureFactory, Type? memberType, string memberName, params object?[]? parameters) + { + this.FixtureFactory = fixtureFactory ?? throw new ArgumentNullException(nameof(fixtureFactory)); + this.MemberName = memberName ?? throw new ArgumentNullException(nameof(memberName)); + this.Parameters = parameters ?? [null!]; + this.MemberType = memberType; + } + + /// + /// Gets the fixture factory that provides the missing data from . + /// + public Func FixtureFactory { get; } + + /// + /// Gets the type of the class that provides the data. + /// + public Type? MemberType { get; } + + /// + /// Gets the member name. + /// + public string MemberName { get; } + + /// + /// Gets the parameters passed to the member. Only supported for static methods. + /// + public object?[] Parameters { get; } + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var testMethod = dataGeneratorMetadata.GetMethod(); + + if (testMethod is null) + { + throw new ArgumentNullException(nameof(dataGeneratorMetadata), "The test method cannot be null."); + } + + var sourceType = this.MemberType ?? testMethod.DeclaringType + ?? throw new InvalidOperationException("Source type cannot be null."); + + var source = new AutoDataSource( + createFixture: this.FixtureFactory, + source: new MemberDataSource(sourceType, this.MemberName, this.Parameters)); + + return source.GenerateDataSources(dataGeneratorMetadata).Select(x => x()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/ModestAttribute.cs b/src/AutoFixture.TUnit/ModestAttribute.cs new file mode 100644 index 0000000..9606ece --- /dev/null +++ b/src/AutoFixture.TUnit/ModestAttribute.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit; + +/// +/// An attribute that can be applied to parameters in an -driven +/// Theory to indicate that the parameter value should be created using the most modest +/// constructor that can be satisfied by an . +/// +[AttributeUsage(AttributeTargets.Parameter)] +public sealed class ModestAttribute : CustomizeAttribute +{ + /// + /// Gets a customization that associates a with the + /// of the parameter. + /// + /// The parameter for which the customization is requested. + /// + /// A customization that associates a with the + /// of the parameter. + /// + public override ICustomization GetCustomization(ParameterInfo parameter) + { + if (parameter is null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + return new ConstructorCustomization(parameter.ParameterType, new ModestConstructorQuery()); + } +} \ No newline at end of file diff --git a/src/AutoFixture.TUnit/NoAutoPropertiesAttribute.cs b/src/AutoFixture.TUnit/NoAutoPropertiesAttribute.cs new file mode 100644 index 0000000..0faa39e --- /dev/null +++ b/src/AutoFixture.TUnit/NoAutoPropertiesAttribute.cs @@ -0,0 +1,33 @@ +using System.Reflection; + +namespace AutoFixture.TUnit; + +/// +/// An attribute that can be applied to parameters in an -driven +/// Theory to indicate that the parameter value should not have properties auto populated +/// when the creates an instance of that type. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public sealed class NoAutoPropertiesAttribute : CustomizeAttribute +{ + /// + /// Gets a customization that stops auto population of properties for the type of the parameter. + /// + /// The parameter for which the customization is requested. + /// + /// A customization that stops auto population of the of the parameter. + /// + /// + /// is null. + /// + public override ICustomization GetCustomization(ParameterInfo parameter) + { + if (parameter is null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + var targetType = parameter.ParameterType; + return new NoAutoPropertiesCustomization(targetType); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/ArgumentsAutoDataAttributeTests.cs b/tests/AutoFixture.TUnit.Tests/ArgumentsAutoDataAttributeTests.cs new file mode 100644 index 0000000..e381ea2 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/ArgumentsAutoDataAttributeTests.cs @@ -0,0 +1,168 @@ +using System.Reflection; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests; + +public class ArgumentsAutoDataAttributeTests +{ + [Test] + public async Task SutIsDataAttribute() + { + // Arrange & Act + var sut = new ArgumentsAutoDataAttribute(); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task ValuesWillBeEmptyWhenSutIsCreatedWithDefaultConstructor() + { + // Arrange + var sut = new ArgumentsAutoDataAttribute(); + var expected = Enumerable.Empty(); + + // Act + var result = sut.Values; + + // Assert + await Assert.That(result).IsEquivalentTo(expected); + } + + [Test] + public async Task ValuesWillNotBeEmptyWhenSutIsCreatedWithConstructorArguments() + { + // Arrange + var expectedValues = new[] { new object(), new object(), new object() }; + var sut = new ArgumentsAutoDataAttribute(expectedValues); + + // Act + var result = sut.Values; + + // Assert + await Assert.That(result).IsEquivalentTo(expectedValues); + } + + [Test] + public async Task ValuesAreCorrectWhenConstructedWithExplicitAutoDataAttribute() + { + // Arrange + var expectedValues = new[] { new object(), new object(), new object() }; + var sut = new DerivedArgumentsAutoDataAttribute(() => new DelegatingFixture(), expectedValues); + + // Act + var result = sut.Values; + + // Assert + await Assert.That(result).IsEqualTo(expectedValues); + } + + [Test] + public async Task DoesntActivateFixtureImmediately() + { + // Arrange + var wasInvoked = false; + + // Act + _ = new DerivedArgumentsAutoDataAttribute(() => + { + wasInvoked = true; + return new DelegatingFixture(); + }); + + // Assert + await Assert.That(wasInvoked).IsFalse(); + } + + [Test] + [Arguments("CreateWithFrozenAndFavorArrays")] + [Arguments("CreateWithFavorArraysAndFrozen")] + [Arguments("CreateWithFrozenAndFavorEnumerables")] + [Arguments("CreateWithFavorEnumerablesAndFrozen")] + [Arguments("CreateWithFrozenAndFavorLists")] + [Arguments("CreateWithFavorListsAndFrozen")] + [Arguments("CreateWithFrozenAndGreedy")] + [Arguments("CreateWithGreedyAndFrozen")] + [Arguments("CreateWithFrozenAndModest")] + [Arguments("CreateWithModestAndFrozen")] + [Arguments("CreateWithFrozenAndNoAutoProperties")] + [Arguments("CreateWithNoAutoPropertiesAndFrozen")] + public async Task GetDataOrdersCustomizationAttributes(string methodName) + { + // Arrange + var customizationLog = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizationLog.Add(c) + }; + var sut = new DerivedArgumentsAutoDataAttribute(() => fixture); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(typeof(TypeWithCustomizationAttributes), methodName)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(customizationLog[0]).IsAssignableTo(); + + var composite = (CompositeCustomization)customizationLog[0]; + + await Assert.That(composite.Customizations.First()).IsNotTypeOf(); + await Assert.That(composite.Customizations.Last()).IsAssignableTo(); + } + + [Test] + [MethodDataSource(typeof(InlinePrimitiveValuesTestData), nameof(InlinePrimitiveValuesTestData.GetData))] + [MethodDataSource(typeof(InlineFrozenValuesTestData), nameof(InlineFrozenValuesTestData.GetData))] + public async Task ReturnsSingleTestDataWithExpectedValues(AutoFixtureDataSourceAttribute attribute, MethodInfo testMethod, + object[] expected) + { + // Act + var actual = attribute.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod.DeclaringType, testMethod.Name)).ToArray(); + + // Assert + await Assert.That(actual).HasSingleItem(); + await Assert.That(actual[0]).IsEquivalentTo(expected); + } + + [Test] + [ArgumentsAutoData] + public async Task GeneratesRandomData(int a, float b, string c, decimal d) + { + await Assert.That(a).IsNotEqualTo(0); + await Assert.That(b).IsNotEqualTo(0); + await Assert.That(c).IsNotNull(); + await Assert.That(d).IsNotEqualTo(0); + } + + [Test] + [ArgumentsAutoData(12, 32.1f, "hello", 71.231d)] + public async Task InlinesAllData(int a, float b, string c, decimal d) + { + await Assert.That(a).IsEqualTo(12); + await Assert.That(b).IsEqualTo(32.1f); + await Assert.That(c).IsEqualTo("hello"); + await Assert.That(d).IsEqualTo(71.231m); + } + + [Test] + [ArgumentsAutoData(0)] + [ArgumentsAutoData(5)] + [ArgumentsAutoData(-12)] + [ArgumentsAutoData(21.3f)] + [ArgumentsAutoData(18.7d)] + [ArgumentsAutoData(EnumType.First)] + [ArgumentsAutoData("Hello World")] + [ArgumentsAutoData("\t\r\n")] + [ArgumentsAutoData(" ")] + [ArgumentsAutoData("")] + [ArgumentsAutoData([null!])] + public async Task InjectsInlineValues([Frozen] object a, + [Frozen] PropertyHolder value, + PropertyHolder frozen) + { + await Assert.That(value.Property).IsEqualTo(a); + await Assert.That(value).IsSameReferenceAs(frozen); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/AutoDataAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/AutoDataAttributeTest.cs new file mode 100644 index 0000000..05a3442 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/AutoDataAttributeTest.cs @@ -0,0 +1,178 @@ +using AutoFixture.Kernel; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class AutoDataAttributeTest +{ + [Test] + public async Task SutIsDataAttribute() + { + // Arrange & Act + var sut = new AutoDataAttribute(); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task InitializedWithDefaultConstructorHasCorrectFixture() + { + // Arrange + var sut = new AutoDataAttribute(); + + // Act + var result = sut.FixtureFactory(); + + // Assert + await Assert.That(result).IsAssignableTo(); + } + + [Test] + public async Task InitializedWithFixtureFactoryConstructorHasCorrectFixture() + { + // Arrange + var fixture = new Fixture(); + + // Act + var sut = new DerivedAutoDataAttribute(() => fixture); + + // Assert + await Assert.That(sut.FixtureFactory()).IsSameReferenceAs(fixture); + } + + [Test] + public async Task InitializeWithNullFixtureFactoryThrows() + { + // Arrange + // Act & Assert + await Assert.That(() => + new DerivedAutoDataAttribute(null!)).ThrowsExactly(); + } + + [Test] + public async Task DoesntActivateFixtureImmediately() + { + // Arrange + var wasInvoked = false; + + // Act + _ = new DerivedAutoDataAttribute(() => + { + wasInvoked = true; + return null!; + }); + + // Assert + await Assert.That(wasInvoked).IsFalse(); + } + + [Test] + public async Task GetDataWithNullMethodThrows() + { + // Arrange + var sut = new AutoDataAttribute(); + + // Act & assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(null!, null!))).ThrowsException(); + } + + [Test] + public async Task GetDataReturnsCorrectResult() + { + // Arrange + var method = typeof(TypeWithOverloadedMembers) + .GetMethod("DoSomething", [typeof(object)]); + var parameters = method!.GetParameters(); + var expectedResult = new object(); + + object actualParameter = null!; + ISpecimenContext actualContext = null!; + var builder = new DelegatingSpecimenBuilder + { + OnCreate = (r, c) => + { + actualParameter = r; + actualContext = c; + return expectedResult; + } + }; + var composer = new DelegatingFixture { OnCreate = builder.OnCreate }; + var sut = new DerivedAutoDataAttribute(() => composer); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(actualContext).IsNotNull(); + await Assert.That(parameters).HasSingleItem(); + await Assert.That(actualParameter).IsEqualTo(parameters[0]); + await Assert.That(result.Single()).IsEquivalentTo(new[] { expectedResult }); + } + + [Test] + [Arguments("CreateWithFrozenAndFavorArrays")] + [Arguments("CreateWithFavorArraysAndFrozen")] + [Arguments("CreateWithFrozenAndFavorEnumerables")] + [Arguments("CreateWithFavorEnumerablesAndFrozen")] + [Arguments("CreateWithFrozenAndFavorLists")] + [Arguments("CreateWithFavorListsAndFrozen")] + [Arguments("CreateWithFrozenAndGreedy")] + [Arguments("CreateWithGreedyAndFrozen")] + [Arguments("CreateWithFrozenAndModest")] + [Arguments("CreateWithModestAndFrozen")] + [Arguments("CreateWithFrozenAndNoAutoProperties")] + [Arguments("CreateWithNoAutoPropertiesAndFrozen")] + public async Task GetDataOrdersCustomizationAttributes(string methodName) + { + // Arrange + var method = typeof(TypeWithCustomizationAttributes) + .GetMethod(methodName, [typeof(ConcreteType)]); + var customizationLog = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizationLog.Add(c) + }; + var sut = new DerivedAutoDataAttribute(() => fixture); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method.DeclaringType, method.Name)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(customizationLog[0]).IsAssignableTo(); + + var composite = (CompositeCustomization)customizationLog[0]; + + await Assert.That(composite.Customizations.First()).IsNotTypeOf(); + await Assert.That(composite.Customizations.Last()).IsAssignableTo(); + } + + [Test] + public async Task ShouldRecognizeAttributesImplementingIParameterCustomizationSource() + { + // Arrange + var method = typeof(TypeWithIParameterCustomizationSourceUsage) + .GetMethod(nameof(TypeWithIParameterCustomizationSourceUsage.DecoratedMethod)); + + var customizationLog = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizationLog.Add(c) + }; + var sut = new DerivedAutoDataAttribute(() => fixture); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method.DeclaringType, method.Name)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(customizationLog[0]).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/AutoFixture.TUnit.Tests.csproj b/tests/AutoFixture.TUnit.Tests/AutoFixture.TUnit.Tests.csproj index d8bc53f..57224b4 100644 --- a/tests/AutoFixture.TUnit.Tests/AutoFixture.TUnit.Tests.csproj +++ b/tests/AutoFixture.TUnit.Tests/AutoFixture.TUnit.Tests.csproj @@ -5,6 +5,7 @@ net48;net8.0 + disable @@ -18,6 +19,7 @@ + @@ -26,6 +28,7 @@ + diff --git a/tests/AutoFixture.TUnit.Tests/AutoFixtureLogo200x200.png b/tests/AutoFixture.TUnit.Tests/AutoFixtureLogo200x200.png new file mode 100644 index 0000000..32a82c5 Binary files /dev/null and b/tests/AutoFixture.TUnit.Tests/AutoFixtureLogo200x200.png differ diff --git a/tests/AutoFixture.TUnit.Tests/ClassAutoDataAttributeTests.cs b/tests/AutoFixture.TUnit.Tests/ClassAutoDataAttributeTests.cs new file mode 100644 index 0000000..c9ca72c --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/ClassAutoDataAttributeTests.cs @@ -0,0 +1,314 @@ +using System.Collections; +using AutoFixture.Kernel; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class ClassAutoDataAttributeTests +{ + [Test] + public void CanCreateInstance() + { + // Act & Assert + _ = new ClassAutoDataAttribute(typeof(MixedTypeClassData)); + } + + [Test] + public async Task IsDataAttribute() + { + // Arrange & Act + var sut = new ClassAutoDataAttribute(typeof(MixedTypeClassData)); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task ThrowsWhenSourceTypeIsNull() + { + // Act & Assert + await Assert.That(() => new ClassAutoDataAttribute(null!)).ThrowsExactly(); + } + + [Test] + public async Task TreatsNullParameterValueAsArrayWithNull() + { + // Arrange & Act + var sut = new ClassAutoDataAttribute(typeof(MixedTypeClassData), null!); + + // Assert + await Assert.That(sut.Parameters).HasSingleItem() + .And + .IsNotNull(); + } + + [Test] + public async Task ThrowsWhenFixtureFactoryIsNull() + { + // Act & Assert + await Assert.That(() => new DerivedClassAutoDataAttribute( + fixtureFactory: null!, typeof(MixedTypeClassData))).ThrowsExactly(); + } + + [Test] + public async Task GetDataThrowsWhenSourceTypeNotEnumerable() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(MyClass)); + var testMethod = typeof(ExampleTestClass) + .GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()).ToArray()).ThrowsExactly(); + } + + [Test] + public async Task GetDataThrowsWhenParametersDoNotMatchConstructor() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(MyClass), "myString", 33, null!); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()).ToArray()).ThrowsException(); + } + + [Test] + public async Task GetDataDoesNotThrowWhenSourceYieldsNoResults() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(EmptyClassData)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act + var data = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + ; + + // Assert + await Assert.That(data).IsEmpty(); + } + + [Test] + public async Task GetDataThrowsWhenSourceYieldsNullResults() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(ClassWithNullTestData)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act & assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()).ToArray()).ThrowsException(); + } + + [Test] + public void GetDataDoesNotThrow() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(MixedTypeClassData)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act & Assert + sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)); + } + + [Test] + public async Task GetDataReturnsEnumerable() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(MixedTypeClassData)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)); + + // Assert + await Assert.That(actual).IsNotNull(); + } + + [Test] + public async Task GetDataReturnsNonEmptyEnumerable() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(MixedTypeClassData)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)); + + // Assert + await Assert.That(actual).IsNotEmpty(); + } + + [Test] + public async Task GetDataReturnsExpectedTestDataCount() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(MixedTypeClassData)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)); + + // Assert + await Assert.That(actual).HasCount().EqualTo(5); + } + + [Test] + public async Task GetDataThrowsWhenDataSourceNotEnumerable() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(GuardedConstructorHost)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()).ToArray()).ThrowsException(); + } + + [Test] + public async Task GetDataThrowsForNonMatchingConstructorTypes() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(DelegatingTestData), "myString", 33, null!); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()).ToArray()).ThrowsException(); + } + + [Test] + [Arguments("CreateWithFrozenAndFavorArrays")] + [Arguments("CreateWithFavorArraysAndFrozen")] + [Arguments("CreateWithFrozenAndFavorEnumerables")] + [Arguments("CreateWithFavorEnumerablesAndFrozen")] + [Arguments("CreateWithFrozenAndFavorLists")] + [Arguments("CreateWithFavorListsAndFrozen")] + [Arguments("CreateWithFrozenAndGreedy")] + [Arguments("CreateWithGreedyAndFrozen")] + [Arguments("CreateWithFrozenAndModest")] + [Arguments("CreateWithModestAndFrozen")] + [Arguments("CreateWithFrozenAndNoAutoProperties")] + [Arguments("CreateWithNoAutoPropertiesAndFrozen")] + public async Task GetDataOrdersCustomizationAttributes(string methodName) + { + // Arrange + var method = typeof(TypeWithCustomizationAttributes) + .GetMethod(methodName, [typeof(ConcreteType)]); + var customizationLog = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizationLog.Add(c) + }; + + var sut = new DerivedClassAutoDataAttribute(() => fixture, typeof(ClassWithEmptyTestData)); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(customizationLog[0]).IsAssignableTo(); + + var composite = (CompositeCustomization)customizationLog[0]; + + await Assert.That(composite.Customizations.First()).IsNotTypeOf(); + await Assert.That(composite.Customizations.Last()).IsAssignableTo(); + } + + [Test] + public async Task GetDataReturnsExpectedTestData() + { + var builder = new CompositeSpecimenBuilder( + new FixedParameterBuilder("a", 1), + new FixedParameterBuilder("b", "value"), + new FixedParameterBuilder("c", EnumType.First), + new FixedParameterBuilder>("d", new Tuple("value", 1))); + var sut = new DerivedClassAutoDataAttribute( + () => new DelegatingFixture { OnCreate = (r, c) => builder.Create(r, c) }, + typeof(MixedTypeClassData)); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + object[][] expected = + [ + [1, "value", EnumType.First, new Tuple("value", 1)], + [9, "value", EnumType.First, new Tuple("value", 1)], + [12, "test-12", EnumType.First, new Tuple("value", 1)], + [223, "test-17", EnumType.Third, new Tuple("value", 1)], + [-95, "test-92", EnumType.Second, new Tuple("myValue", 5)] + ]; + + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + await Assert.That(actual).IsEquivalentTo(expected); + } + + [Test] + public async Task GetDataReturnsExpectedTestDataFromParameterizedSource() + { + var builder = new CompositeSpecimenBuilder( + new FixedParameterBuilder("a", 1), + new FixedParameterBuilder("b", "value"), + new FixedParameterBuilder>("d", new Tuple("value", 1))); + var sut = new DerivedClassAutoDataAttribute( + () => new DelegatingFixture { OnCreate = (r, c) => builder.Create(r, c) }, + typeof(ParameterizedClassData), + 29, "myValue", EnumType.Third); + var testMethod = typeof(ExampleTestClass).GetMethod(nameof(ExampleTestClass.TestMethod)); + object[][] expected = + [ + [29, "myValue", EnumType.Third, new Tuple("value", 1)], + [29, "myValue", EnumType.Third, new Tuple("value", 1)] + ]; + + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + await Assert.That(actual).IsEquivalentTo(expected); + } + + [Test] + public async Task TestWithNullParametersPasses() + { + // Arrange + var sut = new ClassAutoDataAttribute(typeof(TestDataWithNullValues)); + var testMethod = typeof(ExampleTestClass>) + .GetMethod(nameof(ExampleTestClass>.TestMethod)); + var expected = new[] + { + new object[] { null!, null!, null!, null! }, + new object[] { string.Empty, null!, null!, null! }, + new object[] { null!, " ", null!, null! }, + }; + + // Act + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(actual).IsEquivalentTo(expected); + } + + public class TestDataWithNullValues : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return [null!, null!, null!, null!]; + yield return [string.Empty, null!, null!, null!]; + yield return [null!, " ", null!, null!]; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/ClassAutoDataScenarioTests.cs b/tests/AutoFixture.TUnit.Tests/ClassAutoDataScenarioTests.cs new file mode 100644 index 0000000..d9b146d --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/ClassAutoDataScenarioTests.cs @@ -0,0 +1,63 @@ +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests; + +public class ClassAutoDataScenarioTests +{ + [Test] + [ClassAutoData(typeof(MixedTypeClassData))] + public async Task TestWithMixedTypesPasses(int? a, string b, EnumType? c, Tuple d) + { + await Assert.That(a).IsNotNull(); + await Assert.That(b).IsNotNull(); + await Assert.That(c).IsNotNull(); + await Assert.That(d).IsNotNull(); + } + + [Test] + [ClassAutoData(typeof(ParameterizedClassData), 42, "test-13", EnumType.Third)] + public async Task TestWithParameterizedClassDataReceivesExpectedData( + int a, string b, EnumType c, PropertyHolder d) + { + await Assert.That(a).IsEqualTo(42); + await Assert.That(b).IsEqualTo("test-13"); + await Assert.That(c).IsEqualTo(EnumType.Third); + await Assert.That(d?.Property).IsNotNull(); + } + + [Test] + [ClassAutoData(typeof(ParameterizedClassData), 13, "test-46", EnumType.Second)] + public async Task TestWithFrozenParametersReceivesExpectedData( + [Frozen] int a, [Frozen] string b, [Frozen] EnumType c, + PropertyHolder a1, PropertyHolder b1, PropertyHolder c1) + { + await Assert.That(a).IsEqualTo(13); + await Assert.That(b).IsEqualTo("test-46"); + await Assert.That(c).IsEqualTo(EnumType.Second); + + await Assert.That(a1.Property).IsEqualTo(a); + await Assert.That(b1.Property).IsEqualTo(b); + await Assert.That(c1.Property).IsEqualTo(c); + } + + [Test] + [ClassAutoData(typeof(ParameterizedClassData), 59, "hello-world", EnumType.Second)] + public async Task TestWithInjectedValuesRespectsOtherParameterCustomizations( + [Frozen] int a, [Frozen] string b, [Frozen] EnumType c, + [FavorEnumerables] CompositeTypeWithOverloadedConstructors numbers, + [FavorArrays] CompositeTypeWithOverloadedConstructors strings, + [FavorLists] CompositeTypeWithOverloadedConstructors enums) + { + await Assert.That(numbers.Items).IsAssignableTo>(); + await Assert.That(numbers.Items).IsNotTypeOf>(); + await Assert.That(numbers.Items).IsNotTypeOf(); + await Assert.That(numbers.Items).All().Satisfy(item => item.IsEqualTo(a)); + + await Assert.That(strings.Items).IsAssignableTo(); + await Assert.That(strings.Items).All().Satisfy(item => item.IsEqualTo(b)); + + await Assert.That(enums.Items).IsAssignableTo>(); + await Assert.That(enums.Items).All().Satisfy(item => item.IsEqualTo(c)); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/CompositeDataAttributeSufficientDataTest.cs b/tests/AutoFixture.TUnit.Tests/CompositeDataAttributeSufficientDataTest.cs new file mode 100644 index 0000000..f11eeb7 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/CompositeDataAttributeSufficientDataTest.cs @@ -0,0 +1,208 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.Equality; + +namespace AutoFixture.TUnit.Tests; + +[SuppressMessage("Usage", "TUnit0014:Method should have a `Test` attribute or be made `private` or `protected`")] +[SuppressMessage("Usage", "TUnit0046:Return a `Func` rather than a ``")] +public class CompositeDataAttributeSufficientDataTest +{ + private readonly MethodInfo method = typeof(TypeWithOverloadedMembers) + .GetMethod(nameof(TypeWithOverloadedMembers.DoSomething), + [typeof(object), typeof(object), typeof(object)]); + + [Test] + [MethodDataSource(typeof(CompositeDataAttributeSufficientDataTest), nameof(GetEnumerator))] + public async Task GetDataReturnsCorrectResult(IEnumerable attributes, IEnumerable expectedResult) + { + // Arrange + var attribute = new CompositeDataAttribute(attributes.ToArray()); + + // Act + var result = attribute.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(this.method)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(result).IsEquivalentTo(expectedResult, new CollectionEquivalentToEqualityComparer()); + } + + public IEnumerable<(IEnumerable Attributes, IEnumerable ExpectedResult)> GetEnumerator() + { + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2, 3]]) + ], + expected: + [ + [1, 2, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2, 3]]), + new FakeDataAttribute(this.method, [[4, 5, 6]]) + ], + expected: + [ + [1, 2, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1]]), + new FakeDataAttribute(this.method, [[2, 3, 4]]) + ], + expected: + [ + [1, 3, 4] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2]]), + new FakeDataAttribute(this.method, [[3, 4, 5]]) + ], + expected: + [ + [1, 2, 5] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2, 3], [4, 5, 6]]) + ], + expected: + [ + [1, 2, 3], + [4, 5, 6] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2, 3], [4, 5, 6]]), + new FakeDataAttribute(this.method, + [[7, 8], [9, 10], [11, 12]]) + ], + expected: + [ + [1, 2, 3], + [4, 5, 6] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, + [[1, 2], [3, 4], [5, 6]]), + new FakeDataAttribute(this.method, + [[7, 8, 9], [10, 11, 12], [13, 14, 15]]) + ], + expected: + [ + [1, 2, 9], + [3, 4, 12], + [5, 6, 15] + ]); + + // Second attribute restricts + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2, 3], [4, 5, 6]]), + new FakeDataAttribute(this.method, [[7, 8, 9]]) + ], + expected: + [ + [1, 2, 3] + ]); + + // Shortest data provider is limiting factor + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2, 3]]), + new FakeDataAttribute(this.method, [[4, 5, 6], [7, 8, 9]]) + ], + expected: + [ + [1, 2, 3] + ]); + + // Test incorrect number of parameters - should just return what it's given + // and let xUnit deal with counting parameters + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1, 2]]), + new FakeDataAttribute(this.method, [[3, 4]]) + ], + expected: + [ + [1, 2] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1]]), + new FakeDataAttribute(this.method, [[2, 3]]) + ], + expected: + [ + [1, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1]]), + new FakeDataAttribute(this.method, [[]]), + new FakeDataAttribute(this.method, [[2, 3]]) + ], + expected: + [ + [1, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(this.method, [[1]]), + new FakeDataAttribute(this.method, [[2]]), + new FakeDataAttribute(this.method, [[3]]) + ], + expected: + [ + [1] + ]); + } + + private static (IEnumerable attributes, + IEnumerable expectedResult) CreateTestData(AutoFixtureDataSourceAttribute[] data, object[][] expected) + { + return (data, expected); + } + + private sealed class TheoryComparer : IEqualityComparer + { + public bool Equals(object[] x, object[] y) + { + return x!.SequenceEqual(y!); + } + + public int GetHashCode(object[] array) + { + return array.Select(obj => obj.GetHashCode()).Aggregate((x, y) => x ^ y); + } + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/CompositeDataAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/CompositeDataAttributeTest.cs new file mode 100644 index 0000000..c9313c4 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/CompositeDataAttributeTest.cs @@ -0,0 +1,108 @@ +using System.Reflection; +using AutoFixture.TUnit.Tests.TestTypes; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class CompositeDataAttributeTest +{ + [Test] + public async Task SutIsDataAttribute() + { + // Arrange & Act + var sut = new CompositeDataAttribute(); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task InitializeWithNullArrayThrows() + { + // Arrange + // Act & assert + await Assert.That(() => new CompositeDataAttribute(null!)).ThrowsExactly(); + } + + [Test] + public async Task AttributesIsCorrectWhenInitializedWithArray() + { + // Arrange + var a = () => { }; + var method = a.GetMethodInfo(); + + var attributes = new AutoFixtureDataSourceAttribute[] + { + new FakeDataAttribute(method, []), + new FakeDataAttribute(method, []), + new FakeDataAttribute(method, []) + }; + + var sut = new CompositeDataAttribute(attributes); + // Act + IEnumerable result = sut.Attributes; + // Assert + await Assert.That(result).IsEquivalentTo(attributes); + } + + [Test] + public void InitializeWithNullEnumerableThrows() + { + // Arrange + // Act & assert + Assert.Throws( + () => new CompositeDataAttribute((IReadOnlyCollection)null!)); + } + + [Test] + public async Task AttributesIsCorrectWhenInitializedWithEnumerable() + { + // Arrange + var a = () => { }; + var method = a.GetMethodInfo(); + + var attributes = new AutoFixtureDataSourceAttribute[] + { + new FakeDataAttribute(method, []), + new FakeDataAttribute(method, []), + new FakeDataAttribute(method, []) + }; + + var sut = new CompositeDataAttribute(attributes); + // Act + var result = sut.Attributes; + // Assert + await Assert.That(result).IsEquivalentTo(attributes); + } + + [Test] + public async Task GetDataWithNullMethodThrows() + { + // Arrange + var sut = new CompositeDataAttribute(); + // Act & assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(null!, null!)) + .Select(x => x()).ToArray()).ThrowsException(); + } + + [Test] + public async Task GetDataOnMethodWithNoParametersReturnsNoTheory() + { + // Arrange + Action a = () => { }; + var method = a.GetMethodInfo(); + + var sut = new CompositeDataAttribute( + new FakeDataAttribute(method, []), + new FakeDataAttribute(method, []), + new FakeDataAttribute(method, [])); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(result).All().Satisfy(row => row.IsEmpty()); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/CustomizeAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/CustomizeAttributeTest.cs new file mode 100644 index 0000000..d02724a --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/CustomizeAttributeTest.cs @@ -0,0 +1,36 @@ +using AutoFixture.TUnit.Tests.TestTypes; + +namespace AutoFixture.TUnit.Tests; + +public class CustomizeAttributeTest +{ + [Test] + public async Task TestableSutIsSut() + { + // Arrange + // Act + var sut = new DelegatingCustomizeAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task SutIsAttribute() + { + // Arrange + // Act + var sut = new DelegatingCustomizeAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task SutImplementsIParameterCustomizationSource() + { + // Arrange + // Act + var sut = new DelegatingCustomizeAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/DataGeneratorMetadataHelper.cs b/tests/AutoFixture.TUnit.Tests/DataGeneratorMetadataHelper.cs new file mode 100644 index 0000000..a1921f1 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/DataGeneratorMetadataHelper.cs @@ -0,0 +1,62 @@ +using System.Reflection; +using TUnit.Core.Enums; + +namespace AutoFixture.TUnit.Tests; + +public class DataGeneratorMetadataHelper +{ + public static DataGeneratorMetadata CreateDataGeneratorMetadata(Type type, string methodName) + { + return CreateDataGeneratorMetadata(type.GetMethod(methodName)); + } + + public static DataGeneratorMetadata CreateDataGeneratorMetadata(MethodInfo methodInfo) + { + var parameters = methodInfo.GetParameters(); + var type = methodInfo.ReflectedType ?? methodInfo.DeclaringType!; + var methodName = methodInfo.Name; + var attributes = methodInfo.GetCustomAttributes().ToArray(); + + var sourceGeneratedParameterInformations = parameters?.Select(CreateParameter).ToArray() ?? []; + + return new DataGeneratorMetadata + { + Type = DataGeneratorType.TestParameters, + TestBuilderContext = null!, + TestSessionId = null!, + MembersToGenerate = sourceGeneratedParameterInformations.Cast() + .ToArray(), + TestInformation = new SourceGeneratedMethodInformation + { + Type = type, + Name = methodName, + Attributes = attributes ?? [], + GenericTypeCount = 0, + Class = new SourceGeneratedClassInformation + { + Type = type, + Assembly = null!, + Attributes = type.GetCustomAttributes() + .ToArray(), + Name = type.Name, + Namespace = null!, + Parameters = [], + Properties = [] + }, + Parameters = sourceGeneratedParameterInformations, + ReturnType = typeof(void), + }, + TestClassInstance = null!, + ClassInstanceArguments = [] + }; + } + + private static SourceGeneratedParameterInformation CreateParameter(ParameterInfo parameterInfo) + { + return new SourceGeneratedParameterInformation(parameterInfo.ParameterType) + { + Name = parameterInfo.Name!, + Attributes = parameterInfo.GetCustomAttributes().ToArray() + }; + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/DependencyConstraintsTests.cs b/tests/AutoFixture.TUnit.Tests/DependencyConstraintsTests.cs new file mode 100644 index 0000000..2fe4723 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/DependencyConstraintsTests.cs @@ -0,0 +1,46 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests; + +public class DependencyConstraintsTests +{ + [Test] + [Arguments("FakeItEasy")] + [Arguments("Foq")] + [Arguments("FsCheck")] + [Arguments("Moq")] + [Arguments("NSubstitute")] + [Arguments("nunit.framework")] + [Arguments("Rhino.Mocks")] + [Arguments("Unquote")] + [Arguments("xunit")] + [Arguments("xunit.extensions")] + public async Task AutoFixtureXunit3DoesNotReference(string assemblyName) + { + // Arrange + // Act + var references = typeof(AutoDataAttribute).GetTypeInfo().Assembly.GetReferencedAssemblies(); + // Assert + await Assert.That(references).DoesNotContain(an => an.Name == assemblyName); + } + + [Test] + [Arguments("FakeItEasy")] + [Arguments("Foq")] + [Arguments("FsCheck")] + [Arguments("Moq")] + [Arguments("NSubstitute")] + [Arguments("nunit.framework")] + [Arguments("Rhino.Mocks")] + [Arguments("Unquote")] + [Arguments("xunit")] + [Arguments("xunit.extensions")] + public async Task AutoFixtureXunit3UnitTestsDoNotReference(string assemblyName) + { + // Arrange + // Act + var references = this.GetType().GetTypeInfo().Assembly.GetReferencedAssemblies(); + // Assert + await Assert.That(references).DoesNotContain(an => an.Name == assemblyName); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/EnumerableExtensions.cs b/tests/AutoFixture.TUnit.Tests/EnumerableExtensions.cs new file mode 100644 index 0000000..9d5a06f --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/EnumerableExtensions.cs @@ -0,0 +1,55 @@ +namespace AutoFixture.TUnit.Tests; + +internal static class EnumerableExtensions +{ + /// + /// Applies a specified function to the corresponding elements of any number of sequences. + /// + /// The type of the elements of the input sequences. + /// The type of the elements of the result sequence. + /// The input sequences. + /// A function that specifies how to combine the corresponding elements of the sequences. + /// An that contains elements of the input sequences, combined by resultSelector. + internal static IEnumerable Zip(this IEnumerable> sequences, + Func, TResult> resultSelector) + { + var enumerators = sequences.Select(s => s.GetEnumerator()).ToList(); + while (enumerators.TrueForAll(e => e.MoveNext())) + { + yield return resultSelector(enumerators.Select(e => e.Current)); + } + } + + /// + /// Collapses a series of sequences down by using items from the first sequence until it finishes, + /// then continuing from the same index through the second sequence, and so on until all sequences + /// have been exhausted. + /// + /// The type of the elements of the input sequences. + /// The input sequences. + /// Items from each sequence in turn, yielding those from the first sequence first. + internal static IEnumerable Collapse(this IEnumerable> sequences) + { + var position = 0; + foreach (var sequence in sequences) + { + foreach (var item in sequence.Skip(position)) + { + position++; + yield return item; + } + } + } + + /// + /// Casts the source sequence to an . + /// Enumerates the source sequence to an array if it is not an enumerated collection. + /// + /// The source sequence. + /// The sequence item type. + /// An that contains elements of the source sequence. + public static IReadOnlyCollection AsReadOnlyCollection(this IEnumerable source) + { + return source as IReadOnlyCollection ?? source.ToArray(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/FavorArraysAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/FavorArraysAttributeTest.cs new file mode 100644 index 0000000..f0ee622 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/FavorArraysAttributeTest.cs @@ -0,0 +1,44 @@ +using AutoFixture.Kernel; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class FavorArraysAttributeTest +{ + [Test] + public async Task SutIsAttribute() + { + // Arrange + // Act + var sut = new FavorArraysAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task GetCustomizationFromNullParameterThrows() + { + // Arrange + var sut = new FavorArraysAttribute(); + // Act & assert + await Assert.That(() => + sut.GetCustomization(null!)).ThrowsExactly(); + } + + [Test] + public async Task GetCustomizationReturnsCorrectResult() + { + // Arrange + var sut = new FavorArraysAttribute(); + var parameter = typeof(TypeWithOverloadedMembers) + .GetMethod(nameof(TypeWithOverloadedMembers.DoSomething), [typeof(object)])! + .GetParameters().Single(); + // Act + var result = sut.GetCustomization(parameter); + // Assert + var invoker = await Assert.That(result).IsAssignableTo(); + await Assert.That(invoker?.TargetType).IsEqualTo(parameter.ParameterType); + await Assert.That(invoker?.Query).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/FavorEnumerablesAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/FavorEnumerablesAttributeTest.cs new file mode 100644 index 0000000..77838e6 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/FavorEnumerablesAttributeTest.cs @@ -0,0 +1,44 @@ +using AutoFixture.Kernel; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class FavorEnumerablesAttributeTest +{ + [Test] + public async Task SutIsAttribute() + { + // Arrange + // Act + var sut = new FavorEnumerablesAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task GetCustomizationFromNullParameterThrows() + { + // Arrange + var sut = new FavorEnumerablesAttribute(); + // Act & assert + await Assert.That(() => + sut.GetCustomization(null!)).ThrowsExactly(); + } + + [Test] + public async Task GetCustomizationReturnsCorrectResult() + { + // Arrange + var sut = new FavorEnumerablesAttribute(); + var parameter = typeof(TypeWithOverloadedMembers) + .GetMethod(nameof(TypeWithOverloadedMembers.DoSomething), [typeof(object)])! + .GetParameters().Single(); + // Act + var result = sut.GetCustomization(parameter); + // Assert + var invoker = await Assert.That(result).IsAssignableTo(); + await Assert.That(invoker?.TargetType).IsEqualTo(parameter.ParameterType); + await Assert.That(invoker?.Query).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/FavorListsAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/FavorListsAttributeTest.cs new file mode 100644 index 0000000..906376c --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/FavorListsAttributeTest.cs @@ -0,0 +1,44 @@ +using AutoFixture.Kernel; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class FavorListsAttributeTest +{ + [Test] + public async Task SutIsAttribute() + { + // Arrange + // Act + var sut = new FavorListsAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task GetCustomizationFromNullParameterThrows() + { + // Arrange + var sut = new FavorListsAttribute(); + // Act & assert + await Assert.That(() => + sut.GetCustomization(null!)).ThrowsExactly(); + } + + [Test] + public async Task GetCustomizationReturnsCorrectResult() + { + // Arrange + var sut = new FavorListsAttribute(); + var parameter = typeof(TypeWithOverloadedMembers) + .GetMethod(nameof(TypeWithOverloadedMembers.DoSomething), [typeof(object)])! + .GetParameters().Single(); + // Act + var result = sut.GetCustomization(parameter); + // Assert + var invoker = await Assert.That(result).IsAssignableTo(); + await Assert.That(invoker?.TargetType).IsEqualTo(parameter.ParameterType); + await Assert.That(invoker?.Query).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/FrozenAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/FrozenAttributeTest.cs new file mode 100644 index 0000000..bafde0c --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/FrozenAttributeTest.cs @@ -0,0 +1,26 @@ +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class FrozenAttributeTest +{ + [Test] + public async Task SutIsAttribute() + { + // Arrange + // Act + var sut = new FrozenAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task GetCustomizationFromNullParameterThrows() + { + // Arrange + var sut = new FrozenAttribute(); + // Act & assert + await Assert.That(() => + sut.GetCustomization(null!)).ThrowsExactly(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/GreedyAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/GreedyAttributeTest.cs new file mode 100644 index 0000000..1faf29c --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/GreedyAttributeTest.cs @@ -0,0 +1,47 @@ +using AutoFixture.Kernel; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; +#if NETCOREAPP1_1 +using System.Reflection; +#endif + +namespace AutoFixture.TUnit.Tests; + +public class GreedyAttributeTest +{ + [Test] + public async Task SutIsAttribute() + { + // Arrange + // Act + var sut = new GreedyAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task GetCustomizationFromNullParameterThrows() + { + // Arrange + var sut = new GreedyAttribute(); + // Act & assert + await Assert.That(() => + sut.GetCustomization(null!)).ThrowsExactly(); + } + + [Test] + public async Task GetCustomizationReturnsCorrectResult() + { + // Arrange + var sut = new GreedyAttribute(); + var parameter = typeof(TypeWithOverloadedMembers) + .GetMethod(nameof(TypeWithOverloadedMembers.DoSomething), [typeof(object)])! + .GetParameters().Single(); + // Act + var result = sut.GetCustomization(parameter); + // Assert + var invoker = await Assert.That(result).IsAssignableTo(); + await Assert.That(invoker?.TargetType).IsEqualTo(parameter.ParameterType); + await Assert.That(invoker?.Query).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/AutoDataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/AutoDataSourceTests.cs new file mode 100644 index 0000000..c479105 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/AutoDataSourceTests.cs @@ -0,0 +1,233 @@ +using AutoFixture.Kernel; +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class AutoDataSourceTests +{ + [Test] + public async Task SutIsTestDataSource() + { + // Arrange & Act + var sut = new AutoDataSource(() => new DelegatingFixture()); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task SetsPropertiesToExpectedValues() + { + // Arrange + var fixtureFactory = () => new DelegatingFixture(); + var source = new DelegatingDataSource(); + + // Act + var sut = new AutoDataSource(fixtureFactory, source); + + // Assert + await Assert.That>(sut.CreateFixture).IsSameReferenceAs(fixtureFactory); + await Assert.That(sut.Source).IsSameReferenceAs(source); + } + + [Test] + public async Task GeneratesTestDataUsingFixture() + { + // Arrange + var builder = new CompositeSpecimenBuilder( + new FixedParameterBuilder("a", "value"), + new FixedParameterBuilder("b", 1), + new FixedParameterBuilder("c", 12.2)); + var fixture = new DelegatingFixture + { + OnCreate = (r, c) => builder.Create(r, c), + }; + var sut = new AutoDataSource(() => fixture); + var method = typeof(SampleTestType).GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).Select(x => x()).ToArray(); + + // Assert + await Assert.That(result).IsNotNull(); + var item = await Assert.That(result).HasSingleItem(); + + await Assert.That(item).IsNotNull(); + await Assert.That(item.Length).IsEqualTo(3); + await Assert.That(item[0]).IsEqualTo("value"); + await Assert.That(item[1]).IsEqualTo(1); + await Assert.That(item[2]).IsEqualTo(12.2); + } + + [Test] + public async Task CombinesTestDataFromSourceWithAutoGeneratedValues() + { + // Arrange + var source = new DelegatingDataSource + { + TestData = + [ + ["a", 15], + ["b", 123], + ["c", 999] + ], + }; + var builder = new FixedParameterBuilder("c", 13.3); + var fixture = new DelegatingFixture + { + OnCreate = (r, c) => builder.Create(r, c), + }; + var sut = new AutoDataSource(() => fixture, source); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray(); + + // Assert + await Assert.That(result).IsNotNull(); + await Assert.That(result.Length).IsEqualTo(3); + await Assert.That(result[0]).IsEquivalentTo(new object[] { "a", 15, 13.3 }); + await Assert.That(result[1]).IsEquivalentTo(new object[] { "b", 123, 13.3 }); + await Assert.That(result[2]).IsEquivalentTo(new object[] { "c", 999, 13.3 }); + } + + [Test] + public async Task DoesNotGenerateValuesWhenAllValuesProvidedBySource() + { + // Arrange + var source = new DelegatingDataSource + { + TestData = + [ + ["a", 85, 913.12], + ["b", 519, 73.1], + ["c", 411, 529.6] + ], + }; + var fixture = new DelegatingFixture + { + OnCreate = (_, _) => throw new InvalidOperationException("Fixture should not be called"), + }; + var sut = new AutoDataSource(() => fixture, source); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray(); + + // Assert + await Assert.That(result).IsNotNull(); + await Assert.That(result.Length).IsEqualTo(3); + await Assert.That(result[0]).IsEquivalentTo(new object[] { "a", 85, 913.12 }); + await Assert.That(result[1]).IsEquivalentTo(new object[] { "b", 519, 73.1 }); + await Assert.That(result[2]).IsEquivalentTo(new object[] { "c", 411, 529.6 }); + } + + [Test] + public async Task ReturnsNoTestDataWhenSourceReturnsNoTestData() + { + // Arrange + var source = new DelegatingDataSource + { + TestData = Array.Empty(), + }; + var fixture = new DelegatingFixture(); + var sut = new AutoDataSource(() => fixture, source); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray(); + + // Assert + await Assert.That(result).IsEmpty(); + } + + [Test] + public async Task ThrowsWhenSourceReturnsNull() + { + // Arrange + var source = new DelegatingDataSource { TestData = null! }; + var fixture = new DelegatingFixture(); + var sut = new AutoDataSource(() => fixture, source); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)) + .ToArray()) + .ThrowsException(); + } + + [Test] + public async Task DoesNotCustomizeFixtureWhenParametersNotCustomized() + { + // Arrange + var customizations = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizations.Add(c) + }; + var sut = new AutoDataSource(() => fixture); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray(); + + // Assert + await Assert.That(customizations).IsEmpty(); + } + + [Test] + public async Task CustomizesFixtureUsingParameterCustomizations() + { + // Arrange + var customizations = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizations.Add(c) + }; + var sut = new AutoDataSource(() => fixture); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithCustomizedParameter)); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray(); + + // Assert + await Assert.That(customizations).IsNotEmpty(); + } + + [Test] + public async Task CustomizationsAreAppliedInExpectedOrder() + { + // Arrange + var customizations = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizations.Add(c) + }; + var sut = new AutoDataSource(() => fixture); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleCustomizations)); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray(); + + // Assert + using var scope = Assert.Multiple(); + + await Assert.That(customizations[0]).IsAssignableTo(); + await Assert.That(customizations[1]).IsAssignableTo(); + var composite = await Assert.That(customizations[2]).IsAssignableTo(); + + var compositeCustomizations = composite.Customizations.ToArray(); + await Assert.That(compositeCustomizations.Length).IsEqualTo(2); + await Assert.That(compositeCustomizations[0]).IsAssignableTo(); + await Assert.That(compositeCustomizations[1]).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/ClassDataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/ClassDataSourceTests.cs new file mode 100644 index 0000000..5d9a363 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/ClassDataSourceTests.cs @@ -0,0 +1,138 @@ +using System.Collections; +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class ClassDataSourceTests +{ + [Test] + public async Task SutIsTestDataSource() + { + // Arrange & Act + var sut = new ClassDataSource(typeof(object)); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task ConstructorWithNullTypeThrows() + { + // Act & Assert + await Assert.That(() => _ = new ClassDataSource(null!)).ThrowsExactly(); + } + + [Test] + public async Task ConstructorWithNullParametersThrows() + { + // Act & Assert + await Assert.That(() => _ = new ClassDataSource(typeof(object), null!)).ThrowsExactly(); + } + + [Test] + public async Task TypeIsCorrect() + { + // Arrange + var expected = typeof(object); + var sut = new ClassDataSource(expected); + + // Act + var result = sut.Type; + + // Assert + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + public async Task ParametersIsCorrect() + { + // Arrange + var expected = new[] { new object() }; + var sut = new ClassDataSource(typeof(object), expected); + + // Act + var result = sut.Parameters; + + // Assert + await Assert.That(result).IsEquivalentTo(expected); + } + + [Test] + public async Task ThrowsWhenSourceIsNotEnumerable() + { + // Arrange + var sut = new ClassDataSource(typeof(object)); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)).ToArray()).ThrowsExactly(); + } + + [Test] + public async Task GeneratesTestDatWithPrimitiveValues() + { + // Arrange + var expected = new[] + { + new object[] { "hello", 1, new RecordType("world") }, + new object[] { "foo", 2, new RecordType("bar") }, + new object[] { "Han", 3, new RecordType("Solo") } + }; + var sut = new ClassDataSource(typeof(TestSourceWithMixedValues)); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + + // Act + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(actual).IsEquivalentTo(expected); + } + + private class TestSourceWithMixedValues : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return ["hello", 1, new RecordType("world")]; + yield return ["foo", 2, new RecordType("bar")]; + yield return ["Han", 3, new RecordType("Solo")]; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } + + [Test] + public async Task ThrowsWhenConstructorParametersDontMatch() + { + // Arrange + var parameters = new object[] { "a", 1 }; + var sut = new ClassDataSource(typeof(TestSourceWithMixedValues), parameters); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)).ToArray()).ThrowsException(); + } + + [Test] + public async Task AppliesExpectedConstructorParameters() + { + // Arrange + object[] parameters = [new object[] { "y", 25 }]; + var sut = new ClassDataSource(typeof(DelegatingTestData), parameters); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)).ToArray(); + + // Assert + await Assert.That(result.Single()).IsEquivalentTo(new object[] { "y", 25 }); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/DataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/DataSourceTests.cs new file mode 100644 index 0000000..25d8684 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/DataSourceTests.cs @@ -0,0 +1,111 @@ +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class DataSourceTests +{ + [Test] + public async Task SutIsTestDataSource() + { + // Arrange + var sut = new DelegatingDataSource(); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task ReturnSingleEmptyArrayWhenMethodHasNoParameters() + { + // Arrange + var sut = new DelegatingDataSource(); + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithoutParameters)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)).ToArray(); + + // Assert + var item = await Assert.That(result).HasSingleItem(); + + await Assert.That(item).IsEmpty(); + } + + [Test] + public async Task ThrowsWhenNoDataFoundForMethod() + { + // Arrange + var sut = new DelegatingDataSource { TestData = null! }; + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithSingleParameter)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)).ToArray()).ThrowsExactly(); + } + + [Test] + public async Task ReturnSingleArrayWithSingleItemWhenMethodHasSingleParameter() + { + // Arrange + var sut = new DelegatingDataSource + { + TestData = [["hello"]] + }; + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithSingleParameter)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)).ToArray(); + + // Assert + var testData = await Assert.That(result).HasSingleItem(); + var argument = await Assert.That(testData).HasSingleItem(); + await Assert.That(argument).IsEqualTo("hello"); + } + + [Test] + public async Task ReturnsArgumentsFittingTestParameters() + { + // Arrange + var testData = new[] + { + new object[] { "hello", 16, 32.86d }, + new object[] { null!, -1, -20.22 }, + new object[] { "one", 2 }, + new object[] { null! }, + new object[] { }, + }; + var sut = new DelegatingDataSource { TestData = testData }; + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + var actual = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(actual.Length).IsEqualTo(testData.Length); + + await Assert.That(actual) + .All() + .Satisfy( + assert => assert.Satisfies(y => y.Length, + y => y.IsBetween(0, 3).WithInclusiveBounds())); + } + + [Test] + public async Task ThrowsWhenTestDataContainsMoreArgumentsThanParameters() + { + // Arrange + var testData = new[] { new object[] { "hello", 16, 32.86d, "extra" } }; + var sut = new DelegatingDataSource { TestData = testData }; + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)).ToArray()).ThrowsExactly(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/FieldDataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/FieldDataSourceTests.cs new file mode 100644 index 0000000..2c0f953 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/FieldDataSourceTests.cs @@ -0,0 +1,110 @@ +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class FieldDataSourceTests +{ + public static IEnumerable TestDataFieldWithMixedValues = + [ + ["hello", 1, new FieldHolder { Field = "world" }], + ["foo", 2, new FieldHolder { Field = "bar" }], + ["Han", 3, new FieldHolder { Field = "Solo" }] + ]; + + public static object NonEnumerableField = new object(); + + [Test] + public async Task SutIsTestDataSource() + { + // Arrange + var sourceField = typeof(FieldDataSourceTests) + .GetField(nameof(TestDataFieldWithMixedValues)); + var sut = new FieldDataSource(sourceField); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task ThrowsWhenConstructedWithNullField() + { + // Act & Assert + await Assert.That(() => new FieldDataSource(null!)).ThrowsExactly(); + } + + [Test] + public async Task FieldIsCorrect() + { + // Arrange + var expected = typeof(FieldDataSourceTests) + .GetField(nameof(TestDataFieldWithMixedValues)); + var sut = new FieldDataSource(expected); + + // Act + var result = sut.FieldInfo; + + // Assert + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + public async Task ThrowsWhenInvokedWithNullTestMethod() + { + // Arrange + var sourceField = typeof(FieldDataSourceTests) + .GetField(nameof(TestDataFieldWithMixedValues)); + var sut = new FieldDataSource(sourceField); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(null!)).ThrowsExactly(); + } + + [Test] + public async Task ThrowsWhenSourceIsNotEnumerable() + { + // Arrange + var sourceField = typeof(FieldDataSourceTests) + .GetField(nameof(NonEnumerableField)); + var sut = new FieldDataSource(sourceField); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)).ToArray()).ThrowsExactly(); + } + + [Test] + public async Task GeneratesTestDataMatchingTestParameters() + { + // Arrange + var expected = new[] + { + new object[] { "hello", 1, new RecordType("world") }, + new object[] { "foo", 2, new RecordType("bar") }, + new object[] { "Han", 3, new RecordType("Solo") } + }; + var sourceField = typeof(FieldDataSourceTests) + .GetField(nameof(TestDataFieldWithRecordValues)); + var sut = new FieldDataSource(sourceField); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithRecordTypeParameter)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(result).IsEquivalentTo(expected); + } + + public static IEnumerable TestDataFieldWithRecordValues = + [ + ["hello", 1, new RecordType("world")], + ["foo", 2, new RecordType("bar")], + ["Han", 3, new RecordType("Solo")] + ]; +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/InlineDataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/InlineDataSourceTests.cs new file mode 100644 index 0000000..64a33bd --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/InlineDataSourceTests.cs @@ -0,0 +1,101 @@ +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class InlineDataSourceTests +{ + [Test] + public async Task SutIsTestDataSource() + { + // Arrange + // Act + var sut = new InlineDataSource([ + ]); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task InitializeWithNullValuesThrows() + { + // Arrange + // Act & Assert + await Assert.That(() => + new InlineDataSource(null!)).ThrowsExactly(); + } + + [Test] + public async Task ValuesIsCorrect() + { + // Arrange + var expectedValues = Array.Empty(); + var sut = new InlineDataSource(expectedValues); + // Act + var result = sut.Values; + // Assert + await Assert.That(result).IsEquivalentTo(expectedValues); + } + + [Test] + public async Task GetTestDataWithNullMethodThrows() + { + // Arrange + var sut = new InlineDataSource([ + ]); + // Act & Assert + await Assert.That(() => + sut.GenerateDataSources(null!)).ThrowsExactly(); + } + + [Test] + public async Task SourceThrowsWhenArgumentCountExceedParameterCount() + { + // Arrange + var values = new object[] { "aloha", 42, 12.3d, "extra" }; + var sut = new InlineDataSource(values); + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act & Assert + await Assert.That(() => + sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)) + .Select(x => x()).ToArray()) + .ThrowsExactly(); + } + + [Test] + public async Task ReturnsTestDataWhenArgumentCountMatchesParameterCount() + { + // Arrange + var values = new object[] { "aloha", 42, 12.3d }; + var sut = new InlineDataSource(values); + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)); + + // Assert + var testData = await Assert.That(result).HasSingleItem(); + await Assert.That(testData).IsEqualTo(values); + } + + [Test] + public async Task ReturnsAllArgumentsWhenArgumentCountLessThanParameterCount() + { + // Arrange + var values = new object[] { "aloha", 42 }; + var sut = new InlineDataSource(values); + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithMultipleParameters)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)); + + // Assert + var testData = await Assert.That(result).HasSingleItem(); + await Assert.That(testData).IsEqualTo(values); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/MemberDataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/MemberDataSourceTests.cs new file mode 100644 index 0000000..12b389d --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/MemberDataSourceTests.cs @@ -0,0 +1,113 @@ +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class MemberDataSourceTests +{ + public static object NonTestDataField = new(); + public static object NonTestDataProperty => new(); + public static object NonTestDataMethod() => new(); + public static IEnumerable EmptyTestDataField = Array.Empty(); + public static IEnumerable EmptyTestData => Array.Empty(); + public static IEnumerable GetEmptyTestData() => Array.Empty(); + + [Test] + public async Task SutIsTestDataSource() + { + // Arrange & Act + var sut = new MemberDataSource( + typeof(MemberDataSourceTests), + nameof(GetEmptyTestData)); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task ThrowsWhenTypeIsNull() + { + // Arrange + var method = nameof(GetEmptyTestData); + + // Act & Assert + await Assert.That(() => new MemberDataSource(null!, method)).ThrowsExactly(); + } + + [Test] + public async Task ThrowsWhenNameIsNull() + { + // Arrange + var type = typeof(MemberDataSourceTests); + + // Act & Assert + await Assert.That(() => new MemberDataSource(type, null!)).ThrowsExactly(); + } + + [Test] + public async Task ThrowsWhenArgumentsIsNull() + { + // Arrange + var type = typeof(MemberDataSourceTests); + var method = nameof(GetEmptyTestData); + + // Act & Assert + await Assert.That(() => new MemberDataSource(type, method, null!)).ThrowsExactly(); + } + + [Test] + public async Task InitializesTypeProperty() + { + // Arrange + var type = typeof(MemberDataSourceTests); + var method = nameof(GetEmptyTestData); + + // Act + var sut = new MemberDataSource(type, method); + + // Assert + await Assert.That(sut.Type).IsEqualTo(type); + await Assert.That(sut.Name).IsEqualTo(method); + await Assert.That(sut.Arguments).IsEmpty(); + } + + [Test] + [Arguments(nameof(EmptyTestDataField), typeof(FieldDataSource))] + [Arguments(nameof(EmptyTestData), typeof(PropertyDataSource))] + [Arguments(nameof(GetEmptyTestData), typeof(MethodDataSource))] + public async Task InitializesSourceProperty(string memberName, Type expectedInnerSourceType) + { + // Arrange + var type = typeof(MemberDataSourceTests); + + // Act + var sut = new DelegatingMemberDataSource(type, memberName); + + // Assert + await Assert.That(sut.GetSource()).IsTypeOf(expectedInnerSourceType); + } + + [Test] + public async Task ThrowsWhenSourceDoesNotExist() + { + // Arrange + var type = typeof(MemberDataSourceTests); + + // Act & Assert + await Assert.That(() => _ = new DelegatingMemberDataSource(type, "NonExistentMember")).ThrowsExactly(); + } + + [Test] + [Arguments(nameof(NonTestDataField))] + [Arguments(nameof(NonTestDataProperty))] + [Arguments(nameof(NonTestDataMethod))] + public async Task ThrowsWhenSourceDoesNotReturnTestData(string memberName) + { + // Arrange + var type = typeof(MemberDataSourceTests); + + // Act & Assert + await Assert.That(() => _ = new DelegatingMemberDataSource(type, memberName)).ThrowsExactly(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/MethodDataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/MethodDataSourceTests.cs new file mode 100644 index 0000000..42aceb6 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/MethodDataSourceTests.cs @@ -0,0 +1,106 @@ +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class MethodDataSourceTests +{ + public static IEnumerable GetTestDataFieldWithMixedValues() + { + yield return ["hello", 1, new RecordType("world")]; + yield return ["foo", 2, new RecordType("bar")]; + yield return ["Han", 3, new RecordType("Solo")]; + } + + [Test] + public async Task SutIsTestDataSource() + { + // Arrange + var methodInfo = typeof(MethodDataSourceTests) + .GetMethod(nameof(this.SutIsTestDataSource)); + + // Act + var sut = new MethodDataSource(methodInfo); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task ThrowsWhenMethodInfoIsNull() + { + // Act & Assert + await Assert.That(() => + new MethodDataSource(null!)).ThrowsExactly(); + } + + [Test] + public async Task ThrowsWhenArgumentsIsNull() + { + // Arrange + var methodInfo = typeof(MethodDataSourceTests) + .GetMethod(nameof(this.SutIsTestDataSource)); + + // Act & Assert + await Assert.That(() => + new MethodDataSource(methodInfo, null!)).ThrowsExactly(); + } + + [Test] + public async Task ConstructorSetsProperties() + { + // Arrange + var methodInfo = typeof(MethodDataSourceTests) + .GetMethod(nameof(this.SutIsTestDataSource)); + var arguments = new[] { new object() }; + + // Act + var sut = new MethodDataSource(methodInfo, arguments); + + // Assert + await Assert.That(sut.MethodInfo).IsEqualTo(methodInfo); + await Assert.That(sut.Arguments).IsEquivalentTo(arguments); + } + + [Test] + public async Task GetTestDataInvokesMethodInfo() + { + // Arrange + var expected = new[] + { + new object[] { "hello", 1, new RecordType("world") }, + new object[] { "foo", 2, new RecordType("bar") }, + new object[] { "Han", 3, new RecordType("Solo") } + }; + var testDataSource = typeof(MethodDataSourceTests) + .GetMethod(nameof(GetTestDataFieldWithMixedValues)); + var testData = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + var sut = new MethodDataSource(testDataSource); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testData)) + .Select(x => x()); + + // Assert + await Assert.That(result).IsEquivalentTo(expected); + } + + [Test] + public async Task ThrowsWhenMemberDoesNotReturnAnEnumerableValue() + { + // Arrange + var dataSource = typeof(MethodDataSourceTests) + .GetMethod(nameof(NonEnumerableTestData)); + var testData = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + var sut = new MethodDataSource(dataSource); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testData)).ToArray()).ThrowsExactly(); + } + + public static object NonEnumerableTestData() => new(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Internal/PropertyDataSourceTests.cs b/tests/AutoFixture.TUnit.Tests/Internal/PropertyDataSourceTests.cs new file mode 100644 index 0000000..a7ae04c --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Internal/PropertyDataSourceTests.cs @@ -0,0 +1,144 @@ +using AutoFixture.TUnit.Internal; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests.Internal; + +public class PropertyDataSourceTests +{ + [Test] + public async Task SutIsTestDataSource() + { + // Arrange + var sourceProperty = typeof(PropertyDataSourceTests) + .GetProperty(nameof(TestDataPropertyWithMixedValues)); + var sut = new PropertyDataSource(sourceProperty); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + public static IEnumerable TestDataPropertyWithMixedValues => + [ + ["hello", 1, new PropertyHolder { Property = "world" }], + ["foo", 2, new PropertyHolder { Property = "bar" }], + ["Han", 3, new PropertyHolder { Property = "Solo" }] + ]; + + public static object NonEnumerableProperty => new object(); + + [Test] + public async Task ThrowsWhenConstructedWithNullProperty() + { + // Act & Assert + await Assert.That(() => new PropertyDataSource(null!)).ThrowsExactly(); + } + + [Test] + public async Task PropertyIsCorrect() + { + // Arrange + var expected = typeof(PropertyDataSourceTests) + .GetProperty(nameof(TestDataPropertyWithMixedValues)); + var sut = new PropertyDataSource(expected); + + // Act + var result = sut.PropertyInfo; + + // Assert + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + public async Task ThrowsWhenInvokedWithNullTestMethod() + { + // Arrange + var sourceProperty = typeof(PropertyDataSourceTests) + .GetProperty(nameof(TestDataPropertyWithMixedValues)); + var sut = new PropertyDataSource(sourceProperty); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(null!)).ThrowsExactly(); + } + + [Test] + public async Task ThrowsWhenSourceIsNotEnumerable() + { + // Arrange + var sourceProperty = typeof(PropertyDataSourceTests) + .GetProperty(nameof(NonEnumerableProperty)); + var sut = new PropertyDataSource(sourceProperty); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithReferenceTypeParameter)); + + // Act & Assert + await Assert.That(() => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)) + + .ToArray()).ThrowsExactly(); + } + + [Test] + public async Task GeneratesTestDataMatchingTestParameters() + { + // Arrange + var expected = new[] + { + new object[] { "hello", 1, new RecordType("world") }, + new object[] { "foo", 2, new RecordType("bar") }, + new object[] { "Han", 3, new RecordType("Solo") } + }; + var sourceProperty = typeof(PropertyDataSourceTests) + .GetProperty(nameof(TestDataPropertyWithRecordValues)); + var sut = new PropertyDataSource(sourceProperty); + var method = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithRecordTypeParameter)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(result).IsEquivalentTo(expected); + } + + public static IEnumerable TestDataPropertyWithRecordValues => + [ + ["hello", 1, new RecordType("world")], + ["foo", 2, new RecordType("bar")], + ["Han", 3, new RecordType("Solo")] + ]; + + [Test] + public async Task ReturnsNullArguments() + { + // Arrange + var expected = new[] + { + new object[] { null!, 1, null! }, + new object[] { null!, 2, null! }, + new object[] { null!, 3, null! } + }; + var sourceProperty = typeof(PropertyDataSourceTests) + .GetProperty(nameof(TestDataPropertyWithNullValues)); + var sut = new PropertyDataSource(sourceProperty); + var testMethod = typeof(SampleTestType) + .GetMethod(nameof(SampleTestType.TestMethodWithRecordTypeParameter)); + + // Act + var result = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(result).IsEquivalentTo(expected); + } + + public static IEnumerable TestDataPropertyWithNullValues => + [ + [null!, 1, null!], + [null!, 2, null!], + [null!, 3, null!] + ]; +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/MemberAutoDataAttributeScenarioTests.cs b/tests/AutoFixture.TUnit.Tests/MemberAutoDataAttributeScenarioTests.cs new file mode 100644 index 0000000..3430829 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/MemberAutoDataAttributeScenarioTests.cs @@ -0,0 +1,161 @@ +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests; + +public class MemberAutoDataAttributeScenarioTests +{ + [Test] + [MemberAutoData(nameof(GetSingleStringValueTestData))] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetEmptyTestData))] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetSingleStringValueTestData))] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetStringTestsFromArgument), + parameters: "argument")] + public async Task SingleStringValueTest(string value) + { + await Assert.That(value).IsNotNull(); + await Assert.That(value).IsNotEmpty(); + await Assert.That(string.IsNullOrWhiteSpace(value)).IsFalse(); + } + + [Test] + [MemberAutoData(nameof(GetMultipleValueTestData))] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetMultipleValueTestData))] + public async Task MultipleValueTest(string a, int b, decimal c) + { + await Assert.That(a).IsNotNull(); + await Assert.That(a).IsNotEmpty(); + await Assert.That(string.IsNullOrWhiteSpace(a)).IsFalse(); + + await Assert.That(b != 0).IsTrue().Because("Value should not be default"); + await Assert.That(c != 0).IsTrue().Because("Value should not be default"); + } + + [Test] + [MemberAutoData(nameof(GetSingleStringValueTestData))] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetSingleStringValueTestData))] + public async Task FreezesUninjectedValues( + string a, [Frozen] string b, string c, + PropertyHolder d) + { + // Assert "a" ends with any possible ending from the test data + var aSuffix = a.Split('-').Last(); + await Assert.That(new[] { "one", "two", "three" }).Contains(x => aSuffix == x); + + await Assert.That(b).IsNotNull(); + await Assert.That(b).IsNotEmpty(); + await Assert.That(string.IsNullOrWhiteSpace(b)).IsFalse(); + + await Assert.That(c).IsEqualTo(b); + + await Assert.That(d).IsNotNull(); + await Assert.That(d.Property).IsEqualTo(b); + } + + [Test] + [MemberAutoData(nameof(GetMultipleValueTestData))] + public async Task InjectsValues([Frozen] string a, + [Frozen] int b, + [Frozen] decimal c, + PropertyHolder a1, + PropertyHolder b1, + PropertyHolder c1) + { + // Assert "a" ends with any possible ending from the test data + var aSuffix = a.Split('-').Last(); + await Assert.That(new[] { "one", "two", "three" }).Contains(x => aSuffix == x); + + await Assert.That(new[] { 22, 75, 19 }).Contains(x => x == b); + await Assert.That(new[] { 25.7m, 228.1m, 137.09m }).Contains(x => x == c); + + await Assert.That(a1).IsNotNull(); + await Assert.That(a1.Property).IsEqualTo(a); + + await Assert.That(b1).IsNotNull(); + await Assert.That(b1.Property).IsEqualTo(b); + + await Assert.That(c1).IsNotNull(); + await Assert.That(c1.Property).IsEqualTo(c); + } + + [Test] + [MemberAutoData(nameof(GetStringValuesTestData))] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetStringValuesTestData))] + public async Task DoesNotAlterTestDataValuesWhenFrozen( + [Frozen] string a, string b, string c) + { + var aSuffix = a.Split('-').Last(); + var bSuffix = b.Split('-').Last(); + await Assert.That(new[] { "one", "two", "three" }).Contains(x => aSuffix == x); + await Assert.That(new[] { "uno", "dos", "tres" }).Contains(x => bSuffix == x); + + await Assert.That(b).IsNotEqualTo(a); + await Assert.That(c).IsEqualTo(a); + } + + [Test] + [MemberAutoData(nameof(GetStringValuesTestData))] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetStringValuesTestData))] + public async Task LastInjectedValueIsFrozen( + [Frozen] string a, [Frozen] string b, string c) + { + var aSuffix = a.Split('-').Last(); + var bSuffix = b.Split('-').Last(); + await Assert.That(new[] { "one", "two", "three" }).Contains(x => aSuffix == x); + await Assert.That(new[] { "uno", "dos", "tres" }).Contains(x => bSuffix == x); + + await Assert.That(c).IsNotEqualTo(a); + await Assert.That(c).IsEqualTo(b); + } + + [Test] + [MemberAutoData( + memberType: typeof(TestTypeWithMethodData), + memberName: nameof(TestTypeWithMethodData.GetTestWithComplexTypesData))] + public async Task InjectsComplexTypes( + [Frozen] PropertyHolder a, + PropertyHolder b, + PropertyHolder c) + { + await Assert.That(a).IsNotNull(); + await Assert.That(b).IsNotNull(); + + await Assert.That(b).IsNotSameReferenceAs(a); + await Assert.That(c).IsSameReferenceAs(a); + } + + public static IEnumerable GetSingleStringValueTestData() + { + yield return ["test-one"]; + yield return ["test-two"]; + yield return ["test-three"]; + } + + public static IEnumerable GetMultipleValueTestData() + { + yield return ["test-one", 22, 25.7m]; + yield return ["test-two", 75, 228.1m]; + yield return ["test-three", 19, 137.09m]; + } + + public static IEnumerable GetStringValuesTestData() + { + yield return ["test-one", "test-uno"]; + yield return ["test-two", "test-dos"]; + yield return ["test-three", "test-tres"]; + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/MemberAutoDataAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/MemberAutoDataAttributeTest.cs new file mode 100644 index 0000000..940365b --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/MemberAutoDataAttributeTest.cs @@ -0,0 +1,406 @@ +using System.Diagnostics.CodeAnalysis; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +[SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Local", + Justification = "Using parameter for precondition checks is acceptable in assertions.")] +public class MemberAutoDataAttributeTest +{ + [Test] + public async Task SutIsDataAttribute() + { + // Arrange + var memberName = Guid.NewGuid().ToString(); + + // Act + var sut = new MemberAutoDataAttribute(memberName); + + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task InitializedWithMemberNameAndParameters() + { + // Arrange + var memberName = Guid.NewGuid().ToString(); + var parameters = new object[] { "value-one", 3, 12.2f }; + + // Act + var sut = new MemberAutoDataAttribute(memberName, parameters); + + // Assert + await Assert.That(sut.MemberName).IsEqualTo(memberName); + await Assert.That(sut.Parameters).IsEqualTo(parameters); + await Assert.That(sut.MemberType).IsNull(); + await Assert.That(sut.FixtureFactory).IsNotNull(); + } + + [Test] + public async Task InitializedWithTypeMemberNameAndParameters() + { + // Arrange + var memberName = Guid.NewGuid().ToString(); + var parameters = new object[] { "value-one", 3, 12.2f }; + var testType = typeof(MemberAutoDataAttributeTest); + + // Act + var sut = new MemberAutoDataAttribute(testType, memberName, parameters); + + // Assert + await Assert.That(sut.MemberName).IsEqualTo(memberName); + await Assert.That(sut.Parameters).IsEqualTo(parameters); + await Assert.That(sut.MemberType).IsEqualTo(testType); + await Assert.That(sut.FixtureFactory).IsNotNull(); + } + + [Test] + public async Task ThrowsWhenInitializedWithNullMemberName() + { + // Act & Assert + await Assert.That(() => new MemberAutoDataAttribute(null!)).ThrowsExactly(); + } + + [Test] + public async Task TreatsNullParametersAsArrayWithNullValue() + { + // Arrange + var memberName = Guid.NewGuid().ToString(); + + // Act + var actual = new MemberAutoDataAttribute(memberName, null!); + + // Act & Assert + var value = await Assert.That(actual.Parameters).HasSingleItem(); + await Assert.That(value).IsNull(); + } + + [Test] + public void DoesNotThrowWhenInitializedWithNullType() + { + // Arrange + var memberName = Guid.NewGuid().ToString(); + + // Act & Assert + _ = new MemberAutoDataAttribute(null!, memberName); + } + + [Test] + public void ThrowsWhenTestMethodNull() + { + // Arrange + var sut = new MemberAutoDataAttribute("memberName"); + + // Act & Assert + Assert.Throws( + () => sut.GenerateDataSources(null!).Select(x => x()).ToArray()); + } + + [Test] + public async Task ThrowsWhenMemberNotEnumerable() + { + // Arrange + var memberName = nameof(TestTypeWithMethodData.NonEnumerableMethod); + var sut = new MemberAutoDataAttribute(memberName); + var method = TestTypeWithMethodData.GetNonEnumerableMethodInfo(); + + // Act & Assert + var ex = Assert.Throws( + () => sut.GetData(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray()); + + await Assert.That(ex.Message).Contains(memberName); + } + + [Test] + public async Task ThrowsWhenMemberNotStatic() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.NonStaticSource); + var sut = new MemberAutoDataAttribute(memberName); + var method = TestTypeWithMethodData.GetNonStaticSourceMethodInfo(); + + // Act & Assert + var ex = Assert.Throws( + () => sut.GetData(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)).ToArray()); + + await Assert.That(ex.Message).Contains(memberName); + } + + [Test] + public async Task ThrowsWhenMemberDoesNotExist() + { + // Arrange + var memberName = Guid.NewGuid().ToString(); + var sut = new MemberAutoDataAttribute(typeof(TestTypeWithMethodData), memberName); + var method = TestTypeWithMethodData.GetMultipleValueTestMethodInfo(); + + // Act & Assert + var ex = Assert.Throws( + () => sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)) + .Select(x => x()) + .ToArray()); + await Assert.That(ex.Message).Contains(memberName); + } + + [Test] + public async Task DoesntActivateFixtureImmediately() + { + // Arrange + var memberName = Guid.NewGuid().ToString(); + var wasInvoked = false; + + // Act + _ = new DerivedMemberAutoDataAttribute(() => + { + wasInvoked = true; + return new DelegatingFixture(); + }, memberName); + + // Assert + await Assert.That(wasInvoked).IsFalse(); + } + + [Test] + [Arguments("CreateWithFrozenAndFavorArrays")] + [Arguments("CreateWithFavorArraysAndFrozen")] + [Arguments("CreateWithFrozenAndFavorEnumerables")] + [Arguments("CreateWithFavorEnumerablesAndFrozen")] + [Arguments("CreateWithFrozenAndFavorLists")] + [Arguments("CreateWithFavorListsAndFrozen")] + [Arguments("CreateWithFrozenAndGreedy")] + [Arguments("CreateWithGreedyAndFrozen")] + [Arguments("CreateWithFrozenAndModest")] + [Arguments("CreateWithModestAndFrozen")] + [Arguments("CreateWithFrozenAndNoAutoProperties")] + [Arguments("CreateWithNoAutoPropertiesAndFrozen")] + public async Task GetDataOrdersCustomizationAttributes(string methodName) + { + // Arrange + var method = typeof(TypeWithCustomizationAttributes) + .GetMethod(methodName, [typeof(ConcreteType)])!; + var customizationLog = new List(); + var fixture = new DelegatingFixture + { + OnCustomize = c => customizationLog.Add(c) + }; + + var sut = new DerivedMemberAutoDataAttribute( + () => fixture, + typeof(TestTypeWithMethodData), + nameof(TestTypeWithMethodData.TestDataWithNoValues)); + + // Act + _ = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(method!)) + .Select(x => x()) + .ToArray(); + + // Assert + var composite = await Assert.That(customizationLog[0]).IsAssignableTo(); + await Assert.That(composite.Customizations.First()).IsNotTypeOf(); + await Assert.That(composite.Customizations.Last()).IsAssignableTo(); + } + + [Test] + public async Task GeneratesTestsFromParameterlessMethod() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.GetSingleStringValueTestData); + var sut = new MemberAutoDataAttribute(memberName); + var testMethod = TestTypeWithMethodData.GetSingleStringValueTestMethodInfo(); + var expected = new[] + { + new object[] { "value-one" }, + new object[] { "value-two" }, + new object[] { "value-three" } + }; + + // Act + var testData = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(testData).IsEquivalentTo(expected); + } + + [Test] + public async Task GeneratesTestsFromMethodWithParameter() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.GetStringTestsFromArgument); + var sut = new MemberAutoDataAttribute(memberName, "value"); + var testMethod = TestTypeWithMethodData.GetStringTestsFromArgumentMethodInfo(); + var expected = new[] + { + new object[] { "value-one" }, + new object[] { "value-two" }, + new object[] { "value-three" } + }; + + // Act + var testData = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + await Assert.That(testData).IsEquivalentTo(expected); + } + + [Test] + public async Task GeneratesTestDataForTestsWithMultipleParameters() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.GetMultipleValueTestData); + var sut = new MemberAutoDataAttribute(memberName); + var testMethod = TestTypeWithMethodData.GetMultipleValueTestMethodInfo(); + var expected = new[] + { + new object[] { "value-one", 12, 23.3m }, + new object[] { "value-two", 38, 12.7m }, + new object[] { "value-three", 94, 52.21m } + }; + + // Act + var testData = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(testData).IsEquivalentTo(expected); + } + + [Test] + public async Task GeneratesMissingDataForTestsWithMultipleParameters() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.GetSingleStringValueTestData); + var sut = new MemberAutoDataAttribute(memberName); + var testMethod = TestTypeWithMethodData.GetMultipleValueTestMethodInfo(); + + // Act + var testData = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + var arguments1 = testData[0]; + var arguments2 = testData[1]; + var arguments3 = testData[2]; + + // Assert + await Assert.That(arguments1.Length).IsEqualTo(3); + await Assert.That(arguments1[0]).IsEqualTo("value-one"); + await Assert.That(arguments1[1]).IsNotEqualTo(0); + await Assert.That(arguments1[2]).IsNotEqualTo(0); + + await Assert.That(arguments2.Length).IsEqualTo(3); + await Assert.That(arguments2[0]).IsEqualTo("value-two"); + await Assert.That(arguments2[1]).IsNotEqualTo(0); + await Assert.That(arguments2[2]).IsNotEqualTo(0); + + await Assert.That(arguments3.Length).IsEqualTo(3); + await Assert.That(arguments3[0]).IsEqualTo("value-three"); + await Assert.That(arguments3[1]).IsNotEqualTo(0); + await Assert.That(arguments3[2]).IsNotEqualTo(0); + } + + [Test] + public async Task GeneratesTestDataWithInjectedParameters() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.GetDataForTestWithFrozenParameter); + var sut = new MemberAutoDataAttribute(memberName); + var testMethod = TestTypeWithMethodData.GetTestWithFrozenParameter(); + var expected = new[] + { + new object[] { "value-one", "value-two", "value-two" }, + new object[] { "value-two", "value-three", "value-three" }, + new object[] { "value-three", "value-one", "value-one" } + }; + + // Act + var testData = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(testData).IsEquivalentTo(expected); + } + + [Test] + public async Task AutoGeneratesValuesForFrozenParameters() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.GetSingleStringValueTestData); + var sut = new MemberAutoDataAttribute(memberName); + var testMethod = TestTypeWithMethodData.GetTestWithFrozenParameter(); + + // Act + var testData = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + var arguments1 = testData[0]; + var arguments2 = testData[1]; + var arguments3 = testData[2]; + + // Assert + await Assert.That(arguments1.Length).IsEqualTo(3); + await Assert.That(arguments1[0]).IsEqualTo("value-one"); + await Assert.That(arguments1[1].ToString()!).IsNotEmpty(); + await Assert.That(arguments1[2]).IsEqualTo(arguments1[1]); + + await Assert.That(arguments2.Length).IsEqualTo(3); + await Assert.That(arguments2[0]).IsEqualTo("value-two"); + await Assert.That(arguments2[1].ToString()!).IsNotEmpty(); + await Assert.That(arguments2[2]).IsEqualTo(arguments2[1]); + + await Assert.That(arguments3.Length).IsEqualTo(3); + await Assert.That(arguments3[0]).IsEqualTo("value-three"); + await Assert.That(arguments3[1].ToString()!).IsNotEmpty(); + await Assert.That(arguments3[2]).IsEqualTo(arguments3[1]); + } + + [Test] + public async Task SupportsInheritedTestDataMembers() + { + // Arrange + const string memberName = nameof(TestTypeWithMethodData.GetMultipleValueTestData); + var sut = new MemberAutoDataAttribute(memberName); + var testMethod = ChildTestTypeMethodData.GetMultipleValueTestMethodInfo(); + var expected = new[] + { + new object[] { "value-one", 12, 23.3m }, + new object[] { "value-two", 38, 12.7m }, + new object[] { "value-three", 94, 52.21m } + }; + + // Act + var testData = sut.GenerateDataSources(DataGeneratorMetadataHelper.CreateDataGeneratorMetadata(testMethod!)) + .Select(x => x()) + .ToArray(); + + // Assert + await Assert.That(testData).IsEquivalentTo(expected); + } + + public static IEnumerable TestDataWithNullValues + { + get + { + yield return [null!, null!]; + yield return [string.Empty, null!]; + yield return [" ", null!]; + } + } + + [Test] + [MemberAutoData(nameof(TestDataWithNullValues))] + public async Task NullTestDataReturned(string a, string b, PropertyHolder c) + { + await Assert.That(string.IsNullOrWhiteSpace(a)).IsTrue(); + await Assert.That(b).IsNull(); + await Assert.That(c).IsNotNull(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/ModestAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/ModestAttributeTest.cs new file mode 100644 index 0000000..f0eb7d7 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/ModestAttributeTest.cs @@ -0,0 +1,45 @@ +using AutoFixture.Kernel; +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class ModestAttributeTest +{ + [Test] + public async Task SutIsAttribute() + { + // Arrange & Act + var sut = new ModestAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task GetCustomizationFromNullParameterThrows() + { + // Arrange + var sut = new ModestAttribute(); + // Act & assert + await Assert.That(() => + sut.GetCustomization(null!)).ThrowsExactly(); + } + + [Test] + public async Task GetCustomizationReturnsCorrectResult() + { + // Arrange + var sut = new ModestAttribute(); + var parameter = typeof(TypeWithOverloadedMembers) + .GetMethod(nameof(TypeWithOverloadedMembers.DoSomething), [typeof(object)])! + .GetParameters().Single(); + + // Act + var result = sut.GetCustomization(parameter); + + // Assert + var invoker = await Assert.That(result).IsAssignableTo(); + await Assert.That(invoker?.TargetType).IsEqualTo(parameter.ParameterType); + await Assert.That(invoker?.Query).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/NoAutoPropertiesAttributeTest.cs b/tests/AutoFixture.TUnit.Tests/NoAutoPropertiesAttributeTest.cs new file mode 100644 index 0000000..7d94d55 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/NoAutoPropertiesAttributeTest.cs @@ -0,0 +1,41 @@ +using TestTypeFoundation; +using TUnit.Assertions.AssertConditions.Throws; + +namespace AutoFixture.TUnit.Tests; + +public class NoAutoPropertiesAttributeTest +{ + [Test] + public async Task SutIsAttribute() + { + // Arrange + // Act + var sut = new NoAutoPropertiesAttribute(); + // Assert + await Assert.That(sut).IsAssignableTo(); + } + + [Test] + public async Task GetCustomizationFromNullParameterThrows() + { + // Arrange + var sut = new NoAutoPropertiesAttribute(); + // Act & assert + await Assert.That(() => + sut.GetCustomization(null!)).ThrowsExactly(); + } + + [Test] + public async Task GetCustomizationReturnsTheCorrectResult() + { + // Arrange + var sut = new NoAutoPropertiesAttribute(); + var parameter = TypeWithOverloadedMembers + .GetDoSomethingMethod(typeof(object)) + .GetParameters().Single(); + // Act + var result = sut.GetCustomization(parameter); + // Assert + await Assert.That(result).IsAssignableTo(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/Scenario.cs b/tests/AutoFixture.TUnit.Tests/Scenario.cs new file mode 100644 index 0000000..9ee0a55 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Scenario.cs @@ -0,0 +1,562 @@ +using System.Collections; +using System.ComponentModel.DataAnnotations; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests; + +public class Scenario +{ + [Test, AutoData] + public async Task AutoDataProvidesCorrectInteger(int primitiveValue) + { + await Assert.That(primitiveValue).IsNotEqualTo(0); + } + + [Test, AutoData] + public async Task AutoDataProvidesCorrectString(string text) + { + await Assert.That(text).StartsWith("text"); + } + + [Test, AutoData] + public async Task AutoDataProvidesCorrectObject(PropertyHolder ph) + { + await Assert.That(ph).IsNotNull(); + await Assert.That(ph.Property).IsNotNull(); + } + + [Test, AutoData] + public async Task AutoDataProvidesMultipleObjects(PropertyHolder ph, SingleParameterType spt) + { + await Assert.That(ph).IsNotNull(); + await Assert.That(ph.Property).IsNotNull(); + + await Assert.That(spt).IsNotNull(); + await Assert.That(spt.Parameter).IsNotNull(); + } + + [Test] + [ArgumentsAutoData("foo")] + [ArgumentsAutoData("foo", "bar")] + public async Task InlineAutoDataUsesSuppliedDataValues(string s1, string s2) + { + await Assert.That(s1).IsEqualTo("foo"); + await Assert.That(s2).IsNotNull(); + } + + [Test] + [ArgumentsAutoData("foo")] + [ArgumentsAutoData("foo", "bar")] + public async Task InlineAutoDataSuppliesDataSpecimens(string s1, string s2, MyClass myClass) + { + await Assert.That(s1).IsEqualTo("foo"); + await Assert.That(s2).IsNotNull(); + await Assert.That(myClass).IsNotNull(); + } + + [Test] + [ArgumentsAutoData("foo")] + [ArgumentsAutoData("foo", "bar")] + public async Task InlineAutoDataSuppliesDataSpecimensOnlyForNonProvidedValues(string s1, string s2, string s3) + { + await Assert.That(s1).IsEqualTo("foo"); + await Assert.That(s2).IsNotNull(); + await Assert.That(s3).IsNotEqualTo("foo"); + await Assert.That(s3).IsNotEqualTo("bar"); + } + + // This test and its associated types is used to document one of the + // InlineAutoDataAttribute constructor overloads. + [Test] + [MyCustomArgumentsAutoData(1337)] + [MyCustomArgumentsAutoData(1337, 7)] + [MyCustomArgumentsAutoData(1337, 7, 42)] +#pragma warning disable xUnit1026 // Test methods should use all of their parameters - it's required by the test logic. + public async Task CustomInlineDataSuppliesExtraValues(int x, int y, int z) +#pragma warning restore xUnit1026 // Test methods should use all of their parameters + { + await Assert.That(x).IsEqualTo(1337); + + // y can vary, so we can't express any meaningful assertion for it. + await Assert.That(z).IsEqualTo(42); + } + + public class MyCustomArgumentsAutoDataAttribute + : ArgumentsAutoDataAttribute + { + public MyCustomArgumentsAutoDataAttribute(params object[] values) + : base(() => new Fixture().Customize(new TheAnswer()), values) + { + } + } + + [Test, MemberAutoData(nameof(StringData))] + public async Task MemberAutoDataUsesSuppliedDataValues(string s1, string s2) + { + await Assert.That(s1).IsEqualTo("foo"); + await Assert.That(s2).IsNotNull(); + } + + [Test, MemberAutoData(nameof(StringData))] + public async Task MemberAutoDataSuppliesDataSpecimens(string s1, string s2, MyClass myClass) + { + await Assert.That(s1).IsEqualTo("foo"); + await Assert.That(s2).IsNotNull(); + await Assert.That(myClass).IsNotNull(); + } + + [Test, MemberAutoData(nameof(StringData))] + public async Task MemberAutoDataSuppliesDataSpecimensOnlyForNonProvidedValues(string s1, string s2, string s3) + { + await Assert.That(s1).IsEqualTo("foo"); + await Assert.That(s2).IsNotNull(); + await Assert.That(s3).IsNotEqualTo("foo"); + await Assert.That(s3).IsNotEqualTo("bar"); + } + + [Test, MemberAutoData(nameof(GetParametrizedData), 21, 38, 43)] + public async Task MemberAutoDataCanBeParametrized(int x, int y, int z) + { + await Assert.That(x).IsEqualTo(21); + await Assert.That(y).IsEqualTo(38); + await Assert.That(z).IsEqualTo(43); + } + + [Test, MyCustomMemberAutoData(nameof(IntData))] + public async Task CustomMemberAutoDataSuppliesExtraValues(int x, int y, int z) + { + await Assert.That(x).IsEqualTo(1337); + await Assert.That(y).IsNotEqualTo(0); + await Assert.That(z).IsEqualTo(42); + } + + [Test, MyCustomMemberAutoData(nameof(GetParametrizedData), 21, 38, 43)] + public async Task CustomMemberAutoDataCanBeParametrized(int x, int y, int z) + { + await Assert.That(x).IsEqualTo(21); + await Assert.That(y).IsEqualTo(38); + await Assert.That(z).IsEqualTo(43); + } + + public static IEnumerable StringData + { + get + { + yield return ["foo"]; + yield return ["foo", "bar"]; + } + } + + public static IEnumerable IntData + { + get + { + yield return [1337]; + yield return [1337, 7]; + yield return [1337, 7, 42]; + } + } + + public static IEnumerable GetParametrizedData(int x, int y, int z) + { + yield return [x, y, z]; + } + + public class MyCustomMemberAutoDataAttribute + : MemberAutoDataAttribute + { + public MyCustomMemberAutoDataAttribute(string memberName, params object[] parameters) + : base(() => new Fixture().Customize(new TheAnswer()), memberName, parameters) + { + } + } + + private class TheAnswer : ICustomization + { + public void Customize(IFixture fixture) + { + fixture.Inject(42); + } + } + + [Test, AutoData] + public async Task FreezeFirstParameter([Frozen] Guid g1, Guid g2) + { + await Assert.That(g2).IsEqualTo(g1); + } + + [Test, AutoData] + public async Task FreezeSecondParameterOnlyFreezesSubsequentParameters(Guid g1, [Frozen] Guid g2, Guid g3) + { + await Assert.That(g2).IsNotEqualTo(g1); + await Assert.That(g3).IsNotEqualTo(g1); + + await Assert.That(g3).IsEqualTo(g2); + } + + [Test, AutoData] + public async Task IntroductoryTest(int expectedNumber, MyClass sut) + { + // Arrange + // Act + int result = sut.Echo(expectedNumber); + + // Assert + await Assert.That(result).IsEqualTo(expectedNumber); + } + + [Test, AutoData] + public async Task ModestCreatesParameterWithModestConstructor([Modest] MultiUnorderedConstructorType p) + { + await Assert.That(string.IsNullOrEmpty(p.Text)).IsTrue(); + await Assert.That(p.Number).IsEqualTo(0); + } + + [Test, AutoData] + public async Task GreedyCreatesParameterWithGreedyConstructor([Greedy] MultiUnorderedConstructorType p) + { + await Assert.That(string.IsNullOrEmpty(p.Text)).IsFalse(); + await Assert.That(p.Number).IsNotEqualTo(0); + } + + [Test, AutoData] + public async Task BothFrozenAndGreedyAttributesCanBeAppliedToSameParameter( + [Frozen][Greedy] MultiUnorderedConstructorType p1, MultiUnorderedConstructorType p2) + { + await Assert.That(p1).IsNotNull(); + await Assert.That(string.IsNullOrEmpty(p2.Text)).IsFalse(); + await Assert.That(p2.Number).IsNotEqualTo(0); + } + + [Test, AutoData] + public async Task FavorArraysCausesArrayConstructorToBeInjectedWithFrozenItems([Frozen] int[] numbers, + [FavorArrays] ItemContainer container) + { + await Assert.That(numbers.SequenceEqual(container.Items)).IsTrue(); + } + + [Test, AutoData] + public async Task FreezeFirstParameterShouldAssignSameInstanceToSecondParameter([Frozen] string p1, + string p2) + { + await Assert.That(p2).IsEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByExactTypeShouldAssignSameInstanceToSecondParameter( + [Frozen(Matching.ExactType)] ConcreteType p1, + ConcreteType p2) + { + await Assert.That(p2).IsEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByExactTypeShouldNotAssignSameInstanceToSecondParameterOfDifferentType( + [Frozen(Matching.ExactType)] ConcreteType p1, + object p2) + { + await Assert.That(p2).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByDirectBaseTypeShouldAssignSameInstanceToSecondParameter( + [Frozen(Matching.DirectBaseType)] ConcreteType p1, + AbstractType p2) + { + await Assert.That(p2).IsEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByDirectBaseTypeShouldNotAssignSameInstanceToSecondParameterOfIndirectBaseType( + [Frozen(Matching.DirectBaseType)] ConcreteType p1, + object p2) + { + await Assert.That(p2).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByDirectBaseTypeShouldNotAssignSameInstanceToSecondParameterOfSameType( + [Frozen(Matching.DirectBaseType)] ConcreteType p1, + ConcreteType p2) + { + await Assert.That(p2).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByExactOrDirectBaseTypeShouldAssignSameInstanceToSecondParameterOfSameType( + [Frozen(Matching.ExactType | Matching.DirectBaseType)] + ConcreteType p1, + ConcreteType p2) + { + await Assert.That(p2).IsEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByInterfaceShouldAssignSameInstanceToSecondParameter( + [Frozen(Matching.ImplementedInterfaces)] + NoopInterfaceImplementer p1, + IInterface p2) + { + await Assert.That(p2).IsEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByInterfaceShouldNotAssignSameInstanceToSecondParameterOfNonInterfaceType( + [Frozen(Matching.ImplementedInterfaces)] + NoopInterfaceImplementer p1, + object p2) + { + await Assert.That(p2).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByInterfaceShouldNotAssignSameInstanceToSecondParameterOfSameType( + [Frozen(Matching.ImplementedInterfaces)] + NoopInterfaceImplementer p1, + NoopInterfaceImplementer p2) + { + await Assert.That(p2).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByDirectOrInterfaceShouldAssignSameInstanceToSecondParameterOfSameType( + [Frozen(Matching.ExactType | Matching.ImplementedInterfaces)] + NoopInterfaceImplementer p1, + NoopInterfaceImplementer p2) + { + await Assert.That(p2).IsEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByParameterWithSameNameShouldAssignSameInstanceToSecondParameter( + [Frozen(Matching.ParameterName)] string parameter, + SingleParameterType p2) + { + await Assert.That(p2.Parameter).IsEqualTo(parameter); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByParameterWithDifferentNameShouldNotAssignSameInstanceToSecondParameter( + [Frozen(Matching.ParameterName)] string p1, + SingleParameterType p2) + { + await Assert.That(p2.Parameter).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByParameterWithDifferentNameShouldNotAssignSameInstanceToSecondParameterOfSameType( + [Frozen(Matching.ParameterName)] string p1, + SingleParameterType p2) + { + await Assert.That(p2.Parameter).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByPropertyWithSameNameShouldAssignSameInstanceToSecondParameter( + [Frozen(Matching.PropertyName)] string property, + PropertyHolder p2) + { + await Assert.That(p2.Property).IsEqualTo(property); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByPropertyWithDifferentNameShouldNotAssignSameInstanceToSecondParameter( + [Frozen(Matching.PropertyName)] string p1, + PropertyHolder p2) + { + await Assert.That(p2.Property).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByPropertyWithDifferentNameShouldNotAssignSameInstanceToSecondParameterOfSameType( + [Frozen(Matching.PropertyName)] string p1, + PropertyHolder p2) + { + await Assert.That(p2.Property).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByFieldWithSameNameShouldAssignSameInstanceToSecondParameter( + [Frozen(Matching.FieldName)] string field, + FieldHolder p2) + { + await Assert.That(p2.Field).IsEqualTo(field); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByFieldWithDifferentNameShouldNotAssignSameInstanceToSecondParameter( + [Frozen(Matching.FieldName)] string p1, + FieldHolder p2) + { + await Assert.That(p2.Field).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByFieldWithDifferentNameShouldNotAssignSameInstanceToSecondParameterOfSameType( + [Frozen(Matching.FieldName)] string p1, + FieldHolder p2) + { + await Assert.That(p2.Field).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithSameNameShouldAssignSameInstanceToMatchingParameter( + [Frozen(Matching.MemberName)] string parameter, + SingleParameterType p2) + { + await Assert.That(p2.Parameter).IsEqualTo(parameter); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithDifferentNameShouldNotAssignSameInstanceToParameter( + [Frozen(Matching.MemberName)] string p1, + SingleParameterType p2) + { + await Assert.That(p2.Parameter).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithDifferentNameShouldNotAssignSameInstanceToParameterOfSameType( + [Frozen(Matching.MemberName)] string p1, + SingleParameterType p2) + { + await Assert.That(p2.Parameter).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithSameNameShouldAssignSameInstanceToMatchingProperty( + [Frozen(Matching.MemberName)] string property, + PropertyHolder p2) + { + await Assert.That(p2.Property).IsEqualTo(property); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithDifferentNameShouldNotAssignSameInstanceToProperty( + [Frozen(Matching.MemberName)] string p1, + PropertyHolder p2) + { + await Assert.That(p2.Property).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithDifferentNameShouldNotAssignSameInstanceToPropertyOfSameType( + [Frozen(Matching.MemberName)] string p1, + PropertyHolder p2) + { + await Assert.That(p2.Property).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithSameNameShouldAssignSameInstanceToMatchingField( + [Frozen(Matching.MemberName)] string field, + FieldHolder p2) + { + await Assert.That(p2.Field).IsEqualTo(field); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithDifferentNameShouldNotAssignSameInstanceToField( + [Frozen(Matching.MemberName)] string p1, + FieldHolder p2) + { + await Assert.That(p2.Field).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeFirstParameterByMemberWithDifferentNameShouldNotAssignSameInstanceToFieldOfSameType( + [Frozen(Matching.MemberName)] string p1, + FieldHolder p2) + { + await Assert.That(p2.Field).IsNotEqualTo(p1); + } + + [Test, AutoData] + public async Task FreezeParameterWithStringLengthConstraintShouldCreateConstrainedSpecimen( + [Frozen, StringLength(3)] string p) + { + await Assert.That(p.Length == 3).IsTrue(); + } + + [Test, ClassAutoData(typeof(StringDataClass))] + public async Task ClassAutoDataUsesValuesSuppliedByClass(string s1, string s2, string s3) + { + await Assert.That(new[] { "foo", "dim" }).Contains(s1); + await Assert.That(s2).IsNotEmpty(); + await Assert.That(s3).IsNotEmpty(); + } + + [Test, ClassAutoData(typeof(StringDataClass))] + public async Task ClassAutoDataSuppliesDataSpecimens(string s1, string s2, string s3, MyClass myClass) + { + await Assert.That(s1).IsNotEmpty(); + await Assert.That(s2).IsNotEmpty(); + await Assert.That(s3).IsNotEmpty(); + await Assert.That(myClass).IsNotNull(); + } + + [Test, ClassAutoData(typeof(MixedDataClass))] + public async Task ClassAutoDataSuppliesDataOfMixedTypes(int p1, string p2, PropertyHolder p3, MyClass myClass) + { + await Assert.That(p1).IsNotEqualTo(0); + await Assert.That(p2).IsNotEmpty(); + await Assert.That(p3).IsNotNull(); + await Assert.That(p3.Property).IsNotEmpty(); + await Assert.That(myClass).IsNotNull(); + } + + [Test, ClassAutoData(typeof(ParameterizedDataClass), 28, "bar", 93.102)] + public async Task ClassAutoDataCanBeParameterized(int p1, string p2, double p3, RecordType p4) + { + var actual = new object[] { p1, p2, p3 }; + var expected = new object[] { 28, "bar", 93.102 }; + + await Assert.That(actual).IsEquivalentTo(expected); + await Assert.That(p4).IsNotNull(); + } + + public class StringDataClass : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return ["foo", "bar", "foobar"]; + yield return ["dim", "sum", "dimsum"]; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } + + public class MixedDataClass : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return [1]; + yield return [4, "testValue"]; + yield return [20, "otherValue", new PropertyHolder { Property = "testValue1" }]; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } + + public class ParameterizedDataClass : IEnumerable + { + private readonly int p1; + private readonly string p2; + private readonly double p3; + + public ParameterizedDataClass(int p1, string p2, double p3) + { + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } + + public IEnumerator GetEnumerator() + { + yield return [this.p1, this.p2, this.p3]; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/ChildTestTypeMethodData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/ChildTestTypeMethodData.cs new file mode 100644 index 0000000..79a6ed5 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ChildTestTypeMethodData.cs @@ -0,0 +1,25 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +/// +/// Created to test whether MemberAutoDataAttribute can discover static test data members from parent classes. +/// +public class ChildTestTypeMethodData : TestTypeWithMethodData +{ + public new async Task MultipleValueTest(string a, int b, decimal c) + { + await Assert.That(a).IsNotNull(); + await Assert.That(a).IsNotEmpty(); + await Assert.That(string.IsNullOrWhiteSpace(a)).IsFalse(); + + await Assert.That(b != 0).IsTrue().Because("Value should not be default"); + await Assert.That(c != 0).IsTrue().Because("Value should not be default"); + } + + public static new MethodInfo GetMultipleValueTestMethodInfo() + { + return typeof(ChildTestTypeMethodData) + .GetMethod(nameof(MultipleValueTest)); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/ClassWithEmptyTestData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/ClassWithEmptyTestData.cs new file mode 100644 index 0000000..276aca2 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ClassWithEmptyTestData.cs @@ -0,0 +1,15 @@ +using System.Collections; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class ClassWithEmptyTestData : IEnumerable +{ + public IEnumerator GetEnumerator() + { + yield return []; + yield return []; + yield return []; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/ClassWithNullTestData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/ClassWithNullTestData.cs new file mode 100644 index 0000000..e95410f --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ClassWithNullTestData.cs @@ -0,0 +1,15 @@ +using System.Collections; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class ClassWithNullTestData : IEnumerable +{ + public IEnumerator GetEnumerator() + { + yield return null!; + yield return null!; + yield return null!; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/CompositeTypeWithOverloadedConstructors.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/CompositeTypeWithOverloadedConstructors.cs new file mode 100644 index 0000000..b7c8744 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/CompositeTypeWithOverloadedConstructors.cs @@ -0,0 +1,21 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class CompositeTypeWithOverloadedConstructors +{ + public CompositeTypeWithOverloadedConstructors(params T[] items) + : this(items.AsEnumerable()) + { + } + + public CompositeTypeWithOverloadedConstructors(IList items) + : this(items.AsEnumerable()) + { + } + + public CompositeTypeWithOverloadedConstructors(IEnumerable items) + { + this.Items = items; + } + + public IEnumerable Items { get; } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingCustomization.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingCustomization.cs new file mode 100644 index 0000000..d297a7c --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingCustomization.cs @@ -0,0 +1,16 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class DelegatingCustomization : ICustomization +{ + internal DelegatingCustomization() + { + this.OnCustomize = _ => { }; + } + + public void Customize(IFixture fixture) + { + this.OnCustomize(fixture); + } + + internal Action OnCustomize { get; set; } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingCustomizeAttribute.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingCustomizeAttribute.cs new file mode 100644 index 0000000..b04bd83 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingCustomizeAttribute.cs @@ -0,0 +1,13 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class DelegatingCustomizeAttribute : CustomizeAttribute +{ + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return this.OnGetCustomization(parameter); + } + + public Func OnGetCustomization { get; set; } = p => new DelegatingCustomization(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingDataSource.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingDataSource.cs new file mode 100644 index 0000000..136c159 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingDataSource.cs @@ -0,0 +1,13 @@ +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DelegatingDataSource : AutoFixtureDataSourceAttribute, IDataSource +{ + public IEnumerable TestData { get; set; } = Array.Empty(); + + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + return this.TestData; + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingFixture.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingFixture.cs new file mode 100644 index 0000000..1615294 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingFixture.cs @@ -0,0 +1,94 @@ +using AutoFixture.Dsl; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class DelegatingFixture : IFixture +{ + private readonly List customizations = + [ + ]; + private readonly List residueCollectors = + [ + ]; + + public IList Behaviors => throw new InvalidOperationException(); + + public IList Customizations => this.customizations; + + public bool OmitAutoProperties { get; set; } + + public int RepeatCount { get; set; } + + public IList ResidueCollectors => this.residueCollectors; + + public void AddManyTo(ICollection collection, Func creator) + { + throw new InvalidOperationException(); + } + + public void AddManyTo(ICollection collection) + { + throw new InvalidOperationException(); + } + + public void AddManyTo(ICollection collection, int repeatCount) + { + throw new InvalidOperationException(); + } + + public ICustomizationComposer Build() + { + throw new InvalidOperationException(); + } + + public IFixture Customize(ICustomization customization) + { + this.OnCustomize?.Invoke(customization); + return this; + } + + public void Customize(Func, ISpecimenBuilder> composerTransformation) + { + throw new InvalidOperationException(); + } + + public void Inject(T item) + { + throw new InvalidOperationException(); + } + + public void Register(Func creator) + { + throw new InvalidOperationException(); + } + + public void Register(Func creator) + { + throw new InvalidOperationException(); + } + + public void Register(Func creator) + { + throw new InvalidOperationException(); + } + + public void Register(Func creator) + { + throw new InvalidOperationException(); + } + + public void Register(Func creator) + { + throw new InvalidOperationException(); + } + + public object Create(object request, ISpecimenContext context) + { + return this.OnCreate(request, context); + } + + internal Func OnCreate { get; set; } = (_, _) => new object(); + + internal Action OnCustomize { get; set; } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingMemberDataSource.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingMemberDataSource.cs new file mode 100644 index 0000000..7f198ba --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingMemberDataSource.cs @@ -0,0 +1,14 @@ +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DelegatingMemberDataSource + : MemberDataSource +{ + public DelegatingMemberDataSource(Type type, string name, params object[] arguments) + : base(type, name, arguments) + { + } + + public DataSource GetSource() => this.Source; +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingSpecimenBuilder.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingSpecimenBuilder.cs new file mode 100644 index 0000000..c5d6e79 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingSpecimenBuilder.cs @@ -0,0 +1,13 @@ +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class DelegatingSpecimenBuilder : ISpecimenBuilder +{ + public object Create(object request, ISpecimenContext context) + { + return this.OnCreate(request, context); + } + + internal Func OnCreate { get; set; } = (_, _) => new object(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingTestData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingTestData.cs new file mode 100644 index 0000000..b8d688b --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingTestData.cs @@ -0,0 +1,22 @@ +using System.Collections; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DelegatingTestData : IEnumerable +{ + private readonly List data; + + public DelegatingTestData(params object[][] data) + { + this.data = data.ToList(); + } + + public DelegatingTestData(IEnumerable data) + { + this.data = data as List ?? data.ToList(); + } + + public IEnumerator GetEnumerator() => this.data.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedArgumentsAutoDataAttribute.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedArgumentsAutoDataAttribute.cs new file mode 100644 index 0000000..95841f5 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedArgumentsAutoDataAttribute.cs @@ -0,0 +1,10 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class DerivedArgumentsAutoDataAttribute + : ArgumentsAutoDataAttribute +{ + public DerivedArgumentsAutoDataAttribute(Func fixtureFactory, params object[] values) + : base(fixtureFactory, values) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedAutoDataAttribute.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedAutoDataAttribute.cs new file mode 100644 index 0000000..47f13c8 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedAutoDataAttribute.cs @@ -0,0 +1,9 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DerivedAutoDataAttribute : AutoDataAttribute +{ + public DerivedAutoDataAttribute(Func fixtureFactory) + : base(fixtureFactory) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedClassAutoDataAttribute.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedClassAutoDataAttribute.cs new file mode 100644 index 0000000..4eadeb0 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedClassAutoDataAttribute.cs @@ -0,0 +1,14 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DerivedClassAutoDataAttribute : ClassAutoDataAttribute +{ + public DerivedClassAutoDataAttribute(Type sourceType) + : base(sourceType) + { + } + + public DerivedClassAutoDataAttribute(Func fixtureFactory, Type sourceType, params object[] parameters) + : base(fixtureFactory, sourceType, parameters) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedMemberAutoDataAttribute.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedMemberAutoDataAttribute.cs new file mode 100644 index 0000000..4a91a3a --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedMemberAutoDataAttribute.cs @@ -0,0 +1,14 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DerivedMemberAutoDataAttribute : MemberAutoDataAttribute +{ + public DerivedMemberAutoDataAttribute(Func fixtureFactory, string memberName, params object[] parameters) + : base(fixtureFactory, memberName, parameters) + { + } + + public DerivedMemberAutoDataAttribute(Func fixtureFactory, Type memberType, string memberName, params object[] parameters) + : base(fixtureFactory, memberType, memberName, parameters) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/EmptyClassData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/EmptyClassData.cs new file mode 100644 index 0000000..b9a8f61 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/EmptyClassData.cs @@ -0,0 +1,13 @@ +using System.Collections; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class EmptyClassData : IEnumerable +{ + public IEnumerator GetEnumerator() + { + yield break; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/ExampleTestClass.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/ExampleTestClass.cs new file mode 100644 index 0000000..8f3b637 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ExampleTestClass.cs @@ -0,0 +1,22 @@ +using System.Diagnostics.CodeAnalysis; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class ExampleTestClass +{ + [SuppressMessage("Usage", "xUnit1013:Public method should be marked as test", + Justification = "This test method is used through reflection.")] + public void TestMethod(int a, string b, EnumType c, Tuple d) + { + } +} + +public class ExampleTestClass +{ + [SuppressMessage("Usage", "xUnit1013:Public method should be marked as test", + Justification = "This test method is used through reflection.")] + public void TestMethod(T1 a, T2 b, T3 c, T4 d) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/FakeDataAttribute.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/FakeDataAttribute.cs new file mode 100644 index 0000000..d8a517e --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/FakeDataAttribute.cs @@ -0,0 +1,20 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class FakeDataAttribute : AutoFixtureDataSourceAttribute +{ + private readonly MethodInfo expectedMethod; + private readonly IEnumerable output; + + public FakeDataAttribute(MethodInfo expectedMethod, IEnumerable output) + { + this.output = output; + this.expectedMethod = expectedMethod; + } + + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + return this.output; + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/FixedParameterBuilder.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/FixedParameterBuilder.cs new file mode 100644 index 0000000..9ef50fc --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/FixedParameterBuilder.cs @@ -0,0 +1,12 @@ +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class FixedParameterBuilder + : FilteringSpecimenBuilder +{ + public FixedParameterBuilder(string name, T value) + : base(new FixedBuilder(value), new ParameterSpecification(typeof(T), name)) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/InlineAttributeTestData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/InlineAttributeTestData.cs new file mode 100644 index 0000000..3f0a4f1 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/InlineAttributeTestData.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal abstract class InlineAttributeTestData +{ + protected static DerivedArgumentsAutoDataAttribute CreateAttributeWithFakeFixture( + object[] inlineValues, + params (string ParameterName, object Value)[] parameters) + { + return new DerivedArgumentsAutoDataAttribute( + fixtureFactory: () => new DelegatingFixture { OnCreate = OnCreateParameter }, + values: inlineValues); + + object OnCreateParameter(object request, ISpecimenContext context) + { + if (request is not ParameterInfo parameterInfo) + { + throw new InvalidOperationException(); + } + + return parameters + .Where(x => x.ParameterName == parameterInfo.Name) + .Select(x => x.Value).FirstOrDefault(); + } + } + + protected static DerivedArgumentsAutoDataAttribute CreateAttribute( + object[] inlineValues, + params (string ParameterName, object Value)[] parameters) + { + return new DerivedArgumentsAutoDataAttribute( + () => new Fixture().Customize(CreateCustomization()), + inlineValues); + + ICustomization CreateCustomization() + { + var builders = parameters + .Select(x => new FilteringSpecimenBuilder( + builder: new FixedBuilder(x.Value), + specification: new ParameterSpecification( + new ParameterNameCriterion(x.ParameterName)))) + .ToList(); + + return new DelegatingCustomization + { + OnCustomize = f => f.Customizations + .Insert(0, new CompositeSpecimenBuilder(builders)) + }; + } + } + + public abstract IEnumerable GetData(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/InlineFrozenValuesTestData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/InlineFrozenValuesTestData.cs new file mode 100644 index 0000000..f1952e6 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/InlineFrozenValuesTestData.cs @@ -0,0 +1,32 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +[SuppressMessage("Usage", "TUnit0046:Return a `Func` rather than a ``")] +internal class InlineFrozenValuesTestData : InlineAttributeTestData<(AutoFixtureDataSourceAttribute attribute, MethodInfo testMethod, object[] expected)> +{ + public override IEnumerable<(AutoFixtureDataSourceAttribute attribute, MethodInfo testMethod, object[] expected)> GetData() + { + // All values provided by fixture + yield return + ( + CreateAttribute([], ("a", "string_a0"), ("b", "string_b0")), + TestTypeWithMemberDataSource.GetTestWithFrozenParameter(), + ["string_a0", "string_b0", "string_b0"]); + + // First parameter injected; Frozen parameter generated + yield return + ( + CreateAttribute(["string_a1"], ("b", "string_b1")), + TestTypeWithMemberDataSource.GetTestWithFrozenParameter(), + ["string_a1", "string_b1", "string_b1"]); + + // Frozen parameter is injected + yield return + ( + CreateAttribute(["string_a2", "string_b2"]), + TestTypeWithMemberDataSource.GetTestWithFrozenParameter(), + ["string_a2", "string_b2", "string_b2"]); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/InlinePrimitiveValuesTestData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/InlinePrimitiveValuesTestData.cs new file mode 100644 index 0000000..0169fe0 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/InlinePrimitiveValuesTestData.cs @@ -0,0 +1,35 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +[SuppressMessage("Usage", "TUnit0046:Return a `Func` rather than a ``")] +internal class InlinePrimitiveValuesTestData : InlineAttributeTestData<(AutoFixtureDataSourceAttribute attribute, MethodInfo testMethod, object[] expected)> +{ + public override IEnumerable<(AutoFixtureDataSourceAttribute attribute, MethodInfo testMethod, object[] expected)> GetData() + { + // All values provided by fixture + yield return + ( + CreateAttributeWithFakeFixture([], ("a", "parameter_a1"), ("b", 31), ("c", 54.38m)), + TestTypeWithMemberDataSource.GetMultipleValueTestMethodInfo(), + ["parameter_a1", 31, 54.38m] + ); + + // Some parameters injected, some provided by fixture + yield return + ( + CreateAttributeWithFakeFixture(["parameter_a2"], ("b", 22), ("c", 817.218m)), + TestTypeWithMemberDataSource.GetMultipleValueTestMethodInfo(), + ["parameter_a2", 22, 817.218m] + ); + + // All parameters injected + yield return + ( + CreateAttributeWithFakeFixture(["parameter_a3", 13, 332.009m]), + TestTypeWithMemberDataSource.GetMultipleValueTestMethodInfo(), + ["parameter_a3", 13, 332.009m] + ); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/MixedTypeClassData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/MixedTypeClassData.cs new file mode 100644 index 0000000..e6b2243 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/MixedTypeClassData.cs @@ -0,0 +1,18 @@ +using System.Collections; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class MixedTypeClassData : IEnumerable +{ + public IEnumerator GetEnumerator() + { + yield return []; + yield return [9]; + yield return [12, "test-12"]; + yield return [223, "test-17", EnumType.Third]; + yield return [-95, "test-92", EnumType.Second, new Tuple("myValue", 5)]; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/MyClass.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/MyClass.cs new file mode 100644 index 0000000..c955db3 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/MyClass.cs @@ -0,0 +1,6 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class MyClass +{ + public T Echo(T item) => item; +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterNameCriterion.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterNameCriterion.cs new file mode 100644 index 0000000..274c65d --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterNameCriterion.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class ParameterNameCriterion : IEquatable +{ + public ParameterNameCriterion(string name) + : this(new Criterion(name, StringComparer.Ordinal)) + { + } + + public ParameterNameCriterion(IEquatable nameCriterion) + { + this.NameCriterion = nameCriterion ?? throw new ArgumentNullException(nameof(nameCriterion)); + } + + /// + /// The name criterion originally passed in via the class' constructor. + /// + public IEquatable NameCriterion { get; } + + public bool Equals(ParameterInfo other) + { + return this.NameCriterion.Equals(other.Name); + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterizedClassData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterizedClassData.cs new file mode 100644 index 0000000..59d2452 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterizedClassData.cs @@ -0,0 +1,26 @@ +using System.Collections; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class ParameterizedClassData : IEnumerable +{ + private readonly int p1; + private readonly string p2; + private readonly EnumType p3; + + public ParameterizedClassData(int p1, string p2, EnumType p3) + { + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } + + public IEnumerator GetEnumerator() + { + yield return [this.p1, this.p2, this.p3]; + yield return [this.p1, this.p2, this.p3]; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/SampleTestType.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/SampleTestType.cs new file mode 100644 index 0000000..7e5ec85 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/SampleTestType.cs @@ -0,0 +1,38 @@ +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class SampleTestType +{ + public void TestMethodWithoutParameters() + { + } + + public void TestMethodWithSingleParameter(string value) + { + } + + public void TestMethodWithMultipleParameters(string a, int b, double c) + { + } + + public void TestMethodWithReferenceTypeParameter(string a, int b, PropertyHolder c) + { + } + + public void TestMethodWithRecordTypeParameter(string a, int b, RecordType c) + { + } + + public void TestMethodWithCollectionParameter(string a, int b, IEnumerable c) + { + } + + public void TestMethodWithCustomizedParameter([Frozen] string a, int b, PropertyHolder c) + { + } + + public void TestMethodWithMultipleCustomizations([Frozen] string a, [Frozen] int b, [Greedy][Frozen] PropertyHolder c) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/TestTypeWithMemberDataSource.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/TestTypeWithMemberDataSource.cs new file mode 100644 index 0000000..0a145e6 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/TestTypeWithMemberDataSource.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class TestTypeWithMemberDataSource +{ + [SuppressMessage("Usage", "xUnit1013:Public method should be marked as test", + Justification = "Test is invoked through reflection.")] + public async Task MultipleValueTest(string a, int b, decimal c) + { + await Assert.That(a).IsNotNull(); + await Assert.That(a).IsNotEmpty(); + await Assert.That(string.IsNullOrWhiteSpace(a)).IsFalse(); + + await Assert.That(b != 0).IsTrue().Because("Value should not be default"); + await Assert.That(c != 0).IsTrue().Because("Value should not be default"); + } + + public async Task TestWithFrozenParameter(string a, [Frozen] string b, string c) + { + await Assert.That(a).IsNotNull(); + await Assert.That(b).IsNotNull(); + await Assert.That(c).IsNotNull(); + + await Assert.That(b).IsNotEqualTo(a); + await Assert.That(c).IsEqualTo(b); + } + + public static MethodInfo GetMultipleValueTestMethodInfo() => + typeof(TestTypeWithMemberDataSource) + .GetMethod(nameof(MultipleValueTest)); + + public static MethodInfo GetTestWithFrozenParameter() => + typeof(TestTypeWithMemberDataSource) + .GetMethod(nameof(TestWithFrozenParameter)); +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/TestTypeWithMethodData.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/TestTypeWithMethodData.cs new file mode 100644 index 0000000..751998b --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/TestTypeWithMethodData.cs @@ -0,0 +1,159 @@ +using System.Reflection; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class TestTypeWithMethodData +{ + public IEnumerable NonStaticSource() + { + yield return [new object()]; + yield return [new object()]; + yield return [new object()]; + } + + public static MethodInfo GetNonStaticSourceMethodInfo() + { + return typeof(TestTypeWithMethodData) + .GetMethod(nameof(NonStaticSource)); + } + + public static object NonEnumerableMethod() + { + return new object(); + } + + public static MethodInfo GetNonEnumerableMethodInfo() + { + return typeof(TestTypeWithMethodData) + .GetMethod(nameof(NonEnumerableMethod)); + } + + public static IEnumerable TestDataWithNoValues() + { + yield return []; + yield return []; + yield return []; + } + + public async Task SingleStringValueTest(string value) + { + await Assert.That(value).IsNotNull(); + await Assert.That(value).IsNotEmpty(); + await Assert.That(string.IsNullOrWhiteSpace(value)).IsFalse(); + } + + public static MethodInfo GetSingleStringValueTestMethodInfo() + { + return typeof(TestTypeWithMethodData) + .GetMethod(nameof(SingleStringValueTest)); + } + + public static IEnumerable GetSingleStringValueTestData() + { + yield return ["value-one"]; + yield return ["value-two"]; + yield return ["value-three"]; + } + + public static IEnumerable GetStringTestsFromArgument(string argument) + { + yield return [argument + "-one"]; + yield return [argument + "-two"]; + yield return [argument + "-three"]; + } + + public static MethodInfo GetStringTestsFromArgumentMethodInfo() + { + return typeof(TestTypeWithMethodData) + .GetMethod(nameof(GetStringTestsFromArgument)); + } + + public async Task MultipleValueTest(string a, int b, decimal c) + { + await Assert.That(a).IsNotNull(); + await Assert.That(a).IsNotEmpty(); + await Assert.That(string.IsNullOrWhiteSpace(a)).IsFalse(); + + await Assert.That(b != 0).IsTrue().Because("Value should not be default"); + await Assert.That(c != 0).IsTrue().Because("Value should not be default"); + } + + public static MethodInfo GetMultipleValueTestMethodInfo() + { + return typeof(TestTypeWithMethodData) + .GetMethod(nameof(MultipleValueTest)); + } + + public static IEnumerable GetMultipleValueTestData() + { + yield return ["value-one", 12, 23.3m]; + yield return ["value-two", 38, 12.7m]; + yield return ["value-three", 94, 52.21m]; + } + + public async Task TestWithFrozenParameter(string a, [Frozen] string b, string c) + { + await Assert.That(a).IsNotNull(); + await Assert.That(b).IsNotNull(); + await Assert.That(c).IsNotNull(); + + await Assert.That(b).IsNotEqualTo(a); + await Assert.That(c).IsEqualTo(b); + } + + public static IEnumerable GetDataForTestWithFrozenParameter() + { + yield return ["value-one", "value-two"]; + yield return ["value-two", "value-three"]; + yield return ["value-three", "value-one"]; + } + + public static MethodInfo GetTestWithFrozenParameter() + { + return typeof(TestTypeWithMethodData) + .GetMethod(nameof(TestWithFrozenParameter)); + } + + public async Task TestWithComplexTypes([Frozen] PropertyHolder a, PropertyHolder b) + { + await Assert.That(a).IsNotNull(); + await Assert.That(b).IsNotNull(); + + await Assert.That(b).IsSameReferenceAs(a); + } + + public static IEnumerable GetTestWithComplexTypesData() + { + yield return + [ + new PropertyHolder { Property = "1647400C-9011-4158-BA5A-F841185AF6EF" }, + new PropertyHolder() + ]; + yield return + [ + new PropertyHolder { Property = "E0F5F4F1-4B6B-4B6B-8F4A-7C0F6F4F4F4F" }, + new PropertyHolder { Property = "00000000-0000-0000-0000-000000000000" } + ]; + yield return + [ + new PropertyHolder { Property = "B0B0B0B0-B0B0-B0B0-B0B0-B0B0B0B0B0B0" }, + new PropertyHolder { Property = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF" } + ]; + } + + public static MethodInfo GetTestWithComplexTypes() + { + return typeof(TestTypeWithMethodData) + .GetMethod(nameof(TestWithComplexTypes)); + } + + public static IEnumerable GetStringValuesTestData() + { + yield return ["test-one", "test-uno"]; + yield return ["test-two", "test-dos"]; + yield return ["test-three", "test-tres"]; + } + + public static IEnumerable GetEmptyTestData() => []; +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/TypeWithCustomizationAttributes.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/TypeWithCustomizationAttributes.cs new file mode 100644 index 0000000..aa2ff5a --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/TypeWithCustomizationAttributes.cs @@ -0,0 +1,54 @@ +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class TypeWithCustomizationAttributes +{ + public void CreateWithFrozenAndFavorArrays([Frozen, FavorArrays] ConcreteType sut) + { + } + + public void CreateWithFavorArraysAndFrozen([FavorArrays, Frozen] ConcreteType sut) + { + } + + public void CreateWithFrozenAndFavorEnumerables([Frozen, FavorEnumerables] ConcreteType sut) + { + } + + public void CreateWithFavorEnumerablesAndFrozen([FavorEnumerables, Frozen] ConcreteType sut) + { + } + + public void CreateWithFrozenAndFavorLists([Frozen, FavorLists] ConcreteType sut) + { + } + + public void CreateWithFavorListsAndFrozen([FavorLists, Frozen] ConcreteType sut) + { + } + + public void CreateWithFrozenAndGreedy([Frozen, Greedy] ConcreteType sut) + { + } + + public void CreateWithGreedyAndFrozen([Greedy, Frozen] ConcreteType sut) + { + } + + public void CreateWithFrozenAndModest([Frozen, Modest] ConcreteType sut) + { + } + + public void CreateWithModestAndFrozen([Modest, Frozen] ConcreteType sut) + { + } + + public void CreateWithFrozenAndNoAutoProperties([Frozen, NoAutoProperties] ConcreteType sut) + { + } + + public void CreateWithNoAutoPropertiesAndFrozen([NoAutoProperties, Frozen] ConcreteType sut) + { + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/TestTypes/TypeWithIParameterCustomizationSourceUsage.cs b/tests/AutoFixture.TUnit.Tests/TestTypes/TypeWithIParameterCustomizationSourceUsage.cs new file mode 100644 index 0000000..8099949 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/TypeWithIParameterCustomizationSourceUsage.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class TypeWithIParameterCustomizationSourceUsage +{ + public void DecoratedMethod([CustomizationSource] int arg) + { + } + + [AttributeUsage(AttributeTargets.All)] + public class CustomizationSourceAttribute : Attribute, IParameterCustomizationSource + { + public ICustomization GetCustomization(ParameterInfo parameter) + { + return new Customization(); + } + } + + public class Customization : ICustomization + { + public void Customize(IFixture fixture) + { + } + } +} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/UnitTest1.cs b/tests/AutoFixture.TUnit.Tests/UnitTest1.cs deleted file mode 100644 index 45c769c..0000000 --- a/tests/AutoFixture.TUnit.Tests/UnitTest1.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace AutoFixture.TUnit.Tests; - -public class UnitTest1 -{ - [Test] - public async Task Test1() - { - // Arrange - var sut = new Class1(); - - // Assert - await Assert.That(sut).IsNotNull(); - } -} \ No newline at end of file diff --git a/tests/AutoFixture.TUnit.Tests/ValueTaskExtensions.cs b/tests/AutoFixture.TUnit.Tests/ValueTaskExtensions.cs new file mode 100644 index 0000000..da5da6c --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/ValueTaskExtensions.cs @@ -0,0 +1,20 @@ +namespace AutoFixture.TUnit.Tests; + +internal static class ValueTaskExtensions +{ + /// + /// Wraps the value in a . + /// + /// The value to be wrapped. + /// The generic type of the value task. + /// Returns a completed instance. + public static ValueTask ToValueTask(this T value) => new(value); + + /// + /// Wraps the task in a . + /// + /// The task to be wrapped. + /// The generic type of the value task. + /// Returns a instance. + public static ValueTask ToValueTask(this Task task) => new(task); +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/AbstractClassWithPublicConstructor.cs b/tests/TestTypeFoundation/AbstractClassWithPublicConstructor.cs new file mode 100644 index 0000000..1e038eb --- /dev/null +++ b/tests/TestTypeFoundation/AbstractClassWithPublicConstructor.cs @@ -0,0 +1,5 @@ +namespace TestTypeFoundation; + +public abstract class AbstractClassWithPublicConstructor +{ +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/AbstractGenericType.cs b/tests/TestTypeFoundation/AbstractGenericType.cs new file mode 100644 index 0000000..376b3a1 --- /dev/null +++ b/tests/TestTypeFoundation/AbstractGenericType.cs @@ -0,0 +1,11 @@ +namespace TestTypeFoundation; + +public abstract class AbstractGenericType +{ + protected AbstractGenericType(T value) + { + this.Value = value; + } + + public T Value { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/AbstractType.cs b/tests/TestTypeFoundation/AbstractType.cs new file mode 100644 index 0000000..ab273cd --- /dev/null +++ b/tests/TestTypeFoundation/AbstractType.cs @@ -0,0 +1,14 @@ +namespace TestTypeFoundation; + +public abstract class AbstractType +{ + public object Property1 { get; set; } + + public object Property2 { get; set; } + + public object Property3 { get; set; } + + public virtual object Property4 { get; set; } + + public object Field1; +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/AbstractTypeWithConstructorWithMultipleParameters.cs b/tests/TestTypeFoundation/AbstractTypeWithConstructorWithMultipleParameters.cs new file mode 100644 index 0000000..1ab088a --- /dev/null +++ b/tests/TestTypeFoundation/AbstractTypeWithConstructorWithMultipleParameters.cs @@ -0,0 +1,14 @@ +namespace TestTypeFoundation; + +public abstract class AbstractTypeWithConstructorWithMultipleParameters +{ + protected AbstractTypeWithConstructorWithMultipleParameters(T1 parameter1, T2 parameter2) + { + this.Property1 = parameter1; + this.Property2 = parameter2; + } + + public T1 Property1 { get; } + + public T2 Property2 { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/AbstractTypeWithNonDefaultConstructor.cs b/tests/TestTypeFoundation/AbstractTypeWithNonDefaultConstructor.cs new file mode 100644 index 0000000..6f403a2 --- /dev/null +++ b/tests/TestTypeFoundation/AbstractTypeWithNonDefaultConstructor.cs @@ -0,0 +1,16 @@ +namespace TestTypeFoundation; + +public abstract class AbstractTypeWithNonDefaultConstructor +{ + protected AbstractTypeWithNonDefaultConstructor(T value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + this.Property = value; + } + + public T Property { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ActivityScope.cs b/tests/TestTypeFoundation/ActivityScope.cs new file mode 100644 index 0000000..4f34cc0 --- /dev/null +++ b/tests/TestTypeFoundation/ActivityScope.cs @@ -0,0 +1,45 @@ +namespace TestTypeFoundation; + +[Flags] +public enum ActivityScope : long +{ + /// + /// Undefined scope. + /// + Undefined = 0, + + /// + /// The OnDuty activity has its own special scope. + /// + OnDuty = 0x01, + + /// + /// The OffDuty activity has its own special scope. + /// + OffDuty = 0x02, + + /// + /// A standalone has no parent or children. + /// + Standalone = 0x04, + + /// + /// A parent activity can have one or more child activities.. + /// + Parent = 0x08, + + /// + /// A child activity has a parent activity. + /// + Child = 0x10, + + /// + /// The set of all scopes that are allowed when there is no open Parent activity. + /// + AllInitiatingScopes = Parent | Standalone, + + /// + /// The set all valid scopes. + /// + All = OnDuty | OffDuty | Standalone | Parent | Child, +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/CollectionHolder.cs b/tests/TestTypeFoundation/CollectionHolder.cs new file mode 100644 index 0000000..b0c5432 --- /dev/null +++ b/tests/TestTypeFoundation/CollectionHolder.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class CollectionHolder +{ + public ICollection Collection { get; private set; } = new List(); +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/CompositeType.cs b/tests/TestTypeFoundation/CompositeType.cs new file mode 100644 index 0000000..55d6cb5 --- /dev/null +++ b/tests/TestTypeFoundation/CompositeType.cs @@ -0,0 +1,21 @@ +namespace TestTypeFoundation; + +public class CompositeType : AbstractType +{ + public CompositeType(IEnumerable types) + : this(types.ToArray()) + { + } + + public CompositeType(params AbstractType[] types) + { + if (types == null) + { + throw new ArgumentNullException(nameof(types)); + } + + this.Types = types; + } + + public IEnumerable Types { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ConcreteType.cs b/tests/TestTypeFoundation/ConcreteType.cs new file mode 100644 index 0000000..5748a47 --- /dev/null +++ b/tests/TestTypeFoundation/ConcreteType.cs @@ -0,0 +1,38 @@ +namespace TestTypeFoundation; + +public class ConcreteType : AbstractType +{ + public ConcreteType() + { + } + + public ConcreteType(object obj) + { + this.Property1 = obj; + } + + public ConcreteType(object obj1, object obj2) + { + this.Property1 = obj1; + this.Property2 = obj2; + } + + public ConcreteType(object obj1, object obj2, object obj3) + { + this.Property1 = obj1; + this.Property2 = obj2; + this.Property3 = obj3; + } + + public ConcreteType(object obj1, object obj2, object obj3, object obj4) + { + this.Property1 = obj1; + this.Property2 = obj2; + this.Property3 = obj3; + this.Property4 = obj4; + } + + public override object Property4 { get; set; } + + public object Property5 { get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ConcreteTypeWithPrivateParameterlessConstructor.cs b/tests/TestTypeFoundation/ConcreteTypeWithPrivateParameterlessConstructor.cs new file mode 100644 index 0000000..c9ce80c --- /dev/null +++ b/tests/TestTypeFoundation/ConcreteTypeWithPrivateParameterlessConstructor.cs @@ -0,0 +1,12 @@ +namespace TestTypeFoundation; + +public class ConcreteTypeWithPrivateParameterlessConstructor +{ + private ConcreteTypeWithPrivateParameterlessConstructor() + { + } + + public ConcreteTypeWithPrivateParameterlessConstructor(object obj) + { + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/DoubleFieldHolder.cs b/tests/TestTypeFoundation/DoubleFieldHolder.cs new file mode 100644 index 0000000..ed1cb9c --- /dev/null +++ b/tests/TestTypeFoundation/DoubleFieldHolder.cs @@ -0,0 +1,8 @@ +namespace TestTypeFoundation; + +public class DoubleFieldHolder +{ + public T1 Field1; + + public T2 Field2; +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/DoubleParameterType.cs b/tests/TestTypeFoundation/DoubleParameterType.cs new file mode 100644 index 0000000..89f5a2a --- /dev/null +++ b/tests/TestTypeFoundation/DoubleParameterType.cs @@ -0,0 +1,13 @@ +namespace TestTypeFoundation; + +public class DoubleParameterType +{ + public DoubleParameterType(T1 parameter1, T2 parameter2) + { + this.Parameter1 = parameter1; + this.Parameter2 = parameter2; + } + + public T1 Parameter1 { get; private set; } + public T2 Parameter2 { get; private set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/DoublePropertyHolder.cs b/tests/TestTypeFoundation/DoublePropertyHolder.cs new file mode 100644 index 0000000..2087e7f --- /dev/null +++ b/tests/TestTypeFoundation/DoublePropertyHolder.cs @@ -0,0 +1,8 @@ +namespace TestTypeFoundation; + +public class DoublePropertyHolder +{ + public T1 Property1 { get; set; } + + public T2 Property2 { get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/EmptyEnum.cs b/tests/TestTypeFoundation/EmptyEnum.cs new file mode 100644 index 0000000..224363e --- /dev/null +++ b/tests/TestTypeFoundation/EmptyEnum.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public enum EmptyEnum +{ + // this must not contain any values +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/EnumType.cs b/tests/TestTypeFoundation/EnumType.cs new file mode 100644 index 0000000..8c64374 --- /dev/null +++ b/tests/TestTypeFoundation/EnumType.cs @@ -0,0 +1,57 @@ +namespace TestTypeFoundation; + +public enum EnumType +{ + First = 1, + Second = 2, + Third = 3 +} + +public enum ByteEnumType : byte +{ + First = 99, + Second, + Third +} + +public enum SByteEnumType : sbyte +{ + First = -98, + Second, + Third +} + +public enum ShortEnumType : short +{ + First = 100, + Second, + Third +} + +public enum UShortEnumType : ushort +{ + First = 1, + Second, + Third +} + +public enum UIntEnumType : uint +{ + First, + Second, + Third +} + +public enum LongEnumType : long +{ + First = (long)int.MaxValue + 1, + Second, + Third +} + +public enum ULongEnumType : ulong +{ + First = 1, + Second, + Third +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/EqualityResponder.cs b/tests/TestTypeFoundation/EqualityResponder.cs new file mode 100644 index 0000000..35aaab2 --- /dev/null +++ b/tests/TestTypeFoundation/EqualityResponder.cs @@ -0,0 +1,21 @@ +namespace TestTypeFoundation; + +public class EqualityResponder +{ + private readonly bool equals; + + public EqualityResponder(bool equals) + { + this.equals = equals; + } + + public override bool Equals(object obj) + { + return this.equals; + } + + public override int GetHashCode() + { + return this.equals.GetHashCode(); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/FieldHolder.cs b/tests/TestTypeFoundation/FieldHolder.cs new file mode 100644 index 0000000..c3135eb --- /dev/null +++ b/tests/TestTypeFoundation/FieldHolder.cs @@ -0,0 +1,14 @@ +using System.Reflection; + +namespace TestTypeFoundation; + +public class FieldHolder +{ + public T Field; + + public static FieldInfo GetField() + { + return typeof(FieldHolder) + .GetRuntimeField(nameof(Field)); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/GenericType.cs b/tests/TestTypeFoundation/GenericType.cs new file mode 100644 index 0000000..0ccfd52 --- /dev/null +++ b/tests/TestTypeFoundation/GenericType.cs @@ -0,0 +1,12 @@ +namespace TestTypeFoundation; + +public class GenericType + where T : class +{ + public GenericType(T value) + { + this.Value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private T Value { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/GuardedConstructorHost.cs b/tests/TestTypeFoundation/GuardedConstructorHost.cs new file mode 100644 index 0000000..2f53c29 --- /dev/null +++ b/tests/TestTypeFoundation/GuardedConstructorHost.cs @@ -0,0 +1,17 @@ +namespace TestTypeFoundation; + +public class GuardedConstructorHost + where T : class +{ + public GuardedConstructorHost(T item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + this.Item = item; + } + + public T Item { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/GuardedConstructorHostHoldingStaticReadOnlyField.cs b/tests/TestTypeFoundation/GuardedConstructorHostHoldingStaticReadOnlyField.cs new file mode 100644 index 0000000..df72d37 --- /dev/null +++ b/tests/TestTypeFoundation/GuardedConstructorHostHoldingStaticReadOnlyField.cs @@ -0,0 +1,19 @@ +namespace TestTypeFoundation; + +public class GuardedConstructorHostHoldingStaticReadOnlyField + where TItem : class +{ + public static readonly TStaticField Field; + + public GuardedConstructorHostHoldingStaticReadOnlyField(TItem item) + { + if (item is null) + { + throw new ArgumentNullException(nameof(item)); + } + + this.Item = item; + } + + public TItem Item { get; private set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/GuardedConstructorHostHoldingStaticReadOnlyProperty.cs b/tests/TestTypeFoundation/GuardedConstructorHostHoldingStaticReadOnlyProperty.cs new file mode 100644 index 0000000..0ae442e --- /dev/null +++ b/tests/TestTypeFoundation/GuardedConstructorHostHoldingStaticReadOnlyProperty.cs @@ -0,0 +1,24 @@ +namespace TestTypeFoundation; + +public class GuardedConstructorHostHoldingStaticReadOnlyProperty + where TItem : class +{ + static GuardedConstructorHostHoldingStaticReadOnlyProperty() + { + Property = default(TStaticProperty); + } + + public GuardedConstructorHostHoldingStaticReadOnlyProperty(TItem item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + this.Item = item; + } + + public static TStaticProperty Property { get; private set; } + + public TItem Item { get; private set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/GuardedMethodHost.cs b/tests/TestTypeFoundation/GuardedMethodHost.cs new file mode 100644 index 0000000..4c9a1a3 --- /dev/null +++ b/tests/TestTypeFoundation/GuardedMethodHost.cs @@ -0,0 +1,80 @@ +namespace TestTypeFoundation; + +public class GuardedMethodHost +{ + public void ConsumeString(string s) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (s.Length == 0) + { + throw new ArgumentException("String cannot be empty.", nameof(s)); + } + } + + public void ConsumeInt32(int i) + { + } + + public void ConsumeGuid(Guid g) + { + if (g == Guid.Empty) + { + throw new ArgumentException("Guid cannot be empty.", nameof(g)); + } + } + + public void ConsumeStringAndInt32(string s, int i) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (s.Length == 0) + { + throw new ArgumentException("String cannot be empty.", nameof(s)); + } + } + + public void ConsumeStringAndGuid(string s, Guid g) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (s.Length == 0) + { + throw new ArgumentException("String cannot be empty.", nameof(s)); + } + if (g == Guid.Empty) + { + throw new ArgumentException("Guid cannot be empty.", nameof(g)); + } + } + + public void ConsumeInt32AndGuid(int i, Guid g) + { + if (g == Guid.Empty) + { + throw new ArgumentException("Guid cannot be empty.", nameof(g)); + } + } + + public void ConsumeStringAndInt32AndGuid(string s, int i, Guid g) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (s.Length == 0) + { + throw new ArgumentException("String cannot be empty.", nameof(s)); + } + if (g == Guid.Empty) + { + throw new ArgumentException("Guid cannot be empty.", nameof(g)); + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/GuardedPropertyHolder.cs b/tests/TestTypeFoundation/GuardedPropertyHolder.cs new file mode 100644 index 0000000..4a8adc6 --- /dev/null +++ b/tests/TestTypeFoundation/GuardedPropertyHolder.cs @@ -0,0 +1,25 @@ +namespace TestTypeFoundation; + +public class GuardedPropertyHolder + where T : class +{ + private T property; + + public T Property + { + get + { + return this.property; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + this.property = value; + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/GuardedStaticMethodOnStaticTypeHost.cs b/tests/TestTypeFoundation/GuardedStaticMethodOnStaticTypeHost.cs new file mode 100644 index 0000000..258b40b --- /dev/null +++ b/tests/TestTypeFoundation/GuardedStaticMethodOnStaticTypeHost.cs @@ -0,0 +1,12 @@ +namespace TestTypeFoundation; + +public static class GuardedStaticMethodOnStaticTypeHost +{ + public static void Method(object argument) + { + if (argument == null) + { + throw new ArgumentNullException(nameof(argument)); + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/IInterface.cs b/tests/TestTypeFoundation/IInterface.cs new file mode 100644 index 0000000..e3377b0 --- /dev/null +++ b/tests/TestTypeFoundation/IInterface.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public interface IInterface +{ + object MakeIt(object obj); +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/IllBehavedPropertyHolder.cs b/tests/TestTypeFoundation/IllBehavedPropertyHolder.cs new file mode 100644 index 0000000..63a9bd2 --- /dev/null +++ b/tests/TestTypeFoundation/IllBehavedPropertyHolder.cs @@ -0,0 +1,31 @@ +namespace TestTypeFoundation; + +public class IllBehavedPropertyHolder +{ + private T propertyIllBehavedSet; + + public T PropertyIllBehavedGet + { + get + { + return default(T); + } + + set + { + } + } + + public T PropertyIllBehavedSet + { + get + { + return this.propertyIllBehavedSet; + } + + set + { + this.propertyIllBehavedSet = default(T); + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/IndexedPropertyHolder.cs b/tests/TestTypeFoundation/IndexedPropertyHolder.cs new file mode 100644 index 0000000..45f6adf --- /dev/null +++ b/tests/TestTypeFoundation/IndexedPropertyHolder.cs @@ -0,0 +1,14 @@ +namespace TestTypeFoundation; + +public class IndexedPropertyHolder +{ + private readonly List items = + [ + ]; + + public T this[int index] + { + get { return this.items[index]; } + set { this.items[index] = value; } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/InternalGetterPropertyHolder.cs b/tests/TestTypeFoundation/InternalGetterPropertyHolder.cs new file mode 100644 index 0000000..04e5283 --- /dev/null +++ b/tests/TestTypeFoundation/InternalGetterPropertyHolder.cs @@ -0,0 +1,11 @@ +namespace TestTypeFoundation; + +public class InternalGetterPropertyHolder +{ + public InternalGetterPropertyHolder(T property) + { + this.Property = property; + } + + public T Property { internal get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ItemContainer.cs b/tests/TestTypeFoundation/ItemContainer.cs new file mode 100644 index 0000000..77395dd --- /dev/null +++ b/tests/TestTypeFoundation/ItemContainer.cs @@ -0,0 +1,21 @@ +namespace TestTypeFoundation; + +public class ItemContainer +{ + public ItemContainer(IEnumerable items) + : this(items.ToArray()) + { + } + + public ItemContainer(IList items) + : this(items.ToArray()) + { + } + + public ItemContainer(params T[] items) + { + this.Items = items; + } + + public IEnumerable Items { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ItemHolder.cs b/tests/TestTypeFoundation/ItemHolder.cs new file mode 100644 index 0000000..709e202 --- /dev/null +++ b/tests/TestTypeFoundation/ItemHolder.cs @@ -0,0 +1,62 @@ +namespace TestTypeFoundation; + +public class ItemHolder +{ + public ItemHolder() + { + } + + public ItemHolder(T1 item) + : this([item], [ + ]) + { + } + + public ItemHolder(T2 item) + : this([ + ], [item]) + { + } + + private ItemHolder(T1[] t1S, T2[] t2S) + { + this.Item1S = t1S; + this.Item2S = t2S; + } + + public IEnumerable Item1S { get; } + + public IEnumerable Item2S { get; } +} + +/* Note that constructors must be unordered because this class is used to test that + * constructors are correctly ordered by various implementations of IMethodQuery. For that + * reason, please don't be a boy scout and order constructors 'nicely'. */ +public class ItemHolder +{ + public ItemHolder(T x, T y, T z) + : this([x, y, z]) + { + } + + public ItemHolder(T item) + : this([item]) + { + } + + public ItemHolder() + { + } + + public ItemHolder(T x, T y) + : this([x, y]) + { + } + + private ItemHolder(T[] items) + { + this.Items = items; + } + + public IEnumerable Items { get; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/MultiUnorderedConstructorType.cs b/tests/TestTypeFoundation/MultiUnorderedConstructorType.cs new file mode 100644 index 0000000..c51e8a2 --- /dev/null +++ b/tests/TestTypeFoundation/MultiUnorderedConstructorType.cs @@ -0,0 +1,47 @@ +namespace TestTypeFoundation; + +public class MultiUnorderedConstructorType +{ + public MultiUnorderedConstructorType(ParameterObject paramObj) + : this(paramObj.Text, paramObj.Number) + { + } + + public MultiUnorderedConstructorType() + : this(string.Empty, 0) + { + } + + public MultiUnorderedConstructorType(string text, int number) + { + if (text == null) + { + throw new ArgumentNullException(nameof(text)); + } + + this.Text = text; + this.Number = number; + } + + public string Text { get; } + + public int Number { get; } + + public class ParameterObject + { + public ParameterObject(string text, int number) + { + if (text == null) + { + throw new ArgumentNullException(nameof(text)); + } + + this.Text = text; + this.Number = number; + } + + public string Text { get; } + + public int Number { get; } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/MutableValueType.cs b/tests/TestTypeFoundation/MutableValueType.cs new file mode 100644 index 0000000..724247c --- /dev/null +++ b/tests/TestTypeFoundation/MutableValueType.cs @@ -0,0 +1,15 @@ +namespace TestTypeFoundation; + +public struct MutableValueType +{ + public MutableValueType(object property1, object property2, object property3) + { + this.Property1 = property1; + this.Property2 = property2; + this.Property3 = property3; + } + + public object Property1 { get; set; } + public object Property2 { get; set; } + public object Property3 { get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/MutableValueTypeWithoutConstructor.cs b/tests/TestTypeFoundation/MutableValueTypeWithoutConstructor.cs new file mode 100644 index 0000000..158d1a3 --- /dev/null +++ b/tests/TestTypeFoundation/MutableValueTypeWithoutConstructor.cs @@ -0,0 +1,7 @@ +namespace TestTypeFoundation; + +public struct MutableValueTypeWithoutConstructor +{ + public object Property1 { get; set; } + public object Property2 { get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/NonCompliantCollectionHolder.cs b/tests/TestTypeFoundation/NonCompliantCollectionHolder.cs new file mode 100644 index 0000000..00205a8 --- /dev/null +++ b/tests/TestTypeFoundation/NonCompliantCollectionHolder.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class NonCompliantCollectionHolder +{ + public ICollection Collection { get; set; } = new List(); +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/NoopInterfaceImplementer.cs b/tests/TestTypeFoundation/NoopInterfaceImplementer.cs new file mode 100644 index 0000000..b99cd27 --- /dev/null +++ b/tests/TestTypeFoundation/NoopInterfaceImplementer.cs @@ -0,0 +1,9 @@ +namespace TestTypeFoundation; + +public class NoopInterfaceImplementer : IInterface +{ + public object MakeIt(object obj) + { + return obj; + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/Properties/AssemblyInfo.cs b/tests/TestTypeFoundation/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1d950fa --- /dev/null +++ b/tests/TestTypeFoundation/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7ef374e5-5287-4977-a405-2b330be7fd26")] diff --git a/tests/TestTypeFoundation/PropertyHolder.cs b/tests/TestTypeFoundation/PropertyHolder.cs new file mode 100644 index 0000000..b1e471c --- /dev/null +++ b/tests/TestTypeFoundation/PropertyHolder.cs @@ -0,0 +1,19 @@ +using System.Reflection; + +namespace TestTypeFoundation; + +public class PropertyHolder +{ + public T Property { get; set; } + + public void SetProperty(T value) + { + this.Property = value; + } + + public static PropertyInfo GetProperty() + { + return typeof(PropertyHolder) + .GetRuntimeProperty(nameof(Property)); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/QuadrupleParameterType.cs b/tests/TestTypeFoundation/QuadrupleParameterType.cs new file mode 100644 index 0000000..ccb9147 --- /dev/null +++ b/tests/TestTypeFoundation/QuadrupleParameterType.cs @@ -0,0 +1,17 @@ +namespace TestTypeFoundation; + +public class QuadrupleParameterType +{ + public QuadrupleParameterType(T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4) + { + this.Parameter1 = parameter1; + this.Parameter2 = parameter2; + this.Parameter3 = parameter3; + this.Parameter4 = parameter4; + } + + public T1 Parameter1 { get; private set; } + public T2 Parameter2 { get; private set; } + public T3 Parameter3 { get; private set; } + public T4 Parameter4 { get; private set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ReadOnlyFieldHolder.cs b/tests/TestTypeFoundation/ReadOnlyFieldHolder.cs new file mode 100644 index 0000000..30250c9 --- /dev/null +++ b/tests/TestTypeFoundation/ReadOnlyFieldHolder.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class ReadOnlyFieldHolder +{ + public readonly T Field; +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ReadOnlyPropertyHolder.cs b/tests/TestTypeFoundation/ReadOnlyPropertyHolder.cs new file mode 100644 index 0000000..392624a --- /dev/null +++ b/tests/TestTypeFoundation/ReadOnlyPropertyHolder.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class ReadOnlyPropertyHolder +{ + public T Property { get; private set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/RecordType.cs b/tests/TestTypeFoundation/RecordType.cs new file mode 100644 index 0000000..7ad88cd --- /dev/null +++ b/tests/TestTypeFoundation/RecordType.cs @@ -0,0 +1,36 @@ +namespace TestTypeFoundation; + +public class RecordType : IEquatable> +{ + public RecordType(T value) + { + this.Value = value; + } + + public T Value { get; } + + public bool Equals(RecordType other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return EqualityComparer.Default.Equals(this.Value, other.Value); + } + + public override bool Equals(object obj) + { + return this.Equals(obj as RecordType); + } + + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(this.Value); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/SingleParameterType.cs b/tests/TestTypeFoundation/SingleParameterType.cs new file mode 100644 index 0000000..e157947 --- /dev/null +++ b/tests/TestTypeFoundation/SingleParameterType.cs @@ -0,0 +1,11 @@ +namespace TestTypeFoundation; + +public class SingleParameterType +{ + public SingleParameterType(T parameter) + { + this.Parameter = parameter; + } + + public T Parameter { get; private set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/StaticFieldHolder.cs b/tests/TestTypeFoundation/StaticFieldHolder.cs new file mode 100644 index 0000000..738e055 --- /dev/null +++ b/tests/TestTypeFoundation/StaticFieldHolder.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class StaticFieldHolder +{ + public static T Field; +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/StaticPropertyHolder.cs b/tests/TestTypeFoundation/StaticPropertyHolder.cs new file mode 100644 index 0000000..d95b0f7 --- /dev/null +++ b/tests/TestTypeFoundation/StaticPropertyHolder.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class StaticPropertyHolder +{ + public static T Property { get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TestTypeFoundation.csproj b/tests/TestTypeFoundation/TestTypeFoundation.csproj new file mode 100644 index 0000000..3c04f23 --- /dev/null +++ b/tests/TestTypeFoundation/TestTypeFoundation.csproj @@ -0,0 +1,18 @@ + + + + + + netstandard2.0 + TestTypeFoundation + TestTypeFoundation + disable + false + + + + + + + + diff --git a/tests/TestTypeFoundation/TriState.cs b/tests/TestTypeFoundation/TriState.cs new file mode 100644 index 0000000..c88423c --- /dev/null +++ b/tests/TestTypeFoundation/TriState.cs @@ -0,0 +1,8 @@ +namespace TestTypeFoundation; + +public enum TriState +{ + First = 0, + Second, + Third +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TripleParameterType.cs b/tests/TestTypeFoundation/TripleParameterType.cs new file mode 100644 index 0000000..952e515 --- /dev/null +++ b/tests/TestTypeFoundation/TripleParameterType.cs @@ -0,0 +1,15 @@ +namespace TestTypeFoundation; + +public class TripleParameterType +{ + public TripleParameterType(T1 parameter1, T2 parameter2, T3 parameter3) + { + this.Parameter1 = parameter1; + this.Parameter2 = parameter2; + this.Parameter3 = parameter3; + } + + public T1 Parameter1 { get; private set; } + public T2 Parameter2 { get; private set; } + public T3 Parameter3 { get; private set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TriplePropertyHolder.cs b/tests/TestTypeFoundation/TriplePropertyHolder.cs new file mode 100644 index 0000000..f9ec32a --- /dev/null +++ b/tests/TestTypeFoundation/TriplePropertyHolder.cs @@ -0,0 +1,10 @@ +namespace TestTypeFoundation; + +public class TriplePropertyHolder +{ + public T1 Property1 { get; set; } + + public T2 Property2 { get; set; } + + public T3 Property3 { get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithCastOperatorsWithoutPublicConstructor.cs b/tests/TestTypeFoundation/TypeWithCastOperatorsWithoutPublicConstructor.cs new file mode 100644 index 0000000..300d37c --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithCastOperatorsWithoutPublicConstructor.cs @@ -0,0 +1,18 @@ +namespace TestTypeFoundation; + +public class TypeWithCastOperatorsWithoutPublicConstructor +{ + private TypeWithCastOperatorsWithoutPublicConstructor() + { + } + + public static implicit operator TypeWithCastOperatorsWithoutPublicConstructor(int ignored) + { + return new TypeWithCastOperatorsWithoutPublicConstructor(); + } + + public static explicit operator TypeWithCastOperatorsWithoutPublicConstructor(string ignored) + { + return new TypeWithCastOperatorsWithoutPublicConstructor(); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithConcreteParameterMethod.cs b/tests/TestTypeFoundation/TypeWithConcreteParameterMethod.cs new file mode 100644 index 0000000..0007af5 --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithConcreteParameterMethod.cs @@ -0,0 +1,8 @@ +namespace TestTypeFoundation; + +public class TypeWithConcreteParameterMethod +{ + public void DoSomething(ConcreteType param) + { + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithEmptyEnumField.cs b/tests/TestTypeFoundation/TypeWithEmptyEnumField.cs new file mode 100644 index 0000000..4fcdd3c --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithEmptyEnumField.cs @@ -0,0 +1,8 @@ +namespace TestTypeFoundation; + +public class TypeWithEmptyEnumField +{ + public EmptyEnum EmptyEnumField; + + public EmptyEnum EmptyEnumProperty { get; set; } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithFactoryMethod.cs b/tests/TestTypeFoundation/TypeWithFactoryMethod.cs new file mode 100644 index 0000000..6921237 --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithFactoryMethod.cs @@ -0,0 +1,23 @@ +namespace TestTypeFoundation; + +public class TypeWithFactoryMethod +{ + private TypeWithFactoryMethod() + { + } + + public static TypeWithFactoryMethod Create() + { + return new TypeWithFactoryMethod(); + } + + public static TypeWithFactoryMethod Create(object argument) + { + return new TypeWithFactoryMethod(); + } + + public static TypeWithFactoryMethod Create(IEnumerable arguments) + { + return new TypeWithFactoryMethod(); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithFactoryProperty.cs b/tests/TestTypeFoundation/TypeWithFactoryProperty.cs new file mode 100644 index 0000000..8355290 --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithFactoryProperty.cs @@ -0,0 +1,16 @@ +namespace TestTypeFoundation; + +public class TypeWithFactoryProperty +{ + private TypeWithFactoryProperty() + { + } + + public static TypeWithFactoryProperty Factory + { + get + { + return new TypeWithFactoryProperty(); + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithIndexer.cs b/tests/TestTypeFoundation/TypeWithIndexer.cs new file mode 100644 index 0000000..e5b7c0f --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithIndexer.cs @@ -0,0 +1,18 @@ +namespace TestTypeFoundation; + +public class TypeWithIndexer +{ + private readonly Dictionary dict = new(); + + public string this[string index] + { + get + { + return this.dict[index]; + } + set + { + this.dict[index] = value; + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithOverloadedMembers.cs b/tests/TestTypeFoundation/TypeWithOverloadedMembers.cs new file mode 100644 index 0000000..7751e05 --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithOverloadedMembers.cs @@ -0,0 +1,28 @@ +using System.Reflection; + +namespace TestTypeFoundation; + +public class TypeWithOverloadedMembers +{ + public object SomeProperty { get; set; } + + public void DoSomething() + { + } + + public void DoSomething(object obj) + { + } + + public void DoSomething(object x, object y) + { + } + + public void DoSomething(object x, object y, object z) + { + } + + public static MethodInfo GetDoSomethingMethod(params Type[] parameterTypes) => + typeof(TypeWithOverloadedMembers) + .GetMethod(nameof(DoSomething), parameterTypes); +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithPseudoFactoryMethod.cs b/tests/TestTypeFoundation/TypeWithPseudoFactoryMethod.cs new file mode 100644 index 0000000..695353f --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithPseudoFactoryMethod.cs @@ -0,0 +1,20 @@ +namespace TestTypeFoundation; + +/// +/// This type contains a method that returns an instance of the same type +/// so that it looks like a Factory Method. In fact, it is not because it +/// receives a parameter of the same type, so using it as a factory inevitably +/// leads to circular dependencies. +/// +public class TypeWithPseudoFactoryMethod +{ + private TypeWithPseudoFactoryMethod() + { + } + + public static TypeWithPseudoFactoryMethod DoSomething( + TypeWithPseudoFactoryMethod argument) + { + return new TypeWithPseudoFactoryMethod(); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/TypeWithRefMethod.cs b/tests/TestTypeFoundation/TypeWithRefMethod.cs new file mode 100644 index 0000000..fb54c33 --- /dev/null +++ b/tests/TestTypeFoundation/TypeWithRefMethod.cs @@ -0,0 +1,16 @@ +namespace TestTypeFoundation; + +public class TypeWithRefMethod +{ + public void InvokeIt(T x, ref T y) + { + if (x == null) + { + throw new ArgumentNullException(nameof(x)); + } + if (y == null) + { + throw new ArgumentNullException(nameof(y)); + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/UnguardedConstructorHost.cs b/tests/TestTypeFoundation/UnguardedConstructorHost.cs new file mode 100644 index 0000000..5ae0dc6 --- /dev/null +++ b/tests/TestTypeFoundation/UnguardedConstructorHost.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace TestTypeFoundation; + +public class UnguardedConstructorHost +{ + public UnguardedConstructorHost(T item) + { + this.Item = item; + } + + public T Item { get; } + + private static ConstructorInfo GetConstructor() + { + var typeInfo = typeof(UnguardedConstructorHost) + .GetTypeInfo(); + + return typeInfo.DeclaredConstructors.Single(); + } + + public static ParameterInfo GetParameter() + { + return GetConstructor().GetParameters().Single(); + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/UnguardedMethodHost.cs b/tests/TestTypeFoundation/UnguardedMethodHost.cs new file mode 100644 index 0000000..6e06bb3 --- /dev/null +++ b/tests/TestTypeFoundation/UnguardedMethodHost.cs @@ -0,0 +1,16 @@ +namespace TestTypeFoundation; + +public class UnguardedMethodHost +{ + public void ConsumeUnguardedString(string s) + { + } + + public void ConsumeGuardedGuidAndUnguardedString(Guid g, string s) + { + if (g == Guid.Empty) + { + throw new ArgumentException("Guid cannot be empty.", nameof(g)); + } + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/UnguardedStaticMethodOnStaticTypeHost.cs b/tests/TestTypeFoundation/UnguardedStaticMethodOnStaticTypeHost.cs new file mode 100644 index 0000000..338390c --- /dev/null +++ b/tests/TestTypeFoundation/UnguardedStaticMethodOnStaticTypeHost.cs @@ -0,0 +1,8 @@ +namespace TestTypeFoundation; + +public static class UnguardedStaticMethodOnStaticTypeHost +{ + public static void Method(object argument) + { + } +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/UnguardedStaticPropertyOnStaticTypeHost.cs b/tests/TestTypeFoundation/UnguardedStaticPropertyOnStaticTypeHost.cs new file mode 100644 index 0000000..92c1dfe --- /dev/null +++ b/tests/TestTypeFoundation/UnguardedStaticPropertyOnStaticTypeHost.cs @@ -0,0 +1,10 @@ +namespace TestTypeFoundation; + +public static class UnguardedStaticPropertyOnStaticTypeHost +{ + public static object Property + { + get; + set; + } +} \ No newline at end of file