diff --git a/.editorconfig b/.editorconfig index 69ef33e..2e19d1c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,9 +8,14 @@ indent_style = space indent_size = 2 indent_style = space -[*.cs] -# 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 +[*] + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers = false +csharp_preferred_modifier_order = public, private, protected, internal, file, static, new, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion + +# ReSharper properties +resharper_blank_lines_around_auto_property = 1 +resharper_blank_lines_around_field = 1 +resharper_blank_lines_around_property = 1 +resharper_braces_for_ifelse = required diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 75d776d..b96acab 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -105,11 +105,11 @@ "allOf": [ { "properties": { - "CI": { + "_ci": { "type": "boolean", "description": "Forces the continuous integration build flag" }, - "Configuration": { + "_configuration": { "type": "string", "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", "enum": [ @@ -118,6 +118,10 @@ "Verify" ] }, + "_solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded. Default is AutoFixture.TUnit.sln" + }, "github-token": { "type": "string", "description": "GitHub auth token", @@ -127,10 +131,6 @@ "type": "string", "description": "NuGet API Key (secret)", "default": "Secrets must be entered via 'nuke :secrets [profile]'" - }, - "Solution": { - "type": "string", - "description": "Path to a solution file that is automatically loaded" } } }, diff --git a/AutoFixture.TUnit.sln b/AutoFixture.TUnit.sln index 92436f4..425e306 100644 --- a/AutoFixture.TUnit.sln +++ b/AutoFixture.TUnit.sln @@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution LICENCE.txt = LICENCE.txt 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 +43,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.globalconfig b/AutoFixture.globalconfig index 297b7df..3f911a7 100644 --- a/AutoFixture.globalconfig +++ b/AutoFixture.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/build/Build.cs b/build/Build.cs index 763f543..9ce5c61 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); @@ -41,31 +41,31 @@ partial class Build : NukeBuild const string ReleaseBranch = "release/*"; [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] - readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; + readonly Configuration _configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; - [Solution] readonly Solution Solution; - [GitRepository] readonly GitRepository GitRepository; - [GitVersion] readonly GitVersion GitVersion; - [CI] readonly GitHubActions GitHubActions; + [Solution("AutoFixture.TUnit.sln")] readonly Solution _solution; + [GitRepository] readonly GitRepository _gitRepository; + [GitVersion] readonly GitVersion _gitVersion; + [CI] readonly GitHubActions _gitHubActions; - [Parameter("GitHub auth token", Name = "github-token"), Secret] readonly string GitHubToken; - [Parameter("Forces the continuous integration build flag")] readonly bool CI; + [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; - readonly string NuGetSource = "https://api.nuget.org/v3/index.json"; + [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[] - { - Solution.GetProject("_build"), - Solution.GetProject("TestTypeFoundation") - }; + IEnumerable Excluded => + [ + _solution.GetProject("_build"), + _solution.GetProject("TestTypeFoundation") + ]; - IEnumerable TestProjects => Solution.GetAllProjects("*Tests"); - IEnumerable Libraries => Solution.Projects.Except(TestProjects).Except(Excluded); + IEnumerable TestProjects => _solution.GetAllProjects("*Tests"); + IEnumerable Libraries => _solution.Projects.Except(TestProjects).Except(Excluded); IEnumerable CSharpLibraries => Libraries.Where(x => x.Is(ProjectType.CSharpProject)); IEnumerable Packages => PackagesDirectory.GlobFiles("*.nupkg"); - bool IsContinuousIntegration => IsServerBuild || CI; + bool IsContinuousIntegration => IsServerBuild || _ci; AbsolutePath SourceDirectory => RootDirectory / "src"; AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; @@ -87,7 +87,7 @@ partial class Build : NukeBuild Target Restore => _ => _ .Executes(() => { - DotNetRestore(s => s.SetProjectFile(Solution)); + DotNetRestore(s => s.SetProjectFile(_solution)); }); Target Verify => _ => _ @@ -96,7 +96,7 @@ partial class Build : NukeBuild .Executes(() => { DotNetBuild(s => s - .SetProjectFile(Solution) + .SetProjectFile(_solution) .SetConfiguration(Configuration.Verify) .SetNoRestore(FinishedTargets.Contains(Restore)) .SetContinuousIntegrationBuild(IsContinuousIntegration)); @@ -107,14 +107,14 @@ partial class Build : NukeBuild .Executes(() => { DotNetBuild(s => s - .SetProjectFile(Solution) - .SetConfiguration(Configuration) + .SetProjectFile(_solution) + .SetConfiguration(_configuration) .SetDeterministic(IsContinuousIntegration) .SetContinuousIntegrationBuild(IsContinuousIntegration) - .SetVersion(GitVersion.NuGetVersionV2) - .SetAssemblyVersion(GitVersion.AssemblySemVer) - .SetFileVersion(GitVersion.AssemblySemFileVer) - .SetInformationalVersion(GitVersion.InformationalVersion) + .SetVersion(_gitVersion.NuGetVersionV2) + .SetAssemblyVersion(_gitVersion.AssemblySemVer) + .SetFileVersion(_gitVersion.AssemblySemFileVer) + .SetInformationalVersion(_gitVersion.InformationalVersion) .SetNoRestore(FinishedTargets.Contains(Restore))); }); @@ -124,8 +124,8 @@ partial class Build : NukeBuild .Executes(() => { DotNetTest(s => s - .SetProjectFile(Solution) - .SetConfiguration(Configuration) + .SetProjectFile(_solution) + .SetConfiguration(_configuration) .SetResultsDirectory(TestResultsDirectory) .SetNoBuild(FinishedTargets.Contains(Compile)) .When(_ => InvokedTargets.Contains(Cover), _ => _ @@ -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) @@ -166,17 +166,17 @@ partial class Build : NukeBuild .Executes(() => { DotNetPack(s => s - .SetConfiguration(Configuration) + .SetConfiguration(_configuration) .SetNoBuild(FinishedTargets.Contains(Compile)) .SetOutputDirectory(PackagesDirectory) .SetSymbolPackageFormat(DotNetSymbolPackageFormat.snupkg) .EnableIncludeSymbols() .SetDeterministic(IsContinuousIntegration) .SetContinuousIntegrationBuild(IsContinuousIntegration) - .SetVersion(GitVersion.NuGetVersionV2) - .SetAssemblyVersion(GitVersion.AssemblySemVer) - .SetFileVersion(GitVersion.AssemblySemFileVer) - .SetInformationalVersion(GitVersion.InformationalVersion) + .SetVersion(_gitVersion.NuGetVersionV2) + .SetAssemblyVersion(_gitVersion.AssemblySemVer) + .SetFileVersion(_gitVersion.AssemblySemFileVer) + .SetInformationalVersion(_gitVersion.InformationalVersion) .CombineWith(CSharpLibraries, (s, p) => s.SetProject(p))); }); @@ -188,10 +188,10 @@ partial class Build : NukeBuild DotNetNuGetPush(s => s .EnableSkipDuplicate() .When( - _ => GitHubActions.IsOnSemVerTag(), + _ => _gitHubActions.IsOnSemVerTag(), v => v - .SetApiKey(NuGetApiKey) - .SetSource(NuGetSource)) + .SetApiKey(_nuGetApiKey) + .SetSource(_nuGetSource)) .CombineWith(Packages, (_, p) => _.SetTargetPath(p))); }); diff --git a/build/GitHubActionsExtensions.cs b/build/GitHubActionsExtensions.cs index 3c811e3..acc4949 100644 --- a/build/GitHubActionsExtensions.cs +++ b/build/GitHubActionsExtensions.cs @@ -1,15 +1,14 @@ using System.Text.RegularExpressions; -namespace Nuke.Common.CI.GitHubActions +namespace Nuke.Common.CI.GitHubActions; + +public static 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 = new(@"^refs\/tags\/v(?\d+\.\d+\.\d+)", RegexOptions.Compiled); - 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); } } \ No newline at end of file diff --git a/src/AutoFixture.TUnit/ArgumentsAutoDataAttribute.cs b/src/AutoFixture.TUnit/ArgumentsAutoDataAttribute.cs new file mode 100644 index 0000000..87be067 --- /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) + { + FixtureFactory = fixtureFactory ?? throw new ArgumentNullException(nameof(fixtureFactory)); + 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(FixtureFactory, new InlineDataSource(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..f233a2b --- /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) + { + 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(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..beb578c --- /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 = 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..1b837f8 --- /dev/null +++ b/src/AutoFixture.TUnit/ClassAutoDataAttribute.cs @@ -0,0 +1,102 @@ +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. + /// + /// [Theory] + /// [CustomAutoClassData(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 CustomAutoClassData : ClassAutoDataAttribute + /// { + /// public CustomAutoClassData(Type sourceType) : + /// base(() => new Fixture(), sourceType) + /// { + /// } + /// } + /// + /// 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) + { + FixtureFactory = fixtureFactory ?? throw new ArgumentNullException(nameof(fixtureFactory)); + SourceType = sourceType ?? throw new ArgumentNullException(nameof(sourceType)); + 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( + FixtureFactory, + new ClassDataSource(SourceType, 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..792472f --- /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(_attributes); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + if (dataGeneratorMetadata is null) + { + throw new ArgumentNullException(nameof(dataGeneratorMetadata)); + } + + var results = _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/Extensions/DataGeneratorMetadataExtensions.cs b/src/AutoFixture.TUnit/Extensions/DataGeneratorMetadataExtensions.cs new file mode 100644 index 0000000..82a51e0 --- /dev/null +++ b/src/AutoFixture.TUnit/Extensions/DataGeneratorMetadataExtensions.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using TUnit.Core.Enums; + +namespace AutoFixture.TUnit.Extensions; + +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/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..2112d18 --- /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) + { + 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(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..22626fa --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/Argument.cs @@ -0,0 +1,10 @@ +namespace AutoFixture.TUnit.Internal; + +internal sealed class Argument(TestParameter parameter, object? value) +{ + public TestParameter Parameter { get; } = parameter ?? throw new ArgumentNullException(nameof(parameter)); + + public object? Value { get; } = value; + + public ICustomization GetCustomization() => Parameter.GetCustomization(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..5714986 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/AutoDataSource.cs @@ -0,0 +1,92 @@ +using AutoFixture.TUnit.Extensions; + +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) + { + CreateFixture = createFixture ?? throw new ArgumentNullException(nameof(createFixture)); + 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 Source is null + ? GenerateValues(dataGeneratorMetadata) + : CombineValues(dataGeneratorMetadata, Source); + } + + private IEnumerable GenerateValues(DataGeneratorMetadata metadata) + { + var parameters = Array.ConvertAll(metadata.GetMethod().GetParameters(), TestParameter.From); + var fixture = 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 = 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..7b8ed69 --- /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) + { + 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(_parameters); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var instance = Activator.CreateInstance(type: Type, args: _parameters); + + if (instance is not IEnumerable enumerable) + { + throw new InvalidOperationException($"Data source type \"{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..90f4f6b --- /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/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..53fbba0 --- /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) + { + 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 = 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..2be3440 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/FrozenValueCustomization.cs @@ -0,0 +1,17 @@ +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Internal; + +internal sealed class FrozenValueCustomization(IRequestSpecification specification, object? value) : ICustomization +{ + private readonly IRequestSpecification _specification = specification ?? throw new ArgumentNullException(nameof(specification)); + + public void Customize(IFixture fixture) + { + var builder = new FilteringSpecimenBuilder( + builder: new FixedBuilder(value), + specification: _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..be0f78d --- /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(_values); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + if (dataGeneratorMetadata is null) + { + throw new ArgumentNullException(nameof(dataGeneratorMetadata)); + } + + var membersToGenerate = dataGeneratorMetadata.MembersToGenerate; + if (_values.Length > membersToGenerate.Length) + { + throw new InvalidOperationException( + "The number of arguments provided exceeds the number of parameters."); + } + + yield return _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..051b500 --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/MemberDataSource.cs @@ -0,0 +1,88 @@ +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) + { + Type = type ?? throw new ArgumentNullException(nameof(type)); + Name = name ?? throw new ArgumentNullException(nameof(name)); + this._arguments = arguments ?? throw new ArgumentNullException(nameof(arguments)); + Source = 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(_arguments); + + /// + /// Gets the test data source. + /// + protected DataSource Source { get; } + + private DataSource GetTestDataSource() + { + var sourceMember = Type.GetMember(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}", + Name, 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", + Name, Type.FullName); + throw new ArgumentException(message); + } + + return sourceMember switch + { + FieldInfo fieldInfo => new FieldDataSource(fieldInfo), + PropertyInfo propertyInfo => new PropertyDataSource(propertyInfo), + MethodInfo methodInfo => new MethodDataSource(methodInfo, _arguments), + _ => throw new InvalidOperationException("Unsupported member type.") + }; + } + + /// + public IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + return 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..4c27946 --- /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) + { + 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(_arguments); + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var value = MethodInfo.Invoke(null, _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..79c5576 --- /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) + { + ParameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo)); + Flags = flags; + _matcherSpecification = new ParameterMatcherBuilder(ParameterInfo) + .SetFlags(Flags).Build(); + } + + /// + /// Gets the source parameter. + /// + public ParameterInfo ParameterInfo { get; } + + /// + /// Gets the matching flags. + /// + public Matching Flags { get; } + + /// + public bool IsSatisfiedBy(object request) + { + return _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..b83a31e --- /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) + { + MatchExactType = flags.HasFlag(Matching.ExactType); + MatchDirectBaseType = flags.HasFlag(Matching.DirectBaseType); + MatchImplementedInterfaces = flags.HasFlag(Matching.ImplementedInterfaces); + MatchParameter = flags.HasFlag(Matching.ParameterName); + MatchProperty = flags.HasFlag(Matching.PropertyName); + 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 (MatchExactRequest) + { + specifications.Add(AsExactRequest()); + } + + if (MatchExactType) + { + specifications.Add(AsExactType()); + } + + if (MatchDirectBaseType) + { + specifications.Add(AsDirectBaseType()); + } + + if (MatchImplementedInterfaces) + { + specifications.Add(AsImplementedInterfaces()); + } + + if (MatchProperty) + { + specifications.Add(AsProperty()); + } + + if (MatchParameter) + { + specifications.Add(AsParameter()); + } + + if (MatchField) + { + specifications.Add(AsField()); + } + + return specifications.Count == 1 + ? specifications[0] + : new OrRequestSpecification(specifications); + } + + private IRequestSpecification AsExactRequest() + { + return new EqualRequestSpecification(_parameterInfo); + } + + private IRequestSpecification AsExactType() + { + return new OrRequestSpecification( + new ExactTypeSpecification(_parameterInfo.ParameterType), + new SeedRequestSpecification(_parameterInfo.ParameterType)); + } + + private IRequestSpecification AsDirectBaseType() + { + return new AndRequestSpecification( + new InverseRequestSpecification( + new ExactTypeSpecification(_parameterInfo.ParameterType)), + new DirectBaseTypeSpecification(_parameterInfo.ParameterType)); + } + + private IRequestSpecification AsImplementedInterfaces() + { + return new AndRequestSpecification( + new InverseRequestSpecification( + new ExactTypeSpecification(_parameterInfo.ParameterType)), + new ImplementedInterfaceSpecification(_parameterInfo.ParameterType)); + } + + private IRequestSpecification AsParameter() + { + return new ParameterSpecification( + new ParameterTypeAndNameCriterion( + new Criterion(_parameterInfo.ParameterType, new DerivesFromTypeComparer()), + new Criterion(_parameterInfo.Name, StringComparer.OrdinalIgnoreCase))); + } + + private IRequestSpecification AsProperty() + { + return new PropertySpecification( + new PropertyTypeAndNameCriterion( + new Criterion(_parameterInfo.ParameterType, new DerivesFromTypeComparer()), + new Criterion(_parameterInfo.Name, StringComparer.OrdinalIgnoreCase))); + } + + private IRequestSpecification AsField() + { + return new FieldSpecification( + new FieldTypeAndNameCriterion( + new Criterion(_parameterInfo.ParameterType, new DerivesFromTypeComparer()), + new Criterion(_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..fb4ff60 --- /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) + { + PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo)); + } + + /// + /// Gets the source property. + /// + public PropertyInfo PropertyInfo { get; } + + /// + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + var value = 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..ed54cad --- /dev/null +++ b/src/AutoFixture.TUnit/Internal/TestParameter.cs @@ -0,0 +1,49 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Internal; + +internal sealed class TestParameter(ParameterInfo parameterInfo) +{ + private readonly Lazy _lazyCustomization = new( + () => GetCustomization(parameterInfo)); + + private readonly Lazy _lazyFrozenAttribute = new( + () => parameterInfo.GetCustomAttributes() + .OfType().FirstOrDefault()); + + public ParameterInfo ParameterInfo { get; } = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo)); + + public ICustomization GetCustomization() => _lazyCustomization.Value; + + public ICustomization GetCustomization(object? value) + { + var frozenAttribute = _lazyFrozenAttribute.Value; + + if (frozenAttribute is null) + { + return NullCustomization.Instance; + } + + return new FrozenValueCustomization( + new ParameterFilter(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..712ef02 --- /dev/null +++ b/src/AutoFixture.TUnit/MemberAutoDataAttribute.cs @@ -0,0 +1,106 @@ +using System.Diagnostics.CodeAnalysis; +using AutoFixture.TUnit.Extensions; +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) + { + FixtureFactory = fixtureFactory ?? throw new ArgumentNullException(nameof(fixtureFactory)); + MemberName = memberName ?? throw new ArgumentNullException(nameof(memberName)); + Parameters = parameters ?? [null!]; + 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 = MemberType ?? testMethod.DeclaringType + ?? throw new InvalidOperationException("Source type cannot be null."); + + var source = new AutoDataSource( + createFixture: FixtureFactory, + source: new MemberDataSource(sourceType, MemberName, 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..fe454f5 --- /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..38fc7ec 100644 --- a/tests/AutoFixture.TUnit.Tests/AutoFixture.TUnit.Tests.csproj +++ b/tests/AutoFixture.TUnit.Tests/AutoFixture.TUnit.Tests.csproj @@ -18,6 +18,7 @@ + @@ -26,6 +27,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..58c5eb8 --- /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 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..2bd16f3 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/CompositeDataAttributeSufficientDataTest.cs @@ -0,0 +1,207 @@ +using System.Reflection; +using AutoFixture.TUnit.Tests.TestTypes; +using TestTypeFoundation; +using TUnit.Assertions.Equality; + +namespace AutoFixture.TUnit.Tests; + +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(_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(_method, [[1, 2, 3]]) + ], + expected: + [ + [1, 2, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1, 2, 3]]), + new FakeDataAttribute(_method, [[4, 5, 6]]) + ], + expected: + [ + [1, 2, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1]]), + new FakeDataAttribute(_method, [[2, 3, 4]]) + ], + expected: + [ + [1, 3, 4] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1, 2]]), + new FakeDataAttribute(_method, [[3, 4, 5]]) + ], + expected: + [ + [1, 2, 5] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1, 2, 3], [4, 5, 6]]) + ], + expected: + [ + [1, 2, 3], + [4, 5, 6] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1, 2, 3], [4, 5, 6]]), + new FakeDataAttribute(_method, + [[7, 8], [9, 10], [11, 12]]) + ], + expected: + [ + [1, 2, 3], + [4, 5, 6] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, + [[1, 2], [3, 4], [5, 6]]), + new FakeDataAttribute(_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(_method, [[1, 2, 3], [4, 5, 6]]), + new FakeDataAttribute(_method, [[7, 8, 9]]) + ], + expected: + [ + [1, 2, 3] + ]); + + // Shortest data provider is limiting factor + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1, 2, 3]]), + new FakeDataAttribute(_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(_method, [[1, 2]]), + new FakeDataAttribute(_method, [[3, 4]]) + ], + expected: + [ + [1, 2] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1]]), + new FakeDataAttribute(_method, [[2, 3]]) + ], + expected: + [ + [1, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1]]), + new FakeDataAttribute(_method, [[]]), + new FakeDataAttribute(_method, [[2, 3]]) + ], + expected: + [ + [1, 3] + ]); + + yield return CreateTestData( + data: + [ + new FakeDataAttribute(_method, [[1]]), + new FakeDataAttribute(_method, [[2]]), + new FakeDataAttribute(_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..b6b076d --- /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 = 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..53df50b --- /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..dc2d875 --- /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() => 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..ee97832 --- /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..214a0b5 --- /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..47ce3a0 --- /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..0f8c3e6 --- /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..f001d08 --- /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(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(SutIsTestDataSource)); + + // Act & Assert + await Assert.That(() => + new MethodDataSource(methodInfo, null!)).ThrowsExactly(); + } + + [Test] + public async Task ConstructorSetsProperties() + { + // Arrange + var methodInfo = typeof(MethodDataSourceTests) + .GetMethod(nameof(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..76ba20b --- /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..d29e84b --- /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 != default, "Value should not be default").IsTrue(); + await Assert.That(c != default, "Value should not be default").IsTrue(); + } + + [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..8f201aa --- /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..59b3181 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/Scenario.cs @@ -0,0 +1,539 @@ +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(params object?[] values) + : ArgumentsAutoDataAttribute(() => 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(string memberName, params object?[] parameters) + : MemberAutoDataAttribute(() => 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() => 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() => GetEnumerator(); + } + + public class ParameterizedDataClass(int p1, string p2, double p3) : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return [p1, p2, p3]; + } + + IEnumerator IEnumerable.GetEnumerator() => 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..350cb9e --- /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, "Value should not be default").IsTrue(); + await Assert.That(c != 0, "Value should not be default").IsTrue(); + } + + 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..56af2ae --- /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() => 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..503ad89 --- /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() => 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..8545ec3 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/CompositeTypeWithOverloadedConstructors.cs @@ -0,0 +1,16 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class CompositeTypeWithOverloadedConstructors(IEnumerable items) +{ + public CompositeTypeWithOverloadedConstructors(params T[] items) + : this(items.AsEnumerable()) + { + } + + public CompositeTypeWithOverloadedConstructors(IList items) + : this(items.AsEnumerable()) + { + } + + public IEnumerable Items { get; } = items; +} \ 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..ed47d20 --- /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() + { + OnCustomize = _ => { }; + } + + public void Customize(IFixture fixture) + { + 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..aedfc51 --- /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 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..0deca77 --- /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 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..edddd31 --- /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 => _customizations; + + public bool OmitAutoProperties { get; set; } + + public int RepeatCount { get; set; } + + public IList ResidueCollectors => _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) + { + 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 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..dffe7f4 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DelegatingMemberDataSource.cs @@ -0,0 +1,9 @@ +using AutoFixture.TUnit.Internal; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DelegatingMemberDataSource(Type type, string name, params object?[] arguments) + : MemberDataSource(type, name, arguments) +{ + public DataSource GetSource() => 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..752af7d --- /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 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..f3d4f99 --- /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() => _data.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => 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..455976d --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedArgumentsAutoDataAttribute.cs @@ -0,0 +1,4 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class DerivedArgumentsAutoDataAttribute(Func fixtureFactory, params object?[] values) + : ArgumentsAutoDataAttribute(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..a09828b --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/DerivedAutoDataAttribute.cs @@ -0,0 +1,3 @@ +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class DerivedAutoDataAttribute(Func fixtureFactory) : AutoDataAttribute(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..98a1ce3 --- /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..3d54985 --- /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..726cd51 --- /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() => 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..63e7e60 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/FakeDataAttribute.cs @@ -0,0 +1,13 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class FakeDataAttribute(MethodInfo expectedMethod, IEnumerable output) : AutoFixtureDataSourceAttribute +{ + private readonly MethodInfo _expectedMethod = expectedMethod; + + public override IEnumerable GetData(DataGeneratorMetadata dataGeneratorMetadata) + { + return 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..bf42058 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/FixedParameterBuilder.cs @@ -0,0 +1,6 @@ +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class FixedParameterBuilder(string name, T value) + : FilteringSpecimenBuilder(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..d2842e8 --- /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..a975c1e --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/InlineFrozenValuesTestData.cs @@ -0,0 +1,32 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +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..e4f306e --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/InlinePrimitiveValuesTestData.cs @@ -0,0 +1,32 @@ +using System.Reflection; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +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..7e6a0bc --- /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() => 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..0a53403 --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterNameCriterion.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +internal class ParameterNameCriterion(IEquatable nameCriterion) : IEquatable +{ + public ParameterNameCriterion(string name) + : this(new Criterion(name, StringComparer.Ordinal)) + { + } + + /// + /// The name criterion originally passed in via the class' constructor. + /// + public IEquatable NameCriterion { get; } = nameCriterion ?? throw new ArgumentNullException(nameof(nameCriterion)); + + public bool Equals(ParameterInfo other) + { + return 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..b3aa6dd --- /dev/null +++ b/tests/AutoFixture.TUnit.Tests/TestTypes/ParameterizedClassData.cs @@ -0,0 +1,15 @@ +using System.Collections; +using TestTypeFoundation; + +namespace AutoFixture.TUnit.Tests.TestTypes; + +public class ParameterizedClassData(int p1, string p2, EnumType p3) : IEnumerable +{ + public IEnumerator GetEnumerator() + { + yield return [p1, p2, p3]; + yield return [p1, p2, p3]; + } + + IEnumerator IEnumerable.GetEnumerator() => 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..d2baa40 --- /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, "Value should not be default").IsTrue(); + await Assert.That(c != 0, "Value should not be default").IsTrue(); + } + + 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..9d11ce1 --- /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, "Value should not be default").IsTrue(); + await Assert.That(c != 0, "Value should not be default").IsTrue(); + } + + 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..7a9b13b --- /dev/null +++ b/tests/TestTypeFoundation/AbstractGenericType.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public abstract class AbstractGenericType(T t) +{ + public T Value { get; } = t; +} \ 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..d083f72 --- /dev/null +++ b/tests/TestTypeFoundation/AbstractTypeWithConstructorWithMultipleParameters.cs @@ -0,0 +1,10 @@ +namespace TestTypeFoundation; + +public abstract class AbstractTypeWithConstructorWithMultipleParameters( + T1 parameter1, + T2 parameter2) +{ + public T1 Property1 { get; } = parameter1; + + public T2 Property2 { get; } = parameter2; +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/AbstractTypeWithNonDefaultConstructor.cs b/tests/TestTypeFoundation/AbstractTypeWithNonDefaultConstructor.cs new file mode 100644 index 0000000..aab8b8a --- /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)); + } + + 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..847c05d --- /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)); + } + + 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..02d5ee6 --- /dev/null +++ b/tests/TestTypeFoundation/ConcreteType.cs @@ -0,0 +1,38 @@ +namespace TestTypeFoundation; + +public class ConcreteType : AbstractType +{ + public ConcreteType() + { + } + + public ConcreteType(object obj) + { + Property1 = obj; + } + + public ConcreteType(object obj1, object obj2) + { + Property1 = obj1; + Property2 = obj2; + } + + public ConcreteType(object obj1, object obj2, object obj3) + { + Property1 = obj1; + Property2 = obj2; + Property3 = obj3; + } + + public ConcreteType(object obj1, object obj2, object obj3, object obj4) + { + Property1 = obj1; + Property2 = obj2; + Property3 = obj3; + 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..2b2cca3 --- /dev/null +++ b/tests/TestTypeFoundation/DoubleParameterType.cs @@ -0,0 +1,8 @@ +namespace TestTypeFoundation; + +public class DoubleParameterType(T1 parameter1, T2 parameter2) +{ + public T1 Parameter1 { get; private set; } = parameter1; + + public T2 Parameter2 { get; private set; } = parameter2; +} \ 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..d601638 --- /dev/null +++ b/tests/TestTypeFoundation/EqualityResponder.cs @@ -0,0 +1,14 @@ +namespace TestTypeFoundation; + +public class EqualityResponder(bool equals) +{ + public override bool Equals(object obj) + { + return equals; + } + + public override int GetHashCode() + { + return 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..6a64727 --- /dev/null +++ b/tests/TestTypeFoundation/GenericType.cs @@ -0,0 +1,17 @@ +namespace TestTypeFoundation; + +public class GenericType + where T : class +{ + public GenericType(T t) + { + if (t == null) + { + throw new ArgumentNullException(nameof(t)); + } + + Value = t; + } + + 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..d2528c3 --- /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)); + } + + 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..410bac4 --- /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)); + } + + 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..ae535f7 --- /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)); + } + + 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..c7847d7 --- /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 _property; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _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..67a5d4d --- /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 _propertyIllBehavedSet; + } + + set + { + _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..bcd1452 --- /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 _items[index]; } + set { _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..4f54c29 --- /dev/null +++ b/tests/TestTypeFoundation/InternalGetterPropertyHolder.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class InternalGetterPropertyHolder(T property) +{ + public T Property { internal get; set; } = property; +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ItemContainer.cs b/tests/TestTypeFoundation/ItemContainer.cs new file mode 100644 index 0000000..d66cd48 --- /dev/null +++ b/tests/TestTypeFoundation/ItemContainer.cs @@ -0,0 +1,16 @@ +namespace TestTypeFoundation; + +public class ItemContainer(params T[] items) +{ + public ItemContainer(IEnumerable items) + : this(items.ToArray()) + { + } + + public ItemContainer(IList items) + : this(items.ToArray()) + { + } + + public IEnumerable Items { get; } = items; +} \ No newline at end of file diff --git a/tests/TestTypeFoundation/ItemHolder.cs b/tests/TestTypeFoundation/ItemHolder.cs new file mode 100644 index 0000000..0bb6260 --- /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) + { + Item1S = t1S; + 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) + { + 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..63ede74 --- /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)); + } + + Text = text; + 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)); + } + + Text = text; + 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..0fbc62a --- /dev/null +++ b/tests/TestTypeFoundation/MutableValueType.cs @@ -0,0 +1,10 @@ +namespace TestTypeFoundation; + +public struct MutableValueType(object property1, object property2, object property3) +{ + public object Property1 { get; set; } = property1; + + public object Property2 { get; set; } = property2; + + public object Property3 { get; set; } = property3; +} \ 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..1b3b870 --- /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) + { + 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..4df736e --- /dev/null +++ b/tests/TestTypeFoundation/QuadrupleParameterType.cs @@ -0,0 +1,12 @@ +namespace TestTypeFoundation; + +public class QuadrupleParameterType(T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4) +{ + public T1 Parameter1 { get; private set; } = parameter1; + + public T2 Parameter2 { get; private set; } = parameter2; + + public T3 Parameter3 { get; private set; } = parameter3; + + public T4 Parameter4 { get; private set; } = parameter4; +} \ 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..dedd9e7 --- /dev/null +++ b/tests/TestTypeFoundation/RecordType.cs @@ -0,0 +1,31 @@ +namespace TestTypeFoundation; + +public class RecordType(T value) : IEquatable> +{ + public T Value { get; } = value; + + public bool Equals(RecordType? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return EqualityComparer.Default.Equals(Value, other.Value); + } + + public override bool Equals(object? obj) + { + return Equals(obj as RecordType); + } + + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(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..ba34642 --- /dev/null +++ b/tests/TestTypeFoundation/SingleParameterType.cs @@ -0,0 +1,6 @@ +namespace TestTypeFoundation; + +public class SingleParameterType(T parameter) +{ + public T Parameter { get; private set; } = parameter; +} \ 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..a2b784e --- /dev/null +++ b/tests/TestTypeFoundation/TestTypeFoundation.csproj @@ -0,0 +1,16 @@ + + + + + + net462;netstandard2.0;net5.0;net6.0 + TestTypeFoundation + TestTypeFoundation + + + + + + + + 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..d86f763 --- /dev/null +++ b/tests/TestTypeFoundation/TripleParameterType.cs @@ -0,0 +1,10 @@ +namespace TestTypeFoundation; + +public class TripleParameterType(T1 parameter1, T2 parameter2, T3 parameter3) +{ + public T1 Parameter1 { get; private set; } = parameter1; + + public T2 Parameter2 { get; private set; } = parameter2; + + public T3 Parameter3 { get; private set; } = parameter3; +} \ 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..4d092b6 --- /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 _dict[index]; + } + set + { + _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..6e64938 --- /dev/null +++ b/tests/TestTypeFoundation/UnguardedConstructorHost.cs @@ -0,0 +1,21 @@ +using System.Reflection; + +namespace TestTypeFoundation; + +public class UnguardedConstructorHost(T item) +{ + public T Item { get; } = item; + + 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