Skip to content

Commit

Permalink
Merge branch 'array-support'
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaronontheweb committed Mar 18, 2012
2 parents 99ad992 + 18f805e commit a889791
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 28 deletions.
1 change: 1 addition & 0 deletions Faker.Tests/Faker.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
<Compile Include="GeneratorTests\NameGeneratorTests.cs" />
<Compile Include="GeneratorTests\NumberGeneratorTests.cs" />
<Compile Include="GeneratorTests\StringsGeneratorTests.cs" />
<Compile Include="MatcherTests\ListMatcherTests.cs" />
<Compile Include="MatcherTests\NestedPocoMatcherTests.cs" />
<Compile Include="MatcherTests\SimplePocoMatcherTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
110 changes: 110 additions & 0 deletions Faker.Tests/MatcherTests/ListMatcherTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NUnit.Framework;

namespace Faker.Tests.MatcherTests
{
[TestFixture(Description = "Tests for validating that our matcher is able to ")]
public class ListMatcherTests
{
public const string ValidEmailRegex = @"(\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b)";

public Regex _valid_email_regex = new Regex(ValidEmailRegex, RegexOptions.IgnoreCase);

private Matcher _matcher;

#region List test classes

public class SpecialFieldsTestClass
{
public int UserID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public long Timestamp { get; set; }
public DateTime DateRegistered { get; set; }
}

public class SimpleListClass
{
public List<string> strings { get; set; }
public List<DateTime> dates { get; set; }
}

public class PocoListsClass
{
public List<SpecialFieldsTestClass> Users { get; set; }
}

#endregion

#region Setup / Teardown

[SetUp]
public void SetUp()
{
_matcher = new Matcher();
}

#endregion

#region Tests

[Test(Description = "Should be able to inject and inject values for lists of primitive elements, even with the lists currently set to null")]
public void Should_Bind_Primitive_Lists_With_Uninstantiated_Target_Objects()
{
var simpleListClass = new SimpleListClass();

_matcher.Match(simpleListClass);

Assert.IsNotNull(simpleListClass.dates);
Assert.IsNotNull(simpleListClass.strings);
Assert.IsTrue(simpleListClass.dates.Count > 0);
Assert.IsTrue(simpleListClass.strings.Count > 0);
Assert.IsTrue(simpleListClass.dates.All(x => x != default(DateTime)));
Assert.IsTrue(simpleListClass.strings.All(x => x != default(string)));
}

[Test(Description = "Should be able to inject and inject values for lists of primitive elements, even with the lists currently set to null")]
public void Should_Bind_Primitive_Lists_With_Instantiated_Target_Objects()
{
var simpleListClass = new SimpleListClass {dates = new List<DateTime>(), strings = new List<string>()};

_matcher.Match(simpleListClass);

Assert.IsNotNull(simpleListClass.dates);
Assert.IsNotNull(simpleListClass.strings);
Assert.IsTrue(simpleListClass.dates.Count > 0);
Assert.IsTrue(simpleListClass.strings.Count > 0);
Assert.IsTrue(simpleListClass.dates.All(x => x != default(DateTime)));
Assert.IsTrue(simpleListClass.strings.All(x => x != default(string)));
}

[Test(Description = "Should be able to inject and map values for lists that contain other POCO objects")]
public void Should_Bind_Lists_With_Poco_Objects()
{
var richListClass = new PocoListsClass();

_matcher.Match(richListClass);

Assert.IsNotNull(richListClass.Users);
Assert.IsTrue(richListClass.Users.Count > 0);

foreach(var user in richListClass.Users)
{
/* Assert to see that we have populated all of the fields on our test instance */
Assert.AreNotEqual(user.UserID, default(int));
Assert.AreNotEqual(user.Timestamp, default(long));
Assert.AreNotEqual(user.DateRegistered, default(DateTime));

Assert.IsNotNullOrEmpty(user.Name);
Assert.IsNotNullOrEmpty(user.Email);
Assert.IsTrue(_valid_email_regex.IsMatch(user.Email));
}
}

#endregion
}
}
28 changes: 24 additions & 4 deletions Faker/Fake.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ namespace Faker
/// </summary>
public class Fake<T> where T : new()
{
private IList<ITypeSelector> _selectors;
/// <summary>
/// Engine used to power our fakes
/// </summary>
private readonly Matcher _matcher;

public Fake()
{
_selectors = new List<ITypeSelector>();
_matcher = new Matcher();
}

/// <summary>
Expand All @@ -25,7 +28,14 @@ public Fake()
/// <returns>A populated instance of a given class</returns>
T Generate()
{
throw new NotImplementedException();
//create a new instance of the type we want to Fake
var instance = (T)Matcher.SafeObjectCreate(typeof(T));

//Match all of the properties of the object and come up with the most reasonable guess we can as to the type of data needed
_matcher.Match(instance);

//Return the instance once matching is complete
return instance;
}

/// <summary>
Expand All @@ -34,7 +44,17 @@ T Generate()
/// <returns>A list of populated instances with length [count] of a given class</returns>
IList<T> Generate(int count)
{
throw new NotImplementedException();
//Create a list to hold all of the fakes we want to return back to the caller
var items = new List<T>();

//Build the list of objects
for(var i = 0; i < count; i++)
{
items.Add(this.Generate());
}

//Return the list to the caller
return items;
}
}
}
78 changes: 54 additions & 24 deletions Faker/Matcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class Matcher
/// <summary>
/// Default constructor - uses the default TypeTable
/// </summary>
public Matcher():this(new TypeTable()){}
public Matcher() : this(new TypeTable()) { }

