// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; using System.Text; using System.Text.RegularExpressions; namespace System.Json { /// /// Settings used by the class. /// public static class CreatorSettings { static CreatorSettings() { MaxArrayLength = 10; MaxListLength = 10; MaxStringLength = 100; CreateOnlyAsciiChars = false; DontCreateSurrogateChars = false; CreateDateTimeWithSubMilliseconds = true; NullValueProbability = 0.01; AvoidStackOverflowDueToTypeCycles = false; CreatorSurrogate = null; } /// /// Gets or sets the maximum length of arrays created by the . /// public static int MaxArrayLength { get; set; } /// /// Gets or sets the maximum length of lists created by the . /// public static int MaxListLength { get; set; } /// /// Gets or sets the maximum length of strings created by the . /// public static int MaxStringLength { get; set; } /// /// Gets or sets a flag indicating whether only ascii chars should be used when creating strings. /// public static bool CreateOnlyAsciiChars { get; set; } /// /// Gets or sets a flag indicating whether chars in the surrogate range can be returned by the /// when creating char instances. /// public static bool DontCreateSurrogateChars { get; set; } /// /// Gets or sets a flag indicating whether values created by the /// can have submillisecond precision. /// public static bool CreateDateTimeWithSubMilliseconds { get; set; } /// /// Gets or sets a value (0-1) indicating the probability of the /// returning a null value when creating instances of class types. /// public static double NullValueProbability { get; set; } /// /// Gets or sets a flag indicating whether the protection against stack overflow /// for cyclic types is enabled. If this flag is set, whenever a type which has already /// been created up in the stack is created again, the /// will return the default value for that type. /// public static bool AvoidStackOverflowDueToTypeCycles { get; set; } /// /// Gets or sets the instance of an which can intercept /// requests to create instances on the . /// public static InstanceCreatorSurrogate CreatorSurrogate { get; set; } } /// /// Utility class used to create test instances of primitive types. /// public static class PrimitiveCreator { static readonly Regex RelativeIPv6UriRegex = new Regex(@"^\/\/(.+\@)?\[\:\:\d\]"); static Dictionary creators; static PrimitiveCreator() { Type primitiveCreatorType = typeof(PrimitiveCreator); creators = new Dictionary(); creators.Add(typeof(bool), primitiveCreatorType.GetMethod("CreateInstanceOfBoolean", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(byte), primitiveCreatorType.GetMethod("CreateInstanceOfByte", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(char), primitiveCreatorType.GetMethod("CreateInstanceOfChar", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(DateTime), primitiveCreatorType.GetMethod("CreateInstanceOfDateTime", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(DateTimeOffset), primitiveCreatorType.GetMethod("CreateInstanceOfDateTimeOffset", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(decimal), primitiveCreatorType.GetMethod("CreateInstanceOfDecimal", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(double), primitiveCreatorType.GetMethod("CreateInstanceOfDouble", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(Guid), primitiveCreatorType.GetMethod("CreateInstanceOfGuid", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(short), primitiveCreatorType.GetMethod("CreateInstanceOfInt16", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(int), primitiveCreatorType.GetMethod("CreateInstanceOfInt32", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(long), primitiveCreatorType.GetMethod("CreateInstanceOfInt64", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(object), primitiveCreatorType.GetMethod("CreateInstanceOfObject", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(sbyte), primitiveCreatorType.GetMethod("CreateInstanceOfSByte", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(float), primitiveCreatorType.GetMethod("CreateInstanceOfSingle", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(string), primitiveCreatorType.GetMethod("CreateInstanceOfString", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(Random) }, null)); creators.Add(typeof(ushort), primitiveCreatorType.GetMethod("CreateInstanceOfUInt16", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(uint), primitiveCreatorType.GetMethod("CreateInstanceOfUInt32", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(ulong), primitiveCreatorType.GetMethod("CreateInstanceOfUInt64", BindingFlags.Public | BindingFlags.Static)); creators.Add(typeof(Uri), primitiveCreatorType.GetMethod("CreateInstanceOfUri", BindingFlags.Public | BindingFlags.Static)); } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static bool CreateInstanceOfBoolean(Random rndGen) { return rndGen.Next(2) == 0; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static byte CreateInstanceOfByte(Random rndGen) { byte[] rndValue = new byte[1]; rndGen.NextBytes(rndValue); return rndValue[0]; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static char CreateInstanceOfChar(Random rndGen) { if (CreatorSettings.CreateOnlyAsciiChars) { return (char)rndGen.Next(0x20, 0x7F); } else if (CreatorSettings.DontCreateSurrogateChars) { char c; do { c = (char)rndGen.Next((int)Char.MinValue, (int)Char.MaxValue); } while (Char.IsSurrogate(c)); return c; } else { return (char)rndGen.Next((int)Char.MinValue, (int)Char.MaxValue + 1); } } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static System.DateTime CreateInstanceOfDateTime(Random rndGen) { long temp = CreateInstanceOfInt64(rndGen); temp = Math.Abs(temp); DateTime result; try { result = new DateTime(temp % (DateTime.MaxValue.Ticks + 1)); } catch (ArgumentOutOfRangeException) { result = DateTime.Now; } int kind = rndGen.Next(3); switch (kind) { case 0: result = DateTime.SpecifyKind(result, DateTimeKind.Local); break; case 1: result = DateTime.SpecifyKind(result, DateTimeKind.Unspecified); break; default: result = DateTime.SpecifyKind(result, DateTimeKind.Utc); break; } if (!CreatorSettings.CreateDateTimeWithSubMilliseconds) { result = new DateTime( result.Year, result.Month, result.Day, result.Hour, result.Minute, result.Second, result.Millisecond, result.Kind); } return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static System.DateTimeOffset CreateInstanceOfDateTimeOffset(Random rndGen) { DateTime temp = CreateInstanceOfDateTime(rndGen); temp = DateTime.SpecifyKind(temp, DateTimeKind.Unspecified); int offsetMinutes = rndGen.Next(-14 * 60, 14 * 60); DateTimeOffset result = new DateTimeOffset(temp, TimeSpan.FromMinutes(offsetMinutes)); return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static decimal CreateInstanceOfDecimal(Random rndGen) { int low = CreateInstanceOfInt32(rndGen); int mid = CreateInstanceOfInt32(rndGen); int high = CreateInstanceOfInt32(rndGen); bool isNegative = rndGen.Next(2) == 0; const int MaxDecimalScale = 28; byte scale = (byte)rndGen.Next(0, MaxDecimalScale + 1); return new decimal(low, mid, high, isNegative, scale); } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static double CreateInstanceOfDouble(Random rndGen) { bool negative = rndGen.Next(2) == 0; int temp = rndGen.Next(40); double result; switch (temp) { case 0: return Double.NaN; case 1: return Double.PositiveInfinity; case 2: return Double.NegativeInfinity; case 3: return Double.MinValue; case 4: return Double.MaxValue; case 5: return Double.Epsilon; default: result = (double)(rndGen.NextDouble() * 100000); if (negative) { result = -result; } return result; } } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static System.Guid CreateInstanceOfGuid(Random rndGen) { byte[] temp = new byte[16]; rndGen.NextBytes(temp); return new Guid(temp); } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static short CreateInstanceOfInt16(Random rndGen) { byte[] rndValue = new byte[2]; rndGen.NextBytes(rndValue); short result = 0; for (int i = 0; i < rndValue.Length; i++) { result = (short)(result << 8); result = (short)(result | (short)rndValue[i]); } return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static int CreateInstanceOfInt32(Random rndGen) { byte[] rndValue = new byte[4]; rndGen.NextBytes(rndValue); int result = 0; for (int i = 0; i < rndValue.Length; i++) { result = (int)(result << 8); result = (int)(result | (int)rndValue[i]); } return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static long CreateInstanceOfInt64(Random rndGen) { byte[] rndValue = new byte[8]; rndGen.NextBytes(rndValue); long result = 0; for (int i = 0; i < rndValue.Length; i++) { result = (long)(result << 8); result = (long)(result | (long)rndValue[i]); } return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static object CreateInstanceOfObject(Random rndGen) { return (rndGen.Next(5) == 0) ? null : new object(); } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. [CLSCompliant(false)] public static sbyte CreateInstanceOfSByte(Random rndGen) { byte[] rndValue = new byte[1]; rndGen.NextBytes(rndValue); sbyte result = (sbyte)rndValue[0]; return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static float CreateInstanceOfSingle(Random rndGen) { bool negative = rndGen.Next(2) == 0; int temp = rndGen.Next(40); float result; switch (temp) { case 0: return Single.NaN; case 1: return Single.PositiveInfinity; case 2: return Single.NegativeInfinity; case 3: return Single.MinValue; case 4: return Single.MaxValue; case 5: return Single.Epsilon; default: result = (float)(rndGen.NextDouble() * 100000); if (negative) { result = -result; } return result; } } /// /// Creates an instance of the type. /// /// A used to create the instance. /// The size of the string to be creted. /// The characters to use when creating the string. /// An instance of the type. public static string CreateRandomString(Random rndGen, int size, string charsToUse) { int maxSize = CreatorSettings.MaxStringLength; // invalid per the XML spec (http://www.w3.org/TR/REC-xml/#charsets), cannot be sent as XML string invalidXmlChars = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u000B\u000C\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\uFFFE\uFFFF"; const int LowSurrogateMin = 0xDC00; const int LowSurrogateMax = 0xDFFF; const int HighSurrogateMin = 0xD800; const int HighSurrogateMax = 0xDBFF; if (size < 0) { double rndNumber = rndGen.NextDouble(); if (rndNumber < CreatorSettings.NullValueProbability) { return null; // 1% chance of null value } size = (int)Math.Pow(maxSize, rndNumber); // this will create more small strings than large ones size--; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < size; i++) { char c; if (charsToUse != null) { c = charsToUse[rndGen.Next(charsToUse.Length)]; sb.Append(c); } else { if (CreatorSettings.CreateOnlyAsciiChars || rndGen.Next(2) == 0) { c = (char)rndGen.Next(0x20, 0x7F); // low-ascii chars sb.Append(c); } else { do { c = (char)rndGen.Next((int)Char.MinValue, (int)Char.MaxValue + 1); } while ((LowSurrogateMin <= c && c <= LowSurrogateMax) || (invalidXmlChars.IndexOf(c) >= 0)); sb.Append(c); if (HighSurrogateMin <= c && c <= HighSurrogateMax) { // need to add a low surrogate c = (char)rndGen.Next(LowSurrogateMin, LowSurrogateMax + 1); sb.Append(c); } } } } return sb.ToString(); } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static string CreateInstanceOfString(Random rndGen) { return CreateInstanceOfString(rndGen, true); } /// /// Creates an instance of the type. /// /// A used to create the instance. /// A flag indicating whether null values can be returned. /// An instance of the type. public static string CreateInstanceOfString(Random rndGen, bool allowNull) { string result; do { result = CreateRandomString(rndGen, -1, null); } while (result == null && !allowNull); return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// The size of the string to be creted. /// The characters to use when creating the string. /// An instance of the type. public static string CreateInstanceOfString(Random rndGen, int size, string charsToUse) { return CreateRandomString(rndGen, size, charsToUse); } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. [CLSCompliant(false)] public static ushort CreateInstanceOfUInt16(Random rndGen) { byte[] rndValue = new byte[2]; rndGen.NextBytes(rndValue); ushort result = 0; for (int i = 0; i < rndValue.Length; i++) { result = (ushort)(result << 8); result = (ushort)(result | (ushort)rndValue[i]); } return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. [CLSCompliant(false)] public static uint CreateInstanceOfUInt32(Random rndGen) { byte[] rndValue = new byte[4]; rndGen.NextBytes(rndValue); uint result = 0; for (int i = 0; i < rndValue.Length; i++) { result = (uint)(result << 8); result = (uint)(result | (uint)rndValue[i]); } return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. [CLSCompliant(false)] public static ulong CreateInstanceOfUInt64(Random rndGen) { byte[] rndValue = new byte[8]; rndGen.NextBytes(rndValue); ulong result = 0; for (int i = 0; i < rndValue.Length; i++) { result = (ulong)(result << 8); result = (ulong)(result | (ulong)rndValue[i]); } return result; } /// /// Creates an instance of the type. /// /// A used to create the instance. /// An instance of the type. public static System.Uri CreateInstanceOfUri(Random rndGen) { Uri result; UriKind kind; try { string uriString; do { uriString = UriCreator.CreateUri(rndGen, out kind); } while (IsRelativeIPv6Uri(uriString, kind)); result = new Uri(uriString, kind); } catch (ArgumentException) { result = new Uri("my.schema://userName:password@my.domain/path1/path2?query1=123&query2=%22hello%22"); } return result; } /// /// Creates an instance of the a string which represents an . /// /// A used to create the instance. /// An instance of the a string which represents an . public static string CreateInstanceOfUriString(Random rndGen) { UriKind kind; return UriCreator.CreateUri(rndGen, out kind); } /// /// Checks whether this creator can create an instance of the given type. /// /// The type to be created. /// true if this creator can create an instance of the given type; false otherwise. public static bool CanCreateInstanceOf(Type type) { return creators.ContainsKey(type); } /// /// Creates an instance of the given primitive type. /// /// The type to create an instance. /// A used to create the instance. /// An instance of the given type. public static object CreatePrimitiveInstance(Type type, Random rndGen) { if (creators.ContainsKey(type)) { return creators[type].Invoke(null, new object[] { rndGen }); } else { throw new ArgumentException("Type " + type.FullName + " not supported"); } } private static bool IsRelativeIPv6Uri(string uriString, UriKind kind) { return kind == UriKind.Relative && RelativeIPv6UriRegex.Match(uriString).Success; } /// /// Creates URI instances based on RFC 2396 /// internal static class UriCreator { static readonly string digit; static readonly string upalpha; static readonly string lowalpha; static readonly string alpha; static readonly string alphanum; static readonly string hex; static readonly string mark; static readonly string unreserved; static readonly string reserved; static UriCreator() { digit = "0123456789"; upalpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; lowalpha = upalpha.ToLower(); alpha = upalpha + lowalpha; alphanum = alpha + digit; hex = digit + "ABCDEFabcdef"; mark = "-_.!~*'()"; unreserved = alphanum + mark; reserved = ";/?:@&=+$,"; } internal static string CreateUri(Random rndGen, out UriKind kind) { StringBuilder sb = new StringBuilder(); kind = UriKind.Relative; if (rndGen.Next(3) > 0) { // Add URI scheme CreateScheme(sb, rndGen); kind = UriKind.Absolute; } if (rndGen.Next(3) > 0) { // Add URI host sb.Append("//"); if (rndGen.Next(10) == 0) { CreateUserInfo(sb, rndGen); } CreateHost(sb, rndGen); if (rndGen.Next(2) > 0) { sb.Append(':'); sb.Append(rndGen.Next(65536)); } } if (rndGen.Next(4) > 0) { // Add URI path for (int i = 0; i < rndGen.Next(1, 4); i++) { sb.Append('/'); AddPathSegment(sb, rndGen); } } if (rndGen.Next(3) == 0) { // Add URI query string sb.Append('?'); AddUriC(sb, rndGen); } return sb.ToString(); } private static void CreateScheme(StringBuilder sb, Random rndGen) { int size = rndGen.Next(1, 10); AddChars(sb, rndGen, alpha, 1); string schemeChars = alpha + digit + "+-."; AddChars(sb, rndGen, schemeChars, size); sb.Append(':'); } private static void CreateIPv4Address(StringBuilder sb, Random rndGen) { for (int i = 0; i < 4; i++) { if (i > 0) { sb.Append('.'); } sb.Append(rndGen.Next(1000)); } } private static void AddIPv6AddressPart(StringBuilder sb, Random rndGen) { int size = rndGen.Next(1, 10); if (size > 4) { size = 4; } AddChars(sb, rndGen, hex, size); } private static void CreateIPv6Address(StringBuilder sb, Random rndGen) { sb.Append('['); int temp = rndGen.Next(6); int i; switch (temp) { case 0: sb.Append("::"); break; case 1: sb.Append("::1"); break; case 2: sb.Append("FF01::101"); break; case 3: sb.Append("::1"); break; case 4: for (i = 0; i < 3; i++) { AddIPv6AddressPart(sb, rndGen); sb.Append(':'); } for (i = 0; i < 3; i++) { sb.Append(':'); AddIPv6AddressPart(sb, rndGen); } break; default: for (i = 0; i < 8; i++) { if (i > 0) { sb.Append(':'); } AddIPv6AddressPart(sb, rndGen); } break; } sb.Append(']'); } private static void AddChars(StringBuilder sb, Random rndGen, string validChars, int size) { for (int i = 0; i < size; i++) { sb.Append(validChars[rndGen.Next(validChars.Length)]); } } private static void CreateHostName(StringBuilder sb, Random rndGen) { int domainLabelCount = rndGen.Next(4); int size; for (int i = 0; i < domainLabelCount; i++) { AddChars(sb, rndGen, alphanum, 1); size = rndGen.Next(10) - 1; if (size > 0) { AddChars(sb, rndGen, alphanum + "-", size); AddChars(sb, rndGen, alphanum, 1); } sb.Append('.'); } AddChars(sb, rndGen, alpha, 1); size = rndGen.Next(10) - 1; if (size > 0) { AddChars(sb, rndGen, alphanum + "-", size); AddChars(sb, rndGen, alphanum, 1); } } private static void CreateHost(StringBuilder sb, Random rndGen) { int temp = rndGen.Next(3); switch (temp) { case 0: CreateIPv4Address(sb, rndGen); break; case 1: CreateIPv6Address(sb, rndGen); break; case 2: CreateHostName(sb, rndGen); break; } } private static void CreateUserInfo(StringBuilder sb, Random rndGen) { AddChars(sb, rndGen, alpha, rndGen.Next(1, 10)); if (rndGen.Next(3) > 0) { sb.Append(':'); AddChars(sb, rndGen, alpha, rndGen.Next(1, 10)); } sb.Append('@'); } private static void AddEscapedChar(StringBuilder sb, Random rndGen) { sb.Append('%'); AddChars(sb, rndGen, hex, 2); } private static void AddPathSegment(StringBuilder sb, Random rndGen) { string pchar = unreserved + ":@&=+$,"; int size = rndGen.Next(1, 10); for (int i = 0; i < size; i++) { if (rndGen.Next(pchar.Length + 1) > 0) { AddChars(sb, rndGen, pchar, 1); } else { AddEscapedChar(sb, rndGen); } } } private static void AddUriC(StringBuilder sb, Random rndGen) { int size = rndGen.Next(20); string reservedPlusUnreserved = reserved + unreserved; for (int i = 0; i < size; i++) { if (rndGen.Next(5) > 0) { AddChars(sb, rndGen, reservedPlusUnreserved, 1); } else { AddEscapedChar(sb, rndGen); } } } } } /// /// Utility class used to create test instances of arbitrary types. /// public static class InstanceCreator { private static Stack typesInCreationStack = new Stack(); /// /// Creates an instance of an array type. /// /// The array type. /// A used to create the instance. /// An instance of the given array type. public static object CreateInstanceOfArray(Type arrayType, Random rndGen) { Type type = arrayType.GetElementType(); double rndNumber = rndGen.NextDouble(); if (rndNumber < CreatorSettings.NullValueProbability) { return null; // 1% chance of null value } int size = (int)Math.Pow(CreatorSettings.MaxArrayLength, rndNumber); // this will create more small arrays than large ones size--; Array result = Array.CreateInstance(type, size); for (int i = 0; i < size; i++) { result.SetValue(CreateInstanceOf(type, rndGen), i); } return result; } /// /// Creates an instance of a . /// /// The List<T> type. /// A used to create the instance. /// An instance of the given list type. public static object CreateInstanceOfListOfT(Type listType, Random rndGen) { Type type = listType.GetGenericArguments()[0]; double rndNumber = rndGen.NextDouble(); if (rndNumber < CreatorSettings.NullValueProbability) { return null; // 1% chance of null value } int size = (int)Math.Pow(CreatorSettings.MaxListLength, rndNumber); // this will create more small lists than large ones size--; object result = Activator.CreateInstance(listType); MethodInfo addMethod = listType.GetMethod("Add"); for (int i = 0; i < size; i++) { addMethod.Invoke(result, new object[] { CreateInstanceOf(type, rndGen) }); } return result; } /// /// Creates an instance of a . /// /// The LinkedList<T> type. /// A used to create the instance. /// An instance of the given list type. public static object CreateInstanceOfLinkedListOfT(Type listType, Random rndGen) { Type type = listType.GetGenericArguments()[0]; double rndNumber = rndGen.NextDouble(); if (rndNumber < CreatorSettings.NullValueProbability) { return null; // 1% chance of null value } int size = (int)Math.Pow(CreatorSettings.MaxListLength, rndNumber); // this will create more small lists than large ones size--; object result = Activator.CreateInstance(listType); MethodInfo addMethod = listType.GetMethod("AddLast", new Type[] { type }); for (int i = 0; i < size; i++) { addMethod.Invoke(result, new object[] { CreateInstanceOf(type, rndGen) }); } return result; } /// /// Creates an instance of a . /// /// The IEnumerable<T> type. /// The type to be enumerated. /// A used to create the instance. /// An instance of the given enumerable type. public static object CreateInstanceOfIEnumerableOfT(Type enumerableOfTType, Type enumeredType, Random rndGen) { double rndNumber = rndGen.NextDouble(); if (!enumerableOfTType.IsValueType && rndNumber < CreatorSettings.NullValueProbability) { return null; // 1% chance of null value } int size = (int)Math.Pow(CreatorSettings.MaxListLength, rndNumber); // this will create more small lists than large ones size--; object result = Activator.CreateInstance(enumerableOfTType); MethodInfo addMethod = enumerableOfTType.GetMethod("Add", new Type[] { enumeredType }); if (addMethod == null) { throw new ArgumentException("Cannot create an instance of an IEnumerable type which does not have a public Add method"); } for (int i = 0; i < size; i++) { addMethod.Invoke(result, new object[] { CreateInstanceOf(enumeredType, rndGen) }); } return result; } /// /// Creates an instance of a . /// /// The Nullable<T> type. /// A used to create the instance. /// An instance of the given nullable type. public static object CreateInstanceOfNullableOfT(Type nullableOfTType, Random rndGen) { if (rndGen.Next(5) == 0) { return null; } Type type = nullableOfTType.GetGenericArguments()[0]; return CreateInstanceOf(type, rndGen); } /// /// Creates an instance of an enum type.. /// /// The enum type. /// A used to create the instance. /// An instance of the given enum type. public static object CreateInstanceOfEnum(Type enumType, Random rndGen) { bool hasFlags = enumType.GetCustomAttributes(typeof(FlagsAttribute), true).Length > 0; Array possibleValues = Enum.GetValues(enumType); if (possibleValues.Length == 0) { return 0; } if (!hasFlags) { return possibleValues.GetValue(rndGen.Next(possibleValues.Length)); } else { Type underlyingType = Enum.GetUnderlyingType(enumType); string strResult; if (underlyingType.FullName == typeof(ulong).FullName) { ulong result = 0; if (rndGen.Next(10) > 0) { // 10% chance of value zero foreach (object value in possibleValues) { if (rndGen.Next(2) == 0) { result |= ((IConvertible)value).ToUInt64(null); } } } strResult = result.ToString(); } else { long result = 0; if (rndGen.Next(10) > 0) { // 10% chance of value zero foreach (object value in possibleValues) { if (rndGen.Next(2) == 0) { result |= ((IConvertible)value).ToInt64(null); } } } strResult = result.ToString(); } return Enum.Parse(enumType, strResult, true); } } /// /// Creates an instance of a . /// /// The Dictionary<K,V> type. /// A used to create the instance. /// An instance of the given dictionary type. public static object CreateInstanceOfDictionaryOfKAndV(Type dictionaryType, Random rndGen) { Type[] genericArgs = dictionaryType.GetGenericArguments(); Type typeK = genericArgs[0]; Type typeV = genericArgs[1]; double rndNumber = rndGen.NextDouble(); if (rndNumber < CreatorSettings.NullValueProbability) { return null; // 1% chance of null value } int size = (int)Math.Pow(CreatorSettings.MaxListLength, rndNumber); // this will create more small dictionaries than large ones size--; object result = Activator.CreateInstance(dictionaryType); MethodInfo addMethod = dictionaryType.GetMethod("Add"); MethodInfo containsKeyMethod = dictionaryType.GetMethod("ContainsKey"); for (int i = 0; i < size; i++) { object newKey; do { newKey = CreateInstanceOf(typeK, rndGen); } while (newKey == null); bool containsKey = (bool)containsKeyMethod.Invoke(result, new object[] { newKey }); if (!containsKey) { object newValue = CreateInstanceOf(typeV, rndGen); addMethod.Invoke(result, new object[] { newKey, newValue }); } } return result; } /// /// Creates an instance of the given type. /// /// The type to create an instance from. /// A random generator used to populate the instance. /// An instance of the given type. public static T CreateInstanceOf(Random rndGen) { return (T)InstanceCreator.CreateInstanceOf(typeof(T), rndGen); } /// /// Creates an instance of the given type. /// /// The type to create an instance from. /// A random generator used to populate the instance. /// A flag indicating whether null values can be returned by this method. /// An instance of the given type. public static object CreateInstanceOf(Type type, Random rndGen, bool allowNulls) { double currentNullProbability = CreatorSettings.NullValueProbability; if (!allowNulls) { CreatorSettings.NullValueProbability = 0; } object result = CreateInstanceOf(type, rndGen); if (!allowNulls) { CreatorSettings.NullValueProbability = currentNullProbability; } return result; } /// /// Creates an instance of the given type. /// /// The type to create an instance from. /// A random generator used to populate the instance. /// An instance of the given type. public static object CreateInstanceOf(Type type, Random rndGen) { if (CreatorSettings.CreatorSurrogate != null) { if (CreatorSettings.CreatorSurrogate.CanCreateInstanceOf(type)) { return CreatorSettings.CreatorSurrogate.CreateInstanceOf(type, rndGen); } } ConstructorInfo randomConstructor = type.GetConstructor(new Type[] { typeof(Random) }); if (randomConstructor != null) { // it's possible that a class with a Type.GetConstructor will return a constructor // which takes a System.Object; we only want to use if it really takes a Random argument. ParameterInfo[] ctorParameters = randomConstructor.GetParameters(); if (ctorParameters.Length == 1 && ctorParameters[0].ParameterType == typeof(Random)) { return randomConstructor.Invoke(new object[] { rndGen }); } } // Allow for a static factory method (called 'CreateInstance' to allow for inheritance scenarios MethodInfo randomFactoryMethod = type.GetMethod("CreateInstance", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(Random) }, null); if (randomFactoryMethod != null) { ParameterInfo[] methodParameters = randomFactoryMethod.GetParameters(); if (methodParameters.Length == 1 && methodParameters[0].ParameterType == typeof(Random)) { return randomFactoryMethod.Invoke(null, new object[] { rndGen }); } } object result = null; Type genericElementType = null; if (CreatorSettings.AvoidStackOverflowDueToTypeCycles) { if (typesInCreationStack.Contains(type)) { if (type.IsValueType) { return Activator.CreateInstance(type); } else { return null; } } typesInCreationStack.Push(type); } if (PrimitiveCreator.CanCreateInstanceOf(type)) { result = PrimitiveCreator.CreatePrimitiveInstance(type, rndGen); } else if (type.IsEnum) { result = CreateInstanceOfEnum(type, rndGen); } else if (type.IsArray) { result = CreateInstanceOfArray(type, rndGen); } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { result = CreateInstanceOfNullableOfT(type, rndGen); } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { result = CreateInstanceOfListOfT(type, rndGen); } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(LinkedList<>)) { result = CreateInstanceOfLinkedListOfT(type, rndGen); } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { result = CreateInstanceOfDictionaryOfKAndV(type, rndGen); } else if (ContainsAttribute(type, typeof(DataContractAttribute))) { result = ClassInstanceCreator.DataContractCreator.CreateInstanceOf(type, rndGen); } else if (IsIEnumerableOfT(type, out genericElementType)) { result = CreateInstanceOfIEnumerableOfT(type, genericElementType, rndGen); } else if (type.IsPublic || type.IsNestedPublic) { result = ClassInstanceCreator.POCOCreator.CreateInstanceOf(type, rndGen); } else { result = Activator.CreateInstance(type); } if (CreatorSettings.AvoidStackOverflowDueToTypeCycles) { typesInCreationStack.Pop(); } return result; } internal static bool ContainsAttribute(MemberInfo member, Type attributeType) { object[] attributes = member.GetCustomAttributes(attributeType, false); return attributes != null && attributes.Length > 0; } static bool IsIEnumerableOfT(Type type, out Type genericArgumentType) { genericArgumentType = null; foreach (Type interfaceType in type.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { genericArgumentType = interfaceType.GetGenericArguments()[0]; return true; } } return false; } } /// /// Helper class used to create test instances. /// public class ClassInstanceCreator { static ClassInstanceCreator dataContractCreatorWithNonPublicMembers; static ClassInstanceCreator dataContractCreator; static ClassInstanceCreator pocoCreator; bool includeNonPublicMembers; GetMemberNameDelegate getMemberNameDelegate; ShouldBeIncludedDelegate shouldBeIncludedDelegate; private ClassInstanceCreator(GetMemberNameDelegate getMemberNameDelegate, ShouldBeIncludedDelegate shouldBeIncludedDelegate, bool includeNonPublicMembers) : this(getMemberNameDelegate, shouldBeIncludedDelegate) { this.includeNonPublicMembers = includeNonPublicMembers; } private ClassInstanceCreator(GetMemberNameDelegate getMemberNameDelegate, ShouldBeIncludedDelegate shouldBeIncludedDelegate) { this.getMemberNameDelegate = getMemberNameDelegate; this.shouldBeIncludedDelegate = shouldBeIncludedDelegate; } delegate string GetMemberNameDelegate(MemberInfo member); delegate bool ShouldBeIncludedDelegate(MemberInfo member); /// /// Gets an instance of a creator which knows how to create instance of types /// decorated with the . /// public static ClassInstanceCreator DataContractCreator { get { if (dataContractCreator == null) { dataContractCreator = new ClassInstanceCreator(GetDataMemberName, IncludeDataMembersOnly); } return dataContractCreator; } } /// /// Gets an instance of a creator which knows how to create instance of types /// which only sets members decorated with the . /// public static ClassInstanceCreator DataContractCreatorWithNonPublicMembers { get { if (dataContractCreatorWithNonPublicMembers == null) { dataContractCreatorWithNonPublicMembers = new ClassInstanceCreator(GetDataMemberName, IncludeDataMembersOnly, true); } return dataContractCreatorWithNonPublicMembers; } } /// /// Gets an instance of a creator which knows how to create instances of POCO /// types (public types, not decorated with the and /// which have a parameterless public constructor). /// public static ClassInstanceCreator POCOCreator { get { if (pocoCreator == null) { pocoCreator = new ClassInstanceCreator( delegate(MemberInfo member) { return member.Name; }, DoNotIncludeExcludedMembers); } return pocoCreator; } } /// /// Creates an instance of the given type. /// /// The type to create an instance from. /// A random generator used to populate the instance. /// An instance of the given type. public object CreateInstanceOf(Type type, Random rndGen) { object result = null; if (rndGen.NextDouble() < CreatorSettings.NullValueProbability && !type.IsValueType) { // 1% chance of null object, if it is not a struct return null; } ConstructorInfo randomConstructor = type.GetConstructor(new Type[] { typeof(Random) }); if (randomConstructor != null && randomConstructor.GetParameters()[0].ParameterType == typeof(Random)) { result = randomConstructor.Invoke(new object[] { rndGen }); } else { ConstructorInfo defaultConstructor = type.GetConstructor(new Type[0]); if (defaultConstructor != null || type.IsValueType) { result = Activator.CreateInstance(type); this.SetFieldsAndProperties(type, result, rndGen); } else { throw new ArgumentException("Don't know how to create an instance of " + type.FullName); } } return result; } private static string GetDataMemberName(MemberInfo member) { DataMemberAttribute[] dataMemberAttr = (DataMemberAttribute[])member.GetCustomAttributes(typeof(DataMemberAttribute), false); if (dataMemberAttr == null || dataMemberAttr.Length == 0 || dataMemberAttr[0].Name == null) { return member.Name; } else { return dataMemberAttr[0].Name; } } private static bool ContainsAttribute(MemberInfo member, string attributeName) { object[] customAttributes = member.GetCustomAttributes(false); foreach (object attribute in customAttributes) { if (attribute != null && attribute.GetType().Name == attributeName) { return true; } } return false; } private static bool IncludeDataMembersOnly(MemberInfo member) { return ContainsAttribute(member, "DataMemberAttribute"); } private static bool DoNotIncludeExcludedMembers(MemberInfo member) { return !ContainsAttribute(member, "IgnoreDataMemberAttribute"); } int CompareDataMembers(MemberInfo member1, MemberInfo member2) { return this.getMemberNameDelegate(member1).CompareTo(this.getMemberNameDelegate(member2)); } private void SetFieldsAndProperties(Type type, object obj, Random rndGen) { List members = new List(); BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (this.includeNonPublicMembers) { bindingFlags |= BindingFlags.NonPublic; } FieldInfo[] fields = type.GetFields(bindingFlags); PropertyInfo[] properties = type.GetProperties(bindingFlags); foreach (FieldInfo field in fields) { if (this.shouldBeIncludedDelegate(field)) { members.Add(field); } } foreach (PropertyInfo prop in properties) { if (this.shouldBeIncludedDelegate(prop)) { members.Add(prop); } } members.Sort(new Comparison(this.CompareDataMembers)); foreach (MemberInfo member in members) { if (member is FieldInfo) { object fieldValue = InstanceCreator.CreateInstanceOf(((FieldInfo)member).FieldType, rndGen); ((FieldInfo)member).SetValue(obj, fieldValue); } else { PropertyInfo propInfo = (PropertyInfo)member; if (propInfo.CanWrite) { object propertyValue = InstanceCreator.CreateInstanceOf(propInfo.PropertyType, rndGen); propInfo.SetValue(obj, propertyValue, null); } else { if (!this.TrySettingMembersOfGetOnlyCollection(rndGen, propInfo, obj)) { throw new ArgumentException("Cannot set property " + propInfo.Name + " of type " + type.FullName); } } } } } private bool TrySettingMembersOfGetOnlyCollection(Random rndGen, PropertyInfo propInfo, object obj) { Type propType = propInfo.PropertyType; object propValue = propInfo.GetValue(obj, null); if (propValue == null) { // need something to set the value to return false; } if (propType.IsArray) { Array propArray = (Array)propValue; Type arrayType = propType.GetElementType(); for (int i = 0; i < propArray.Length; i++) { object elementValue = InstanceCreator.CreateInstanceOf(arrayType, rndGen); propArray.SetValue(elementValue, i); } return true; } else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(List<>)) { Type listType = propType.GetGenericArguments()[0]; MethodInfo addMethod = propType.GetMethod("Add", new Type[] { listType }); double rndNumber = rndGen.NextDouble(); int size = (int)Math.Pow(CreatorSettings.MaxListLength, rndNumber); // this will create more small lists than large ones size--; for (int i = 0; i < size; i++) { object listValue = InstanceCreator.CreateInstanceOf(listType, rndGen); addMethod.Invoke(propValue, new object[] { listValue }); } return true; } return false; } } /// /// Enables tests to create specific instances of certain types. /// public abstract class InstanceCreatorSurrogate { /// /// Checks whether this surrogate can create instances of a given type. /// /// The type which needs to be created. /// A true value if this surrogate can create the given type; a /// false value otherwise. public abstract bool CanCreateInstanceOf(Type type); /// /// Creates an instance of the given type. /// /// The type to create an instance for. /// A Random generator to assist in creating the instance. /// An instance of the given type. public abstract object CreateInstanceOf(Type type, Random rndGen); } }