From 6bcdcb697c608aa8faf66c5459ab5a2dfdfc048d Mon Sep 17 00:00:00 2001 From: Paul DeVito Date: Sat, 27 Jan 2024 19:20:50 -0500 Subject: [PATCH 1/3] feat: can handle date only and time only --- .gitignore | 4 ++++ src/Generators/GeneratorFactory.cs | 4 ++++ src/Generators/Types/DateOnlyGenerator.cs | 15 +++++++++++++++ src/Generators/Types/TimeOnlyGenerator.cs | 15 +++++++++++++++ src/Soenneker.Utils.AutoBogus.csproj | 2 +- .../Dtos/Complex/OrderItem.cs | 4 ++++ .../Dtos/GenerateAssertions.cs | 6 ++++++ 7 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/Generators/Types/DateOnlyGenerator.cs create mode 100644 src/Generators/Types/TimeOnlyGenerator.cs diff --git a/.gitignore b/.gitignore index 1ee5385..3413bc0 100644 --- a/.gitignore +++ b/.gitignore @@ -360,3 +360,7 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd + +# JetBrains Rider +.idea/ +*.sln.iml \ No newline at end of file diff --git a/src/Generators/GeneratorFactory.cs b/src/Generators/GeneratorFactory.cs index e37fb7a..cb00d85 100644 --- a/src/Generators/GeneratorFactory.cs +++ b/src/Generators/GeneratorFactory.cs @@ -20,6 +20,10 @@ internal static class GeneratorFactory {typeof(char), new CharGenerator()}, {typeof(DateTime), new DateTimeGenerator()}, {typeof(DateTimeOffset), new DateTimeOffsetGenerator()}, +#if NET6_0_OR_GREATER + {typeof(DateOnly), new DateOnlyGenerator()}, + {typeof(TimeOnly), new TimeOnlyGenerator()}, +#endif {typeof(decimal), new DecimalGenerator()}, {typeof(double), new DoubleGenerator()}, {typeof(float), new FloatGenerator()}, diff --git a/src/Generators/Types/DateOnlyGenerator.cs b/src/Generators/Types/DateOnlyGenerator.cs new file mode 100644 index 0000000..9494861 --- /dev/null +++ b/src/Generators/Types/DateOnlyGenerator.cs @@ -0,0 +1,15 @@ +#if NET6_0_OR_GREATER +using Soenneker.Utils.AutoBogus.Context; +using Soenneker.Utils.AutoBogus.Generators.Abstract; + +namespace Soenneker.Utils.AutoBogus.Generators.Types; + +internal sealed class DateOnlyGenerator + : IAutoFakerGenerator +{ + object IAutoFakerGenerator.Generate(AutoFakerContext context) + { + return context.Faker.Date.RecentDateOnly(); + } +} +#endif \ No newline at end of file diff --git a/src/Generators/Types/TimeOnlyGenerator.cs b/src/Generators/Types/TimeOnlyGenerator.cs new file mode 100644 index 0000000..5a0a8af --- /dev/null +++ b/src/Generators/Types/TimeOnlyGenerator.cs @@ -0,0 +1,15 @@ +#if NET6_0_OR_GREATER +using Soenneker.Utils.AutoBogus.Context; +using Soenneker.Utils.AutoBogus.Generators.Abstract; + +namespace Soenneker.Utils.AutoBogus.Generators.Types; + +internal sealed class TimeOnlyGenerator + : IAutoFakerGenerator +{ + object IAutoFakerGenerator.Generate(AutoFakerContext context) + { + return context.Faker.Date.RecentTimeOnly(); + } +} +#endif \ No newline at end of file diff --git a/src/Soenneker.Utils.AutoBogus.csproj b/src/Soenneker.Utils.AutoBogus.csproj index a24adc3..dfaf040 100644 --- a/src/Soenneker.Utils.AutoBogus.csproj +++ b/src/Soenneker.Utils.AutoBogus.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + netstandard2.1;net6.0;net7.0;net8.0 enable AnyCPU $(NoWarn);1591 diff --git a/test/Soenneker.Utils.AutoBogus.Tests/Dtos/Complex/OrderItem.cs b/test/Soenneker.Utils.AutoBogus.Tests/Dtos/Complex/OrderItem.cs index be09023..cf1f0cb 100644 --- a/test/Soenneker.Utils.AutoBogus.Tests/Dtos/Complex/OrderItem.cs +++ b/test/Soenneker.Utils.AutoBogus.Tests/Dtos/Complex/OrderItem.cs @@ -2,6 +2,8 @@ namespace Soenneker.Utils.AutoBogus.Tests.Dtos.Complex; +using System; + public sealed class OrderItem { public OrderItem(Product product) @@ -12,4 +14,6 @@ public OrderItem(Product product) public Product Product { get; } public Quantity Quantity { get; set; } public IDictionary Discounts { get; set; } + public TimeOnly MostEffectiveAt { get; set; } + public DateOnly MostEffectiveOn { get; set; } } \ No newline at end of file diff --git a/test/Soenneker.Utils.AutoBogus.Tests/Dtos/GenerateAssertions.cs b/test/Soenneker.Utils.AutoBogus.Tests/Dtos/GenerateAssertions.cs index a8e61b9..2d915ac 100644 --- a/test/Soenneker.Utils.AutoBogus.Tests/Dtos/GenerateAssertions.cs +++ b/test/Soenneker.Utils.AutoBogus.Tests/Dtos/GenerateAssertions.cs @@ -29,6 +29,8 @@ internal GenerateAssertions(object subject) : base(subject) Assertions.Add(IsChar, AssertChar); Assertions.Add(IsDateTime, AssertDateTime); Assertions.Add(IsDateTimeOffset, AssertDateTimeOffset); + Assertions.Add(IsDateOnly, AssertDateOnly); + Assertions.Add(IsTimeOnly, AssertTimeOnly); Assertions.Add(IsDecimal, AssertDecimal); Assertions.Add(IsDouble, AssertDouble); Assertions.Add(IsFloat, AssertFloat); @@ -165,6 +167,8 @@ private void AssertDefaultValue(MemberInfo memberInfo) private static bool IsChar(Type type) => type == typeof(char); private static bool IsDateTime(Type type) => type == typeof(DateTime); private static bool IsDateTimeOffset(Type type) => type == typeof(DateTimeOffset); + private static bool IsDateOnly(Type type) => type == typeof(DateOnly); + private static bool IsTimeOnly(Type type) => type == typeof(TimeOnly); private static bool IsDecimal(Type type) => type == typeof(decimal); private static bool IsDouble(Type type) => type == typeof(double); private static bool IsFloat(Type type) => type == typeof(float); @@ -194,6 +198,8 @@ private void AssertDefaultValue(MemberInfo memberInfo) private static string AssertChar(string path, Type type, object value) => value != null && char.TryParse(value.ToString(), out char result) && result != default(char) ? null : GetAssertionMessage(path, type, value); private static string AssertDateTime(string path, Type type, object value) => value != null && DateTime.TryParse(value.ToString(), out DateTime result) && result != default ? null : GetAssertionMessage(path, type, value); private static string AssertDateTimeOffset(string path, Type type, object value) => value != null && DateTimeOffset.TryParse(value.ToString(), out DateTimeOffset result) && result != default ? null : GetAssertionMessage(path, type, value); + private static string AssertDateOnly(string path, Type type, object value) => value != null && DateOnly.TryParse(value.ToString(), out DateOnly result) && result != default ? null : GetAssertionMessage(path, type, value); + private static string AssertTimeOnly(string path, Type type, object value) => value != null && TimeOnly.TryParse(value.ToString(), out TimeOnly result) && result != default ? null : GetAssertionMessage(path, type, value); private static string AssertDecimal(string path, Type type, object value) => value != null && decimal.TryParse(value.ToString(), out decimal result) && result != default ? null : GetAssertionMessage(path, type, value); private static string AssertDouble(string path, Type type, object value) => value != null && double.TryParse(value.ToString(), out double result) && result != default ? null : GetAssertionMessage(path, type, value); private static string AssertFloat(string path, Type type, object value) => value != null && float.TryParse(value.ToString(), out float result) && result != default ? null : GetAssertionMessage(path, type, value); From eee668ba35ec5efab9caa1cf9cee7a0527e59f1a Mon Sep 17 00:00:00 2001 From: Jake Soenneker Date: Sun, 28 Jan 2024 09:26:13 -0600 Subject: [PATCH 2/3] Removes netstandard --- src/Generators/GeneratorFactory.cs | 58 +++++++++++------------ src/Generators/Types/DateOnlyGenerator.cs | 6 +-- src/Generators/Types/TimeOnlyGenerator.cs | 6 +-- src/Soenneker.Utils.AutoBogus.csproj | 6 +-- 4 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/Generators/GeneratorFactory.cs b/src/Generators/GeneratorFactory.cs index cb00d85..8129c15 100644 --- a/src/Generators/GeneratorFactory.cs +++ b/src/Generators/GeneratorFactory.cs @@ -20,10 +20,8 @@ internal static class GeneratorFactory {typeof(char), new CharGenerator()}, {typeof(DateTime), new DateTimeGenerator()}, {typeof(DateTimeOffset), new DateTimeOffsetGenerator()}, -#if NET6_0_OR_GREATER - {typeof(DateOnly), new DateOnlyGenerator()}, - {typeof(TimeOnly), new TimeOnlyGenerator()}, -#endif + {typeof(DateOnly), new DateOnlyGenerator()}, + {typeof(TimeOnly), new TimeOnlyGenerator()}, {typeof(decimal), new DecimalGenerator()}, {typeof(double), new DoubleGenerator()}, {typeof(float), new FloatGenerator()}, @@ -118,43 +116,43 @@ internal static IAutoFakerGenerator ResolveGenerator(AutoFakerContext context) switch (genericCollectionType!.Name) { case nameof(GenericCollectionType.ReadOnlyDictionary): - { - Type keyType = generics[0]; - Type valueType = generics[1]; + { + Type keyType = generics[0]; + Type valueType = generics[1]; - return CreateGenericGenerator(typeof(ReadOnlyDictionaryGenerator<,>), keyType, valueType); - } + return CreateGenericGenerator(typeof(ReadOnlyDictionaryGenerator<,>), keyType, valueType); + } case nameof(GenericCollectionType.ImmutableDictionary): case nameof(GenericCollectionType.Dictionary): case nameof(GenericCollectionType.SortedList): - { - return CreateDictionaryGenerator(generics); - } + { + return CreateDictionaryGenerator(generics); + } case nameof(GenericCollectionType.ReadOnlyList): case nameof(GenericCollectionType.ListType): case nameof(GenericCollectionType.ReadOnlyCollection): case nameof(GenericCollectionType.Collection): - { - Type elementType = generics[0]; - return CreateGenericGenerator(typeof(ListGenerator<>), elementType); - } + { + Type elementType = generics[0]; + return CreateGenericGenerator(typeof(ListGenerator<>), elementType); + } case nameof(GenericCollectionType.Set): - { - Type elementType = generics[0]; - return CreateGenericGenerator(typeof(SetGenerator<>), elementType); - } + { + Type elementType = generics[0]; + return CreateGenericGenerator(typeof(SetGenerator<>), elementType); + } case nameof(GenericCollectionType.Enumerable): + { + if (collectionType.Type == type) { - if (collectionType.Type == type) - { - // Not a full list type, we can't fake it if it's anything other than - // the actual IEnumerable interface itelf. - Type elementType = generics[0]; - return CreateGenericGenerator(typeof(EnumerableGenerator<>), elementType); - } - - break; + // Not a full list type, we can't fake it if it's anything other than + // the actual IEnumerable interface itelf. + Type elementType = generics[0]; + return CreateGenericGenerator(typeof(EnumerableGenerator<>), elementType); } + + break; + } } } @@ -178,6 +176,6 @@ private static IAutoFakerGenerator CreateDictionaryGenerator(Type[] generics) private static IAutoFakerGenerator CreateGenericGenerator(Type generatorType, params Type[] genericTypes) { Type type = generatorType.MakeGenericType(genericTypes); - return (IAutoFakerGenerator)Activator.CreateInstance(type); + return (IAutoFakerGenerator) Activator.CreateInstance(type); } } \ No newline at end of file diff --git a/src/Generators/Types/DateOnlyGenerator.cs b/src/Generators/Types/DateOnlyGenerator.cs index 9494861..27ae733 100644 --- a/src/Generators/Types/DateOnlyGenerator.cs +++ b/src/Generators/Types/DateOnlyGenerator.cs @@ -1,5 +1,4 @@ -#if NET6_0_OR_GREATER -using Soenneker.Utils.AutoBogus.Context; +using Soenneker.Utils.AutoBogus.Context; using Soenneker.Utils.AutoBogus.Generators.Abstract; namespace Soenneker.Utils.AutoBogus.Generators.Types; @@ -11,5 +10,4 @@ object IAutoFakerGenerator.Generate(AutoFakerContext context) { return context.Faker.Date.RecentDateOnly(); } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/Generators/Types/TimeOnlyGenerator.cs b/src/Generators/Types/TimeOnlyGenerator.cs index 5a0a8af..b76aa41 100644 --- a/src/Generators/Types/TimeOnlyGenerator.cs +++ b/src/Generators/Types/TimeOnlyGenerator.cs @@ -1,5 +1,4 @@ -#if NET6_0_OR_GREATER -using Soenneker.Utils.AutoBogus.Context; +using Soenneker.Utils.AutoBogus.Context; using Soenneker.Utils.AutoBogus.Generators.Abstract; namespace Soenneker.Utils.AutoBogus.Generators.Types; @@ -11,5 +10,4 @@ object IAutoFakerGenerator.Generate(AutoFakerContext context) { return context.Faker.Date.RecentTimeOnly(); } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/Soenneker.Utils.AutoBogus.csproj b/src/Soenneker.Utils.AutoBogus.csproj index dfaf040..c02a63e 100644 --- a/src/Soenneker.Utils.AutoBogus.csproj +++ b/src/Soenneker.Utils.AutoBogus.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net6.0;net7.0;net8.0 + net6.0;net7.0;net8.0 enable AnyCPU $(NoWarn);1591 @@ -32,10 +32,6 @@ icon.png - - - - From 5cbd83282b4ccfa9f5c5017738525614d66ea771 Mon Sep 17 00:00:00 2001 From: Jake Soenneker Date: Sun, 28 Jan 2024 09:29:42 -0600 Subject: [PATCH 3/3] misc deletions --- src/AutoFaker[T].cs | 317 ------------------ src/Soenneker - Backup.Utils.AutoBogus.csproj | 50 --- 2 files changed, 367 deletions(-) delete mode 100644 src/AutoFaker[T].cs delete mode 100644 src/Soenneker - Backup.Utils.AutoBogus.csproj diff --git a/src/AutoFaker[T].cs b/src/AutoFaker[T].cs deleted file mode 100644 index 84dd1f5..0000000 --- a/src/AutoFaker[T].cs +++ /dev/null @@ -1,317 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Bogus; -using Soenneker.Utils.AutoBogus.Abstract; -using Soenneker.Utils.AutoBogus.Extensions; -using Soenneker.Utils.AutoBogus.Services; - -namespace Soenneker.Utils.AutoBogus; - -/// -/// A class used to invoke generation requests of type . -/// -/// The type of instance to generate. -public class AutoFaker -{ - private AutoConfig _config; - - /// - /// Instantiates an instance of the class. - /// - public AutoFaker() - : this(null, null) - { - } - - /// - /// Instantiates an instance of the class. - /// - /// The locale to use for value generation. - public AutoFaker(string locale) - : this(locale, null) - { - } - - /// - /// Instantiates an instance of the class. - /// - /// The instance to use for the generation request. - public AutoFaker(IAutoBinder binder) - : this(null, binder) - { - } - - /// - /// Instantiates an instance of the class. - /// - /// The locale to use for value generation. - /// The instance to use for the generation request. - public AutoFaker(string locale = null, IAutoBinder binder = null) - : base(locale ?? AutoConfig.DefaultLocale, binder) - { - Binder = binder; - - // Ensure the default create action is cleared - // This is so we can check whether it has been set externally - DefaultCreateAction = CreateActions[currentRuleSet]; - CreateActions[currentRuleSet] = null; - } - - /// - /// The instance to use for the generation request. - /// - public IAutoBinder Binder { get; set; } - - internal AutoConfig Config - { - set - { - _config = value; - - Locale = _config.Locale; - Binder = _config.Binder; - - // Also pass the binder set up to the underlying Faker - binder = _config.Binder; - - if (FakerService.IsValueCreated) - FakerHub = FakerService.Faker; - - // Apply a configured faker if set - //if (_config.FakerHub != null) - //{ - // FakerHub = _config.FakerHub; - //} - } - } - - private bool CreateInitialized { get; set; } - private bool FinishInitialized { get; set; } - private Func DefaultCreateAction { get; set; } - - /// - /// Configures the current faker instance. - /// - /// A handler to build the faker configuration. - /// The current faker instance. - public AutoFaker Configure(Action configure) - { - var config = new AutoConfig(AutoFaker.DefaultConfig); - - var builder = new AutoConfigBuilder(config); - - configure.Invoke(builder); - - Config = config; - - return this; - } - - /// - /// Generates an instance of type . - /// - /// An optional list of delimited rule sets to use for the generate request. - /// The generated instance of type . - public override TType Generate(string ruleSets = null) - { - AutoGenerateContext context = CreateContext(ruleSets); - - PrepareCreate(context); - PrepareFinish(context); - - return base.Generate(ruleSets); - } - - /// - /// Generates a collection of instances of type . - /// - /// The number of instances to generate. - /// An optional list of delimited rule sets to use for the generate request. - /// The collection of generated instances of type . - public override List Generate(int count, string ruleSets = null) - { - AutoGenerateContext context = CreateContext(ruleSets); - - PrepareCreate(context); - PrepareFinish(context); - - return base.Generate(count, ruleSets); - } - - /// - /// Populates the provided instance with auto generated values. - /// - /// The instance to populate. - /// An optional list of delimited rule sets to use for the populate request. - public override void Populate(TType instance, string ruleSets = null) - { - AutoGenerateContext context = CreateContext(ruleSets); - PrepareFinish(context); - - base.Populate(instance, ruleSets); - } - - private AutoGenerateContext CreateContext(string ruleSets) - { - var config = new AutoConfig(_config ?? AutoFaker.DefaultConfig); - - if (!string.IsNullOrWhiteSpace(Locale)) - { - config.Locale = Locale; - } - - if (Binder != null) - { - config.Binder = Binder; - } - - return new AutoGenerateContext(config) - { - RuleSets = ParseRuleSets(ruleSets) - }; - } - - /// - /// Parse and clean the rule set list - /// If the rule set list is empty it defaults to a list containing only 'default' - /// By this point the currentRuleSet should be 'default' - /// - /// - /// - private List ParseRuleSets(string? ruleSets) - { - if (string.IsNullOrWhiteSpace(ruleSets)) - { - ruleSets = null; - } - - List validRuleSets = new List(); - - string[] ruleSetArray = ruleSets?.Split(',') ?? new[] {currentRuleSet}; - - for (int i = 0; i < ruleSetArray.Length; i++) - { - string trimmedRuleSet = ruleSetArray[i].Trim(); - - if (!string.IsNullOrWhiteSpace(trimmedRuleSet)) - { - validRuleSets.Add(trimmedRuleSet); - } - } - - return validRuleSets; - } - - private void PrepareCreate(AutoGenerateContext context) - { - // Check a create handler hasn't previously been set or configured externally - if (!CreateInitialized && CreateActions[currentRuleSet] == null) - { - CreateActions[currentRuleSet] = faker => - { - // Only auto create if the 'default' rule set is defined for generation - // This is because any specific rule sets are expected to handle the full creation - if (context.RuleSets.Contains(currentRuleSet)) - { - Type type = typeof(TType); - - // Set the current type being generated - context.ParentType = null; - context.GenerateType = type; - context.GenerateName = null; - - // Get the members that should not be set during construction - List memberNames = GetRuleSetsMemberNames(context); - - foreach (string? memberName in TypeProperties.Keys) - { - if (memberNames.Contains(memberName)) - { - var path = $"{type.FullName}.{memberName}"; - bool existing = context.Config.SkipPaths.Any(s => s == path); - - if (!existing) - { - context.Config.SkipPaths.Add(path); - } - } - } - - // Create a blank instance. It will be populated in the FinalizeAction registered - // by PrepareFinish (context.Binder.PopulateInstance). - return context.Binder.CreateInstance(context); - } - - return DefaultCreateAction.Invoke(faker); - }; - - CreateInitialized = true; - } - } - - private void PrepareFinish(AutoGenerateContext context) - { - if (FinishInitialized) - return; - - FinalizeActions.TryGetValue(currentRuleSet, out FinalizeAction finishWith); - - FinishWith((faker, instance) => - { - if (instance is not null && instance.GetType().IsExpandoObject()) - { - context.ParentType = null; - context.GenerateType = instance.GetType(); - context.GenerateName = null; - context.Instance = instance; - - IAutoGenerator generator = AutoGeneratorFactory.GetGenerator(context); - generator.Generate(context); - - context.Instance = null; - return; - } - - List memberNames = GetRuleSetsMemberNames(context); - - var members = new MemberInfo[TypeProperties.Count - memberNames.Count]; - var index = 0; - - foreach (KeyValuePair member in TypeProperties) - { - if (!memberNames.Contains(member.Key)) - { - members[index++] = member.Value; - } - } - - context.Binder.PopulateInstance(instance, context, members.Where(m => m != null).ToArray()); - - finishWith?.Action(faker, instance); - }); - - FinishInitialized = true; - } - - private List GetRuleSetsMemberNames(AutoGenerateContext context) - { - var members = new List(); - - for (int i = 0; i < context.RuleSets.Count; i++) - { - string? ruleSetName = context.RuleSets[i]; - - if (Actions.TryGetValue(ruleSetName, out Dictionary>? ruleSet)) - { - foreach (KeyValuePair> keyValuePair in ruleSet) - { - members.Add(keyValuePair.Key); - } - } - } - - return members; - } -} \ No newline at end of file diff --git a/src/Soenneker - Backup.Utils.AutoBogus.csproj b/src/Soenneker - Backup.Utils.AutoBogus.csproj deleted file mode 100644 index 18957b7..0000000 --- a/src/Soenneker - Backup.Utils.AutoBogus.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - netstandard2.1 - enable - AnyCPU - $(NoWarn);1591 - - - - An updated version of AutoBogus, which is an auto creator/populator for Bogus - 2.1.0 - $(BUILD_VERSION) - Soenneker.Utils.AutoBogus - Soenneker.Utils.AutoBogus - utils autobogus autofaker faker bogus tests mock c# .net csharp dotnet - Copyright © 2023 Jake Soenneker - Jake Soenneker - https://soenneker.com - true - true - true - snupkg - true - MIT - https://github.com/soenneker/soenneker.utils.autobogus - https://github.com/soenneker/soenneker.utils.autobogus - false - true - latest - README.md - icon.png - - - - - - - - - - - - - - - - - -