RandomAttribute
Supplies a set of random values to a single parameter of a parameterized test.
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
namespace NUnit.Framework
{
[NullableContext(1)]
[Nullable(0)]
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class RandomAttribute : NUnitAttribute, IParameterDataSource
{
[Nullable(0)]
private abstract class RandomDataSource : IParameterDataSource
{
public Type DataType { get; set; }
public bool Distinct { get; set; }
protected RandomDataSource(Type dataType)
{
DataType = dataType;
}
public abstract IEnumerable GetData(IParameterInfo parameter);
}
[Nullable(0)]
private abstract class RandomDataSource<[Nullable(2)] T> : RandomDataSource
{
[AllowNull]
[MaybeNull]
private readonly T _min;
[AllowNull]
[MaybeNull]
private readonly T _max;
private readonly int _count;
private readonly bool _inRange;
private readonly List<T> _previousValues = new List<T>();
protected RandomDataSource(int count)
: base(typeof(T))
{
_min = default(T);
_max = default(T);
_count = count;
_inRange = false;
}
protected RandomDataSource(T min, T max, int count)
: base(typeof(T))
{
_min = min;
_max = max;
_count = count;
_inRange = true;
}
public override IEnumerable GetData(IParameterInfo parameter)
{
Randomizer randomizer = Randomizer.GetRandomizer(parameter.ParameterInfo);
bool condition = this.CanUseRange() || !this._inRange;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(49, 1);
defaultInterpolatedStringHandler.AppendLiteral("The value type ");
defaultInterpolatedStringHandler.AppendFormatted<Type>(parameter.ParameterType);
defaultInterpolatedStringHandler.AppendLiteral(" does not support range of values.");
Guard.OperationValid(condition, defaultInterpolatedStringHandler.ToStringAndClear());
bool condition2 = !base.Distinct || !this._inRange || this.CanBeDistinct(this._min, this._max, this._count);
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(92, 3);
defaultInterpolatedStringHandler.AppendLiteral("The range of values is [");
defaultInterpolatedStringHandler.AppendFormatted<T>(this._min);
defaultInterpolatedStringHandler.AppendLiteral(", ");
defaultInterpolatedStringHandler.AppendFormatted<T>(this._max);
defaultInterpolatedStringHandler.AppendLiteral("[ and the random value count is ");
defaultInterpolatedStringHandler.AppendFormatted<int>(this._count);
defaultInterpolatedStringHandler.AppendLiteral(" so the values cannot be distinct.");
Guard.OperationValid(condition2, defaultInterpolatedStringHandler.ToStringAndClear());
for (int i = 0; i < this._count; i++) {
if (base.Distinct) {
T val;
do {
val = (this._inRange ? this.GetNext(randomizer, this._min, this._max) : this.GetNext(randomizer));
} while (this._previousValues.Contains(val));
this._previousValues.Add(val);
yield return (object)val;
} else
yield return (object)(this._inRange ? this.GetNext(randomizer, this._min, this._max) : this.GetNext(randomizer));
}
}
protected virtual bool CanUseRange()
{
return true;
}
protected abstract T GetNext(Randomizer randomizer);
protected abstract T GetNext(Randomizer randomizer, T min, T max);
protected abstract bool CanBeDistinct(T min, T max, int count);
}
[Nullable(0)]
private class RandomDataConverter : RandomDataSource
{
private readonly IParameterDataSource _source;
public RandomDataConverter(RandomDataSource source)
: base(source.DataType)
{
_source = source;
}
public override IEnumerable GetData(IParameterInfo parameter)
{
Type parmType = parameter.ParameterType;
foreach (object datum in _source.GetData(parameter)) {
if (datum is int) {
int num = (int)datum;
if (parmType == typeof(short))
yield return (object)(short)num;
else if (parmType == typeof(ushort)) {
yield return (object)(ushort)num;
} else if (parmType == typeof(byte)) {
yield return (object)(byte)num;
} else if (parmType == typeof(sbyte)) {
yield return (object)(sbyte)num;
} else if (parmType == typeof(decimal)) {
yield return (object)(decimal)num;
}
} else if (datum is double) {
double value = (double)datum;
if (parmType == typeof(decimal))
yield return (object)(decimal)value;
}
}
}
}
[Nullable(0)]
private class IntDataSource : RandomDataSource<int>
{
public IntDataSource(int count)
: base(count)
{
}
public IntDataSource(int min, int max, int count)
: base(min, max, count)
{
}
protected override int GetNext(Randomizer randomizer)
{
return randomizer.Next();
}
protected override int GetNext(Randomizer randomizer, int min, int max)
{
return randomizer.Next(min, max);
}
protected override bool CanBeDistinct(int min, int max, int count)
{
if (min + count > min)
return min + count <= max;
return false;
}
}
[Nullable(0)]
private class UIntDataSource : RandomDataSource<uint>
{
public UIntDataSource(int count)
: base(count)
{
}
public UIntDataSource(uint min, uint max, int count)
: base(min, max, count)
{
}
protected override uint GetNext(Randomizer randomizer)
{
return randomizer.NextUInt();
}
protected override uint GetNext(Randomizer randomizer, uint min, uint max)
{
return randomizer.NextUInt(min, max);
}
protected override bool CanBeDistinct(uint min, uint max, int count)
{
if (min + count > min)
return min + count <= max;
return false;
}
}
[Nullable(0)]
private class LongDataSource : RandomDataSource<long>
{
public LongDataSource(int count)
: base(count)
{
}
public LongDataSource(long min, long max, int count)
: base(min, max, count)
{
}
protected override long GetNext(Randomizer randomizer)
{
return randomizer.NextLong();
}
protected override long GetNext(Randomizer randomizer, long min, long max)
{
return randomizer.NextLong(min, max);
}
protected override bool CanBeDistinct(long min, long max, int count)
{
if (min + count > min)
return min + count <= max;
return false;
}
}
[Nullable(0)]
private class ULongDataSource : RandomDataSource<ulong>
{
public ULongDataSource(int count)
: base(count)
{
}
public ULongDataSource(ulong min, ulong max, int count)
: base(min, max, count)
{
}
protected override ulong GetNext(Randomizer randomizer)
{
return randomizer.NextULong();
}
protected override ulong GetNext(Randomizer randomizer, ulong min, ulong max)
{
return randomizer.NextULong(min, max);
}
protected override bool CanBeDistinct(ulong min, ulong max, int count)
{
if (count >= 0 && (ulong)((long)min + (long)count) > min)
return (ulong)((long)min + (long)count) <= max;
return false;
}
}
[Nullable(0)]
private class ShortDataSource : RandomDataSource<short>
{
public ShortDataSource(int count)
: base(count)
{
}
public ShortDataSource(short min, short max, int count)
: base(min, max, count)
{
}
protected override short GetNext(Randomizer randomizer)
{
return randomizer.NextShort();
}
protected override short GetNext(Randomizer randomizer, short min, short max)
{
return randomizer.NextShort(min, max);
}
protected override bool CanBeDistinct(short min, short max, int count)
{
if (min + count > min)
return min + count <= max;
return false;
}
}
[Nullable(0)]
private class UShortDataSource : RandomDataSource<ushort>
{
public UShortDataSource(int count)
: base(count)
{
}
public UShortDataSource(ushort min, ushort max, int count)
: base(min, max, count)
{
}
protected override ushort GetNext(Randomizer randomizer)
{
return randomizer.NextUShort();
}
protected override ushort GetNext(Randomizer randomizer, ushort min, ushort max)
{
return randomizer.NextUShort(min, max);
}
protected override bool CanBeDistinct(ushort min, ushort max, int count)
{
if (min + count > min)
return min + count <= max;
return false;
}
}
[Nullable(0)]
private class DoubleDataSource : RandomDataSource<double>
{
public DoubleDataSource(int count)
: base(count)
{
}
public DoubleDataSource(double min, double max, int count)
: base(min, max, count)
{
}
protected override double GetNext(Randomizer randomizer)
{
return randomizer.NextDouble();
}
protected override double GetNext(Randomizer randomizer, double min, double max)
{
return randomizer.NextDouble(min, max);
}
protected override bool CanBeDistinct(double min, double max, int count)
{
return true;
}
}
[Nullable(0)]
private class FloatDataSource : RandomDataSource<float>
{
public FloatDataSource(int count)
: base(count)
{
}
public FloatDataSource(float min, float max, int count)
: base(min, max, count)
{
}
protected override float GetNext(Randomizer randomizer)
{
return randomizer.NextFloat();
}
protected override float GetNext(Randomizer randomizer, float min, float max)
{
return randomizer.NextFloat(min, max);
}
protected override bool CanBeDistinct(float min, float max, int count)
{
return true;
}
}
[Nullable(0)]
private class ByteDataSource : RandomDataSource<byte>
{
public ByteDataSource(int count)
: base(count)
{
}
public ByteDataSource(byte min, byte max, int count)
: base(min, max, count)
{
}
protected override byte GetNext(Randomizer randomizer)
{
return randomizer.NextByte();
}
protected override byte GetNext(Randomizer randomizer, byte min, byte max)
{
return randomizer.NextByte(min, max);
}
protected override bool CanBeDistinct(byte min, byte max, int count)
{
if (min + count > min)
return min + count <= max;
return false;
}
}
[Nullable(0)]
private class SByteDataSource : RandomDataSource<sbyte>
{
public SByteDataSource(int count)
: base(count)
{
}
public SByteDataSource(sbyte min, sbyte max, int count)
: base(min, max, count)
{
}
protected override sbyte GetNext(Randomizer randomizer)
{
return randomizer.NextSByte();
}
protected override sbyte GetNext(Randomizer randomizer, sbyte min, sbyte max)
{
return randomizer.NextSByte(min, max);
}
protected override bool CanBeDistinct(sbyte min, sbyte max, int count)
{
if (min + count > min)
return min + count <= max;
return false;
}
}
[Nullable(0)]
private class EnumDataSource : RandomDataSource
{
private readonly int _count;
private readonly List<object> _previousValues = new List<object>();
public EnumDataSource(int count)
: base(typeof(Enum))
{
_count = count;
}
public override IEnumerable GetData(IParameterInfo parameter)
{
Guard.ArgumentValid(parameter.ParameterType.IsEnum, "EnumDataSource requires an enum parameter", "parameter");
Randomizer randomizer = Randomizer.GetRandomizer(parameter.ParameterInfo);
base.DataType = parameter.ParameterType;
int num = Enum.GetValues(base.DataType).Cast<int>().Distinct()
.Count();
bool condition = !base.Distinct || _count <= num;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(88, 3);
defaultInterpolatedStringHandler.AppendLiteral("The enum \"");
defaultInterpolatedStringHandler.AppendFormatted(base.DataType.Name);
defaultInterpolatedStringHandler.AppendLiteral("\" has ");
defaultInterpolatedStringHandler.AppendFormatted(num);
defaultInterpolatedStringHandler.AppendLiteral(" values and the random value count is ");
defaultInterpolatedStringHandler.AppendFormatted(_count);
defaultInterpolatedStringHandler.AppendLiteral(" so the values cannot be distinct.");
Guard.OperationValid(condition, defaultInterpolatedStringHandler.ToStringAndClear());
for (int i = 0; i < _count; i++) {
if (base.Distinct) {
object obj;
do {
obj = randomizer.NextEnum(parameter.ParameterType);
} while (_previousValues.Contains(obj));
_previousValues.Add(obj);
yield return obj;
} else
yield return randomizer.NextEnum(parameter.ParameterType);
}
}
}
[Nullable(0)]
private class DecimalDataSource : RandomDataSource<decimal>
{
public DecimalDataSource(int count)
: base(count)
{
}
protected override decimal GetNext(Randomizer randomizer)
{
return randomizer.NextDecimal();
}
protected override decimal GetNext(Randomizer randomizer, decimal min, decimal max)
{
return randomizer.NextDecimal(min, max);
}
protected override bool CanBeDistinct(decimal min, decimal max, int count)
{
return true;
}
}
[Nullable(0)]
private class GuidDataSource : RandomDataSource<Guid>
{
public GuidDataSource(int count)
: base(count)
{
}
protected override Guid GetNext(Randomizer randomizer)
{
return randomizer.NextGuid();
}
protected override Guid GetNext(Randomizer randomizer, Guid min, Guid max)
{
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(54, 1);
defaultInterpolatedStringHandler.AppendFormatted(typeof(Guid));
defaultInterpolatedStringHandler.AppendLiteral(" does not support range of parameters being specified.");
throw new NotSupportedException(defaultInterpolatedStringHandler.ToStringAndClear());
}
protected override bool CanBeDistinct(Guid min, Guid max, int count)
{
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(54, 1);
defaultInterpolatedStringHandler.AppendFormatted(typeof(Guid));
defaultInterpolatedStringHandler.AppendLiteral(" does not support range of parameters being specified.");
throw new NotSupportedException(defaultInterpolatedStringHandler.ToStringAndClear());
}
protected override bool CanUseRange()
{
return false;
}
}
[Nullable(2)]
private RandomDataSource _source;
private readonly int _count;
public bool Distinct { get; set; }
public RandomAttribute(int count)
{
_count = count;
}
public RandomAttribute(int min, int max, int count)
{
_source = new IntDataSource(min, max, count);
}
[CLSCompliant(false)]
public RandomAttribute(uint min, uint max, int count)
{
_source = new UIntDataSource(min, max, count);
}
public RandomAttribute(long min, long max, int count)
{
_source = new LongDataSource(min, max, count);
}
[CLSCompliant(false)]
public RandomAttribute(ulong min, ulong max, int count)
{
_source = new ULongDataSource(min, max, count);
}
public RandomAttribute(short min, short max, int count)
{
_source = new ShortDataSource(min, max, count);
}
[CLSCompliant(false)]
public RandomAttribute(ushort min, ushort max, int count)
{
_source = new UShortDataSource(min, max, count);
}
public RandomAttribute(double min, double max, int count)
{
_source = new DoubleDataSource(min, max, count);
}
public RandomAttribute(float min, float max, int count)
{
_source = new FloatDataSource(min, max, count);
}
public RandomAttribute(byte min, byte max, int count)
{
_source = new ByteDataSource(min, max, count);
}
[CLSCompliant(false)]
public RandomAttribute(sbyte min, sbyte max, int count)
{
_source = new SByteDataSource(min, max, count);
}
public IEnumerable GetData(IParameterInfo parameter)
{
Type parameterType = parameter.ParameterType;
if (_source == null) {
if (parameterType == typeof(int))
_source = new IntDataSource(_count);
else if (parameterType == typeof(uint)) {
_source = new UIntDataSource(_count);
} else if (parameterType == typeof(long)) {
_source = new LongDataSource(_count);
} else if (parameterType == typeof(ulong)) {
_source = new ULongDataSource(_count);
} else if (parameterType == typeof(short)) {
_source = new ShortDataSource(_count);
} else if (parameterType == typeof(ushort)) {
_source = new UShortDataSource(_count);
} else if (parameterType == typeof(double)) {
_source = new DoubleDataSource(_count);
} else if (parameterType == typeof(float)) {
_source = new FloatDataSource(_count);
} else if (parameterType == typeof(byte)) {
_source = new ByteDataSource(_count);
} else if (parameterType == typeof(sbyte)) {
_source = new SByteDataSource(_count);
} else if (parameterType == typeof(decimal)) {
_source = new DecimalDataSource(_count);
} else if (parameterType == typeof(Guid)) {
_source = new GuidDataSource(_count);
} else if (parameterType.IsEnum) {
_source = new EnumDataSource(_count);
} else {
_source = new IntDataSource(_count);
}
} else if (_source.DataType != parameterType && WeConvert(_source.DataType, parameterType)) {
_source.Distinct = Distinct;
_source = new RandomDataConverter(_source);
}
_source.Distinct = Distinct;
return _source.GetData(parameter);
}
private bool WeConvert(Type sourceType, Type targetType)
{
if (targetType == typeof(short) || targetType == typeof(ushort) || targetType == typeof(byte) || targetType == typeof(sbyte))
return sourceType == typeof(int);
if (targetType == typeof(decimal)) {
if (!(sourceType == typeof(int)))
return sourceType == typeof(double);
return true;
}
return false;
}
}
}