Skip to content

Commit

Permalink
Fix setting property with private setter on base type (#390)
Browse files Browse the repository at this point in the history
* Fix setting property with private setter on base type

* Small refactoring: Breakup LINQ statement for debugability and better readability.

Co-authored-by: Thomas Levesque <thomaslevesque@users.noreply.github.com>
Co-authored-by: bchavez <bchavez@bitarmory.com>
  • Loading branch information
3 people authored Aug 29, 2021
1 parent e8c1f45 commit ab05cc0
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 4 deletions.
49 changes: 49 additions & 0 deletions Source/Bogus.Tests/GitHubIssues/Issue389.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using FluentAssertions;
using Xunit;

namespace Bogus.Tests.GitHubIssues
{
public class Issue389
{
[Fact]
public void property_with_private_setter_in_base_class_is_assigned()
{
var foo = new Faker<Foo>()
.RuleFor(f => f.PropInFoo, _ => 42)
.RuleFor(f => f.PropInFooBase, _ => 123)
.Generate();

foo.PropInFoo.Should().Be(42);
foo.PropInFooBase.Should().Be(123);
}

public class FooBase
{
public int PropInFooBase { get; private set; }
}

public class Foo : FooBase
{
public int PropInFoo { get; private set; }
}

public class Zoo : Foo
{
public int PropInZoo { get; private set; }
}

[Fact]
public void property_with_private_setter_inheritance_chain_is_assigned()
{
var zoo = new Faker<Zoo>()
.RuleFor(f => f.PropInFoo, _ => 42)
.RuleFor(f => f.PropInFooBase, _ => 123)
.RuleFor(f => f.PropInZoo, _ => 77)
.Generate();

zoo.PropInFoo.Should().Be(42);
zoo.PropInFooBase.Should().Be(123);
zoo.PropInZoo.Should().Be(77);
}
}
}
31 changes: 27 additions & 4 deletions Source/Bogus/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public Binder(BindingFlags bindingFlags)
{
BindingFlags = bindingFlags;
}


/// <summary>
/// Given T, the method will return a Dictionary[string,MemberInfo] where
Expand All @@ -60,27 +61,34 @@ public Binder(BindingFlags bindingFlags)
/// <returns>The full set of MemberInfos for injection.</returns>
public virtual Dictionary<string, MemberInfo> GetMembers(Type t)
{
var group = t.GetAllMembers(BindingFlags)
var allReflectedMembers = t.GetAllMembers(this.BindingFlags)
.Select(m => UseBaseTypeDeclaredPropertyInfo(t, m));

var settableMembers = allReflectedMembers
.Where(m =>
{
if( m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any() )
{
//no compiler generated stuff
return false;
}

if( m is PropertyInfo pi )
{
return pi.CanWrite;
}

if( m is FieldInfo fi )
{
//No private fields.
//GitHub Issue #13
return !fi.IsPrivate;
}

return false;
})
.GroupBy(mi => mi.Name);
});

var settableMembersByName = settableMembers.GroupBy(mi => mi.Name);

//Issue #70 we could get back multiple keys
//when reflecting over a type. Consider:
Expand All @@ -92,7 +100,22 @@ public virtual Dictionary<string, MemberInfo> GetMembers(Type t)
//reflected MemberInfo that was returned from
//reflection; the second one was the inherited
//ClassA.Value.
return group.ToDictionary(k => k.Key, g => g.First());
return settableMembersByName.ToDictionary(k => k.Key, g => g.First());
}

//Issue #389 - Use Declaring Base Type PropertyInfo instead of a DerivedA's
//PropertyInfo because DerivedA's PropertyInfo could say property is not
//write-able.
protected virtual MemberInfo UseBaseTypeDeclaredPropertyInfo(Type t, MemberInfo m)
{
if( m is PropertyInfo {CanWrite: false} && m.DeclaringType is not null && m.DeclaringType != t )
{
var newPropInfo = m.DeclaringType.GetProperty(m.Name, this.BindingFlags);
if( newPropInfo is not null )
return newPropInfo;
}

return m;
}
}
}

0 comments on commit ab05cc0

Please sign in to comment.