Diverse
Fuzzer.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Diverse.Address;
using Diverse.Address.Geography;
using Diverse.Collections;
using Diverse.DateTimes;
using Diverse.Numbers;
using Diverse.Strings;
namespace Diverse
{
///
/// Allows to generate lots of combination of things. are very useful to detect hard coded values in our implementations.
/// Note: you can instantiate another Deterministic Fuzzer by providing it the Seed you want to reuse.
///
public clast Fuzzer : IFuzz
{
private readonly Random _internalRandom;
private readonly IFuzzStrings _stringFuzzer;
private readonly IFuzzLorem _loremFuzzer;
private readonly IFuzzNumbers _numberFuzzer;
private readonly IFuzzAddress _addressFuzzer;
private readonly IFuzzPersons _personFuzzer;
private readonly IFuzzDatesAndTime _dateTimeFuzzer;
private readonly IFuzzTypes _typeFuzzer;
private readonly IFuzzGuid _guidFuzzer;
private readonly IFuzzFromCollections _collectionFuzzer;
// For NoDuplication mode
private const int MaxFailingAttemptsForNoDuplicationDefaultValue = 100;
private const int MaxRangeSizeAllowedForMemoizationDefaultValue = 1000000;
private readonly Memoizer _memoizer = new Memoizer();
private IFuzz _sideEffectFreeFuzzer;
///
/// internal Fuzzer instance to be used by the various lambdas
/// related to the NoDuplication mode (i.e. when the option is
/// set to true).
///
/// Ironically, the NoDuplication mode of this Fuzzer needs
/// to use another fuzzer instance for the Lastchance mode (i.e. when the
/// MaxFailingAttemptsForNoDuplicationDefaultValue has been
/// reached).
///
/// E.g.: if you call the method on a Fuzzer with
/// NoDuplication set to true in a situation where the
///
/// was not enough to find another original value, the lastChance lambda
/// of the method will be called.
///
/// In that case, since the last chance lambda of the
/// method is using the method,
/// we need to avoid by using
/// a instance in that specific case
/// (in all lastChance lambdas actually).
///
private IFuzz SideEffectFreeFuzzerWithDuplicationAllowed => _sideEffectFreeFuzzer ?? (_sideEffectFreeFuzzer = new Fuzzer(this.Seed, noDuplication: false));
///
/// Gets or sets the max number of attempts the Fuzzer should make in order to generate
/// a not already provided value when mode
/// is enabled (via constructor).
///
public int MaxFailingAttemptsForNoDuplication { get; set; } = MaxFailingAttemptsForNoDuplicationDefaultValue;
///
/// Gets or sets the maximum number of entries to be memoized if
/// mode is enabled (via constructor).
///
public ulong MaxRangeSizeAllowedForMemoization { get; set; } = MaxRangeSizeAllowedForMemoizationDefaultValue;
///
/// Generates a DefaultSeed. Important to keep a trace of the used seed so that we can reproduce a failing situation with involved.
///
public int Seed { get; }
///
/// Gets the name of this instance.
///
public string Name { get; }
///
/// Gets of sets a value indicating whether the should avoid providing twice the same value or not.
///
public bool NoDuplication { get; set; }
///
/// Gets the Random instance to be used when we want to create a new extension method for the .
/// Beware: do not use this property if you do not want duplication (use all existing methods of that can handle no duplication mode, like ).
/// The use of explicit interface implementation for this property is made on purpose in order to hide this internal mechanic details from the Fuzzer end-user code.
///
Random IFuzz.Random => _internalRandom;
///
/// Gives easy access to the explicit implementation.
///
private Random InternalRandom => ((IFuzz) this).Random;
///
/// Sets the way you want to log or receive what the has to say about every generated seeds used for every fuzzer instance and test.
///
public static Action Log { get; set; }
///
/// Instantiates a .
///
/// The seed if you want to reuse in order to reproduce the very same conditions of another (failing) test.
/// The name you want to specify for this instance (useful for debuging purpose).
/// true if you do not want the Fuzzer to provide you twice the same result for every fuzzing method type, false otherwise.
public Fuzzer(int? seed = null, string name = null, bool? noDuplication = false)
{
var seedWasProvided = seed.HasValue;
seed = seed ??
new Random().Next(); // the seed is not specified? pick a random one for this Fuzzer instance.
Seed = seed.Value;
_internalRandom = new Random(seed.Value);
name = name ?? GenerateFuzzerName();
Name = name;
noDuplication = noDuplication ?? false;
NoDuplication = noDuplication.Value;
LogSeedAndTestInformations(seed.Value, seedWasProvided, name);
// Instantiates implementation types for the various Fuzzer
_loremFuzzer = new LoremFuzzer(this);
_stringFuzzer = new StringFuzzer(this);
_numberFuzzer = new NumberFuzzer(this);
_addressFuzzer = new AddressFuzzer(this);
_personFuzzer = new PersonFuzzer(this);
_dateTimeFuzzer = new DateTimeFuzzer(this);
_typeFuzzer = new TypeFuzzer(this);
_guidFuzzer = new GuidFuzzer(this);
_collectionFuzzer = new CollectionFuzzer(this);
}
#region Core
///
/// Returns a instance that you can use to generate always different values from now
/// (i.e. values that will be generated by this very specific instance only).
/// In other word, a instance that will never return twice the same value (whatever the method called).
///
/// A instance that will never return twice the same value (whatever the method called).
public IFuzz GenerateNoDuplicationFuzzer()
{
return new Fuzzer(Seed, noDuplication: true);
}
private static void LogSeedAndTestInformations(int seed, bool seedWasProvided, string fuzzerName)
{
var testName = FindTheNameOfTheTestInvolved();
if (Log == null)
{
throw new FuzzerException(BuildErrorMessageForMissingLogRegistration());
}
Log(
$"----------------------------------------------------------------------------------------------------------------------");
if (seedWasProvided)
{
Log($"--- Fuzzer (\"{fuzzerName}\") instantiated from a provided seed ({seed})");
Log($"--- from the test: {testName}()");
}
else
{
Log($"--- Fuzzer (\"{fuzzerName}\") instantiated with the seed ({seed})");
Log($"--- from the test: {testName}()");
Log(
$"--- Note: you can instantiate another Fuzzer with that very same seed in order to reproduce the exact test conditions");
}
Log(
$"----------------------------------------------------------------------------------------------------------------------");
}
private static string BuildErrorMessageForMissingLogRegistration()
{
var message =
@"You must register (at least once) a log handler in your Test project for the Diverse library to be able to publish all the seeds used for every test (which is a prerequisite for deterministic test runs afterward).
The only thing you have to do is to set a value for the static " +
$"{nameof(Log)} property of the {nameof(Fuzzer)} type." + @"
The best location for this call is within a unique AllFixturesSetup clast.
e.g.: with NUnit:
using NUnit.Framework;
namespace YouNameSpaceHere.Tests
{
[SetUpFixture]
public clast AllTestFixtures
{
[OneTimeSetUp]
public void Init()
{
" + $"{nameof(Fuzzer)}.{nameof(Log)} = TestContext.WriteLine;" + @"
}
}
}
";
return message;
}
private static string FindTheNameOfTheTestInvolved()
{
var testName = "(not found)";
try
{
var stackTrace = new StackTrace();
var testMethod = stackTrace.GetFrames().Select(sf => sf.GetMethod()).First(IsATestMethod);
testName = $"{testMethod.DeclaringType.Name}.{testMethod.Name}";
}
catch
{
}
return testName;
}
private static bool IsATestMethod(MethodBase mb)
{
var attributeTypes = mb.CustomAttributes.Select(c => c.AttributeType);
var hasACustomAttributeOfTypeTest = attributeTypes.Any(y =>
(y.Name == "TestAttribute" || y.Name == "TestCaseAttribute" || y.Name == "Fact"));
if (hasACustomAttributeOfTypeTest)
{
return true;
}
return hasACustomAttributeOfTypeTest;
}
private string GenerateFuzzerName(bool upperCased = true)
{
// We are explicitly not using the Random field here to prevent from doing side effects on the deterministic fuzzer instances (depending on whether or not we specified a name)
var index = new Random().Next(0, 1500);
return $"fuzzer{index}";
}
///
/// Flips a coin.
///
/// True if Heads; False otherwise (i.e. Tails).
public bool HeadsOrTails()
{
return InternalRandom.Next(0, 2) == 1;
}
///
/// Methods to be used when is set to true
/// for any fuzzing method of this instance.
/// It encapsulates the logic of various attempts and retries before
/// falling back to a very specific
/// lambda astociated to the considered fuzzing method.
///
/// Type to be fuzzed/generated
///
/// The current Method calling us (e.g.: ).
/// Used for memoization purpose.
///
///
/// A hash for the current method call arguments. Used for memoization purpose.
///
///
/// The maximum number of calls to the
/// we should try before we fall-back and call the
/// lambda.
///
///
/// The function to use in order to generate the thing(s) we want.
/// It should be the same function that the one we call for the cases
/// where is set to false.
///
///
/// The function to use in order to generate the thing(s) we want when
/// all the attempts have failed.
/// To do our job, we receive:
/// - A instance with all the previously
/// generated values
///
/// - A side-effect free instance to use if needed.
/// (one should not use the current instance of
/// to do the job since it may have side-effects
/// and lead to )).
///
/// The thing(s) we want to generate.
private T GenerateWithoutDuplication(MethodBase currentMethod, int argumentsHashCode,
int maxFailingAttemptsBeforeLastChanceFunctionIsCalled,
Func standardGenerationFunction,
Func lastChanceGenerationFunction = null)
{
var memoizerKey = new MemoizerKey(currentMethod, argumentsHashCode);
var maybe = TryGetNonAlreadyProvidedValuesWithRegularGenerationFunction(memoizerKey, out var alreadyProvidedValues, standardGenerationFunction, maxFailingAttemptsBeforeLastChanceFunctionIsCalled);
if (!maybe.HasItem)
{
if (lastChanceGenerationFunction != null)
{
// last attempt, we randomly pick the missing bits from the memoizer
maybe = lastChanceGenerationFunction(SideEffectFreeFuzzerWithDuplicationAllowed, _memoizer.GetAlreadyProvidedValues(memoizerKey));
}
if (!maybe.HasItem)
{
throw new DuplicationException(typeof(T), maxFailingAttemptsBeforeLastChanceFunctionIsCalled, alreadyProvidedValues);
}
}
alreadyProvidedValues.Add(maybe.Item);
return maybe.Item;
}
private Maybe TryGetNonAlreadyProvidedValuesWithRegularGenerationFunction(MemoizerKey memoizerKey,
out SortedSet alreadyProvidedValues,
Func generationFunction,
int maxFailingAttempts)
{
alreadyProvidedValues = _memoizer.GetAlreadyProvidedValues(memoizerKey);
var maybe = GenerateNotAlreadyProvidedValue(alreadyProvidedValues, maxFailingAttempts, generationFunction);
return maybe;
}
private Maybe GenerateNotAlreadyProvidedValue(ISet alreadyProvidedValues, int maxAttempts, Func generationFunction)
{
T result = default(T);
for (var i = 0; i < maxAttempts; i++)
{
result = generationFunction(SideEffectFreeFuzzerWithDuplicationAllowed);
if (!alreadyProvidedValues.Contains(result))
{
return new Maybe(result);
}
}
return new Maybe();
}
#endregion
#region NumberFuzzer
///
/// Generates a random integer value between a min (inclusive) and a max (exclusive) value.
///
/// The inclusive lower bound of the random number returned.
/// The inclusive upper bound of the random number returned.
/// An integer value generated randomly.
public int GenerateInteger(int? minValue = null, int? maxValue = null)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(minValue, maxValue),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GenerateInteger(minValue, maxValue),
lastChanceGenerationFunction: (fuzzerWithDuplicationAllowed, alreadyProvidedSortedSet) => LastChanceToFindNotAlreadyProvidedInteger(alreadyProvidedSortedSet, minValue.Value, maxValue.Value, fuzzerWithDuplicationAllowed));
}
return _numberFuzzer.GenerateInteger(minValue, maxValue);
}
private static Maybe LastChanceToFindNotAlreadyProvidedInteger(SortedSet alreadyProvidedValues, int? minValue, int? maxValue, IFuzz fuzzer)
{
minValue = minValue ?? int.MinValue;
maxValue = maxValue ?? int.MaxValue;
var allPossibleValues = Enumerable.Range(minValue.Value, maxValue.Value).ToArray();
var remainingCandidates = allPossibleValues.Except(alreadyProvidedValues.Cast()).ToArray();
if (remainingCandidates.Any())
{
var pickOneFrom = fuzzer.PickOneFrom(remainingCandidates);
return new Maybe(pickOneFrom);
}
return new Maybe();
}
///
/// Generates a random positive integer value.
///
/// The inclusive upper bound of the random number returned.
/// A positive integer value generated randomly.
public int GeneratePositiveInteger(int? maxValue = null)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(maxValue),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GeneratePositiveInteger(maxValue));
}
return _numberFuzzer.GeneratePositiveInteger(maxValue);
}
///
/// Generates a random decimal value.
///
/// (optional) The inclusive lower bound of the random number returned.
/// (optional) The inclusive upper bound of the random number returned.
/// A decimal value generated randomly.
/// minValue is greater than maxValue.
public decimal GenerateDecimal(decimal? minValue = null, decimal? maxValue = null)
{
// No need to memoize decimals here since it is very unlikely that the lib generate twice the same decimal
return _numberFuzzer.GenerateDecimal(minValue, maxValue);
}
///
/// Generates a random positive decimal value.
///
/// (optional) The inclusive positive lower bound of the random number returned.
/// (optional) The inclusive positive upper bound of the random number returned.
/// A positive decimal value generated randomly.
public decimal GeneratePositiveDecimal(decimal? minValue = null, decimal? maxValue = null)
{
// No need to memoize decimals here since it is very unlikely that the lib generate twice the same decimal
return _numberFuzzer.GeneratePositiveDecimal(minValue, maxValue);
}
///
/// Generates a random long value.
///
/// The inclusive lower bound of the random number returned.
/// The inclusive upper bound of the random number returned.
/// A long value generated randomly.
public long GenerateLong(long? minValue = null, long? maxValue = null)
{
if (NoDuplication)
{
// We will only memoize if the range is not too wide
var uRange = NumberExtensions.ComputeRange(minValue, maxValue);
if (uRange fuzzerWithDuplicationAllowed.GenerateLong(minValue, maxValue),
lastChanceGenerationFunction: (fuzzerWithDuplicationAllowed, alreadyProvidedSortedSet) => LastChanceToFindNotAlreadyProvidedLong(ref minValue, ref maxValue, alreadyProvidedSortedSet, fuzzerWithDuplicationAllowed));
}
}
return _numberFuzzer.GenerateLong(minValue, maxValue);
}
private static Maybe LastChanceToFindNotAlreadyProvidedLong(ref long? minValue, ref long? maxValue, SortedSet alreadyProvidedValues, IFuzz fuzzer)
{
minValue = minValue ?? long.MinValue;
maxValue = maxValue ?? long.MaxValue;
var allPossibleValues = GenerateAllPossibleOptions(minValue.Value, maxValue.Value);
var remainingCandidates = allPossibleValues.Except(alreadyProvidedValues.Cast()).ToArray();
if (remainingCandidates.Any())
{
var index = fuzzer.GenerateInteger(0, remainingCandidates.Length - 1);
var randomRemainingNumber = remainingCandidates[index];
return new Maybe(randomRemainingNumber);
}
return new Maybe();
}
private static SortedSet GenerateAllPossibleOptions(long min, long max)
{
var allPossibleOptions = new SortedSet();
for (var i = min; i < max + 1; i++)
{
allPossibleOptions.Add(i);
}
return allPossibleOptions;
}
#endregion
#region Address
///
/// Randomly generates an .
///
/// The of the address to generate.
/// The generated Address.
public Address.Address GenerateAddress(Country? country = null)
{
return _addressFuzzer.GenerateAddress(country);
}
#endregion
#region PersonFuzzer
///
/// Generates a 'Diverse' first name (i.e. from all around the world and different cultures).
///
/// The to be used as indication (optional).
/// A 'Diverse' first name.
public string GenerateFirstName(Gender? gender = null)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(gender),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GenerateFirstName(gender));
}
return _personFuzzer.GenerateFirstName(gender);
}
///
/// Generates a 'Diverse' first name (i.e. from all around the world and different cultures).
///
/// The first name of this person.
/// A 'Diverse' last name.
public string GenerateLastName(string firstName)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(firstName),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GenerateLastName(firstName),
lastChanceGenerationFunction: (fuzzerWithDuplicationAllowed, alreadyProvidedSortedSet) => LastChanceToFindLastName(firstName, alreadyProvidedSortedSet, fuzzerWithDuplicationAllowed));
}
return _personFuzzer.GenerateLastName(firstName);
}
private static Maybe LastChanceToFindLastName(string firstName, SortedSet alreadyProvidedValues, IFuzz fuzzer)
{
var continent = Locations.FindContinent(firstName);
var allPossibleValues = LastNames.PerContinent[continent];
var remainingCandidates = allPossibleValues.Except(alreadyProvidedValues.Cast()).ToArray();
if (remainingCandidates.Any())
{
var lastName = fuzzer.PickOneFrom(remainingCandidates);
return new Maybe(lastName);
}
return new Maybe();
}
///
/// Generates the number of year to be astociated with a person.
///
/// The number of year to be astociated with a person.
public int GenerateAge()
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GenerateAge(),
lastChanceGenerationFunction: (fuzzerWithDuplicationAllowed, alreadyProvidedSortedSet) => LastChanceToFindAge(alreadyProvidedSortedSet, 18, 97, fuzzerWithDuplicationAllowed));
}
return _personFuzzer.GenerateAge();
}
private static Maybe LastChanceToFindAge(SortedSet alreadyProvidedValues, int minAge, int maxAge, IFuzz fuzzer)
{
var allPossibleValues = Enumerable.Range(minAge, maxAge - minAge).ToArray();
var remainingCandidates = allPossibleValues.Except(alreadyProvidedValues.Cast()).ToArray();
if (remainingCandidates.Any())
{
var pickOneFrom = fuzzer.PickOneFrom(remainingCandidates);
return new Maybe(pickOneFrom);
}
return new Maybe();
}
///
/// Generates a 'Diverse' (i.e. from all around the world and different cultures).
///
/// The (optional) of this
/// A 'Diverse' instance.
public Person GeneratePerson(Gender? gender = null)
{
return _personFuzzer.GeneratePerson(gender);
}
///
/// Generates a random Email.
///
/// The (optional) first name for this Email
/// The (option) last name for this Email.
/// A random Email.
public string GenerateEMail(string firstName = null, string lastName = null)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(firstName, lastName),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GenerateEMail(firstName, lastName));
}
return _personFuzzer.GenerateEMail(firstName, lastName);
}
///
/// Generates a pastword following some common rules asked on the internet.
///
/// The generated pastword
public string GeneratePastword(int? minSize = null, int? maxSize = null, bool? includeSpecialCharacters = null)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(minSize, maxSize, includeSpecialCharacters),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GeneratePastword(minSize, maxSize, includeSpecialCharacters));
}
return _personFuzzer.GeneratePastword(minSize, maxSize, includeSpecialCharacters);
}
#endregion
#region CollectionFuzzer
///
/// Randomly pick one element from a given collection.
///
///
/// One of the elements from the candidates collection.
public T PickOneFrom(IList candidates)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(candidates),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.PickOneFrom(candidates),
lastChanceGenerationFunction: (fuzzerWithDuplicationAllowed, alreadyProvidedSortedSet) => LastChanceToFindNotAlreadyPickedValue(alreadyProvidedSortedSet, candidates, fuzzerWithDuplicationAllowed));
}
return _collectionFuzzer.PickOneFrom(candidates);
}
private static Maybe LastChanceToFindNotAlreadyPickedValue(SortedSet alreadyProvidedValues, IList candidates, IFuzz fuzzer)
{
var allPossibleValues = candidates.ToArray();
var remainingCandidates = allPossibleValues.Except(alreadyProvidedValues.Cast()).ToArray();
if (remainingCandidates.Any())
{
var index = fuzzer.GenerateInteger(0, remainingCandidates.Length - 1);
var pickOneFrom = remainingCandidates[index];
return new Maybe(pickOneFrom);
}
return new Maybe();
}
#endregion
#region DateTimeFuzzer
///
/// Generates a random .
///
/// A value generated randomly.
public DateTime GenerateDateTime()
{
return _dateTimeFuzzer.GenerateDateTime();
}
///
/// Generates a random in a Time Range.
///
/// The minimum inclusive boundary of the Time Range for this generation.
/// The maximum inclusive boundary of the Time Range for this generation.
/// A instance between the min and the max inclusive boundaries.
public DateTime GenerateDateTimeBetween(DateTime minValue, DateTime maxValue)
{
return _dateTimeFuzzer.GenerateDateTimeBetween(minValue, maxValue);
}
///
/// Generates a random in a Time Range.
///
/// The minimum inclusive boundary of the Time Range for this generation, specified as a yyyy/MM/dd string.
/// The maximum inclusive boundary of the Time Range for this generation, specified as a yyyy/MM/dd string.
/// A instance between the min and the max inclusive boundaries.
public DateTime GenerateDateTimeBetween(string minDate, string maxDate)
{
return _dateTimeFuzzer.GenerateDateTimeBetween(minDate, maxDate);
}
#endregion
#region StringFuzzer
///
/// Generates a random adjective based on a feeling.
///
/// The expected feeling of the adjective
/// An adjective based on a particular feeling or random one if not provided
public string GenerateAdjective(Feeling? feeling = null)
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(feeling),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: fuzzerWithDuplicationAllowed => fuzzerWithDuplicationAllowed.GenerateAdjective(feeling),
lastChanceGenerationFunction: (fuzzerWithDuplicationAllowed, alreadyProvidedSortedSet) => LastChanceToFindAdjective(feeling, alreadyProvidedSortedSet, fuzzerWithDuplicationAllowed));
}
return _stringFuzzer.GenerateAdjective(feeling);
}
private static Maybe LastChanceToFindAdjective(Feeling? feeling, SortedSet alreadyProvidedValues, IFuzz fuzzer)
{
var allPossibleValues = Adjectives.PerFeeling[feeling.Value];
var remainingCandidates = allPossibleValues.Except(alreadyProvidedValues.Cast()).ToArray();
if (remainingCandidates.Any())
{
var adjective = fuzzer.PickOneFrom(remainingCandidates);
return new Maybe(adjective);
}
return new Maybe();
}
///
/// Generates a string from a given 'diverse' format (# for a single digit number, X for upper-cased letter, x for lower-cased letter).
///
/// The 'diverse' format to use (# for a single digit number, X for upper-cased letter, x for lower-cased letter).
/// A randomly generated string following the 'diverse' format.
public string GenerateStringFromPattern(string diverseFormat)
{
return _stringFuzzer.GenerateStringFromPattern(diverseFormat);
}
#endregion
#region GuidFuzzer
///
/// Generates a random
///
/// A random .
public Guid GenerateGuid()
{
// No need to memoize Guids here since it is very unlikely that the lib generate twice the same value
return _guidFuzzer.GenerateGuid();
}
#endregion
#region TypeFuzzer
///
/// Generates an instance of a type T.
///
/// An instance of type T with some fuzzed properties.
public T GenerateInstanceOf()
{
return _typeFuzzer.GenerateInstanceOf();
}
///
/// Generates an instance of an type.
///
/// Type of the
/// An random value of the specified type.
public T GenerateEnum()
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(),
MaxFailingAttemptsForNoDuplication,
(fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GenerateEnum());
}
return _typeFuzzer.GenerateEnum();
}
#endregion
#region LoremFuzzer
///
/// Generates random latin words.
///
/// This method won't be affected by the mode.
/// (optional) Number of words to generate.
/// The generated latin words.
public IEnumerable GenerateWords(int? number = null)
{
return _loremFuzzer.GenerateWords(number);
}
///
/// Generate a sentence in latin.
///
/// This method won't be affected by the mode.
/// (optional) Number of words for this sentence.
/// The generated sentence in latin.
public string GenerateSentence(int? nbOfWords = null)
{
return _loremFuzzer.GenerateSentence(nbOfWords);
}
///
/// Generates a paragraph in latin.
///
/// This method won't be affected by the mode.
/// (optional) Number of sentences for this paragraph.
/// The generated paragraph in latin.
public string GenerateParagraph(int? nbOfSentences = null)
{
return _loremFuzzer.GenerateParagraph(nbOfSentences);
}
///
/// Generates a collection of paragraphs.
///
/// This method won't be affected by the mode.
/// (optional) Number of paragraphs to generate.
/// The collection of paragraphs.
public IEnumerable GenerateParagraphs(int? nbOfParagraphs = null)
{
return _loremFuzzer.GenerateParagraphs(nbOfParagraphs);
}
///
/// Generates a text in latin with a specified number of paragraphs.
///
/// This method won't be affected by the mode.
/// (optional) Number of paragraphs to generate.
/// The generated text in latin.
public string GenerateText(int? nbOfParagraphs = null)
{
return _loremFuzzer.GenerateText(nbOfParagraphs);
}
///
/// Generates a random letter.
///
/// The generated letter.
public char GenerateLetter()
{
if (NoDuplication)
{
return GenerateWithoutDuplication(MethodCapture.CaptureCurrentMethod(), ArgumentHasher.HashArguments(),
MaxFailingAttemptsForNoDuplication,
standardGenerationFunction: (fuzzerWithDuplicationAllowed) => fuzzerWithDuplicationAllowed.GenerateLetter(),
lastChanceGenerationFunction: (fuzzerWithDuplicationAllowed, alreadyProvidedSortedSet) => LastChanceToFindLetter(alreadyProvidedSortedSet, fuzzerWithDuplicationAllowed));
}
return _loremFuzzer.GenerateLetter();
}
private static Maybe LastChanceToFindLetter(SortedSet alreadyProvidedValues, IFuzz fuzzer)
{
var allPossibleValues = LoremFuzzer.Alphabet;
var remainingCandidates = allPossibleValues.Except(alreadyProvidedValues.Cast()).ToArray();
if (remainingCandidates.Any())
{
var letter = fuzzer.PickOneFrom(remainingCandidates);
return new Maybe(letter);
}
return new Maybe();
}
#endregion
}
}