/// <summary>
/// Constructor which accepts a TypeTable as an argument
Expand All @@ -39,7 +39,7 @@ public Matcher(TypeTable table)
public virtual void Match<T>(T targetObject) where T : new()
{
//Get all of the properties of the class
var properties = typeof (T).GetProperties();
var properties = typeof(T).GetProperties();

ProcessProperties(properties, targetObject);
}
Expand Down Expand Up @@ -75,21 +75,21 @@ protected virtual void ProcessProperty(PropertyInfo property, object targetObjec
var selectorCount = TypeMap.CountSelectors(propertyType);

//We have some matching selectors, so we'll evaluate and return the best match
if(selectorCount > 0)
if (selectorCount > 0)
{
//Evaluate all of the possible selectors and find the first available match
var selector = EvaluateSelectors(property, TypeMap.GetSelectors(propertyType));

//We found a matching selector
if(!(selector is MissingSelector))
if (!(selector is MissingSelector))
{
selector.Generate(targetObject, property); //Bind the property
return; //Exit
}
}

//Check to see if the type is a class and has a default constructor
if (propertyType.IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null)
if (propertyType.IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null && !IsArray(propertyType))
{
var subProperties = propertyType.GetProperties();

Expand All @@ -106,60 +106,90 @@ protected virtual void ProcessProperty(PropertyInfo property, object targetObjec
}

//Check to see if the type is an array or any other sort of collection
if(typeof(IList).IsAssignableFrom(propertyType))
if (IsArray(propertyType))
{
//Get the underlying type used int he array
var elementType = propertyType.GetElementType();
//var elementType = propertyType.GetElementType(); //Works only for arrays
var elementType = propertyType.GetGenericArguments()[0]; //Works for IList<T> / IEnumerable<T>

//Get a number of elements we want to create
//Note: (between 0 and 10 for now)
var elementCount = Numbers.Int(0, 10);
//Note: (between 1 and 10 for now)
var elementCount = Numbers.Int(1, 10);

//Create an instance of our target array
IList arrayInstance = null;

//If we're working with a generic list or any other sort of collection
if(propertyType.IsGenericType)
if (propertyType.IsGenericTypeDefinition)
{
arrayInstance = (IList)GenericHelper.CreateGeneric(propertyType, elementType);
}
else
{
arrayInstance = (IList) GenericHelper.CreateGeneric(typeof (List<>), elementType);
arrayInstance = (IList)GenericHelper.CreateGeneric(typeof(List<>), elementType);
}

//Determine if there's a selector available for this type
var hasSelector = TypeMap.CountSelectors(elementType) > 0;
ITypeSelector selector = null;

if(hasSelector)
//So we have a type available for this selector..
if (hasSelector)
{
//selector = EvaluateSelectors(ele)
selector = TypeMap.GetBaseSelector(elementType);
}

for(var i =0; i < elementCount; i++)
for (var i = 0; i < elementCount; i++)
{
//Create a new element instance
var element = Activator.CreateInstance(elementType);
var element = SafeObjectCreate(elementType);

if(hasSelector)
if (hasSelector)
{

selector.Generate(ref element);
}

//If the element type is a sub-class, then populate it recursively
if(elementType.IsClass)
else if (elementType.IsClass) //If the element type is a sub-class, then populate it recursively
{
var subProperties = propertyType.GetProperties();
var subProperties = elementType.GetProperties();

//Populate all of the properties on this object
ProcessProperties(subProperties, element);
}

arrayInstance.Add(element);
}

//Bind the sub-class back onto the original target object
property.SetValue(targetObject, arrayInstance, null);
}

}

/// <summary>
/// Returns true if the targeted type is an array of some sort
/// </summary>
/// <param name="targetType">the type we want to test</param>
/// <returns>true if it's an array, false otherwise</returns>
protected virtual bool IsArray(Type targetType)
{
return typeof(IList).IsAssignableFrom(targetType);
}

/// <summary>
/// Method used for safely creating new instances of type objects; handles a few special cases
/// where activation has to be done carefully.
/// </summary>
/// <param name="t">The target type we want to instantiate</param>
/// <returns>an instance of the specified type</returns>
public static object SafeObjectCreate(Type t)
{
//If the object is a string (tricky)
if (t == typeof(string))
{
return string.Empty;
}

return Activator.CreateInstance(t);
}

/// <summary>
Expand All @@ -170,10 +200,10 @@ protected virtual void ProcessProperty(PropertyInfo property, object targetObjec
/// <returns>the first matching ITypeSelector instance we could find</returns>
protected virtual ITypeSelector EvaluateSelectors(PropertyInfo propertyType, IEnumerable<ITypeSelector> selectors)
{
foreach(var selector in selectors)
foreach (var selector in selectors)
{
//If the selector can bind
if(selector.CanBind(propertyType))
if (selector.CanBind(propertyType))
{
//Return it
return selector;
Expand Down

0 comments on commit a889791

Please sign in to comment.