Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
stakx committed Jun 21, 2017
2 parents 54dd9dc + 62f5413 commit acd1165
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 4 deletions.
8 changes: 6 additions & 2 deletions Source/It.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,25 @@ public static TValue IsNotIn<TValue>(params TValue[] items)
/// <include file='It.xdoc' path='docs/doc[@for="It.IsRegex(regex)"]/*'/>
public static string IsRegex(string regex)
{
Guard.NotNull(() => regex, regex);

// The regex is constructed only once.
var re = new Regex(regex);

// But evaluated every time :)
return Match<string>.Create(value => re.IsMatch(value), () => It.IsRegex(regex));
return Match<string>.Create(value => value != null && re.IsMatch(value), () => It.IsRegex(regex));
}

/// <include file='It.xdoc' path='docs/doc[@for="It.IsRegex(regex,options)"]/*'/>
public static string IsRegex(string regex, RegexOptions options)
{
Guard.NotNull(() => regex, regex);

// The regex is constructed only once.
var re = new Regex(regex, options);

// But evaluated every time :)
return Match<string>.Create(value => re.IsMatch(value), () => It.IsRegex(regex, options));
return Match<string>.Create(value => value != null && re.IsMatch(value), () => It.IsRegex(regex, options));
}
}
}
27 changes: 25 additions & 2 deletions Source/Mock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,10 @@ internal void DoRaise(EventInfo ev, params object[] args)
public virtual Mock<TInterface> As<TInterface>()
where TInterface : class
{
if (this.isInitialized && !this.ImplementedInterfaces.Contains(typeof(TInterface)))
var index = this.ImplementedInterfaces.LastIndexOf(typeof(TInterface));

var isImplemented = index >= 0;
if (this.isInitialized && !isImplemented)
{
throw new InvalidOperationException(Resources.AlreadyInitialized);
}
Expand All @@ -1035,8 +1038,28 @@ public virtual Mock<TInterface> As<TInterface>()
throw new ArgumentException(Resources.AsMustBeInterface);
}

if (!this.ImplementedInterfaces.Contains(typeof(TInterface)))
var isNotOrInternallyImplemented = index < this.InternallyImplementedInterfaceCount - 1; // - 1 because of IMocked<>
if (isNotOrInternallyImplemented)
{
// We get here for either of two reasons:
//
// 1. We are being asked to implement an interface that the mocked type does *not* itself
// inherit or implement. We need to hand this interface type to DynamicProxy's
// `CreateClassProxy` method as an additional interface to be implemented. Therefore we
// add it at the end of this list, after the "internally implemented" interfaces
// (i.e. those that the mocked type inherits or implements itself, plus `IMocked<>`).
// In this case, `index == -1`.
//
// 2. The user is possibly going to create a setup through an interface type that the
// mocked type *does* implement. Since the mocked type might implement that interface's
// methods non-virtually, we can only intercept those if DynamicProxy reimplements the
// interface in the generated proxy type. Therefore we do the same as for (1). Note
// that this might lead to the interface type being contained twice in the list, once
// as an "internally implemented" type, and once as an "additional" type. That should
// not matter apart from slightly higher memory consumption, but it has the benefit
// that we don't need to perform a non-atomic removal of the "internally implemented"
// item.
// In this case, `index >= 0 && index < this.InternallyImplementedInterfaceCount - 1`.
this.ImplementedInterfaces.Add(typeof(TInterface));
}

Expand Down
28 changes: 28 additions & 0 deletions UnitTests/MatchersFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,34 @@ public void RegexMatchesAndEagerlyEvaluates()
Assert.Equal("foo", mock.Object.Execute("B"));
}

[Fact]
public void RegexMustNotBeNull()
{
Assert.Throws<ArgumentNullException>(() => It.IsRegex(null));
}

[Fact]
public void RegexMustNotBeNullWithOptions()
{
Assert.Throws<ArgumentNullException>(() => It.IsRegex(null, RegexOptions.None));
}

[Fact]
public void NullNeverMatchesRegex()
{
var mock = new Mock<IFoo>();
mock.Setup(foo => foo.Execute(It.IsRegex(".*"))).Returns("foo");
Assert.NotEqual("foo", mock.Object.Execute(null));
}

[Fact]
public void NullNeverMatchesRegexWithOptions()
{
var mock = new Mock<IFoo>();
mock.Setup(foo => foo.Execute(It.IsRegex(".*", RegexOptions.None))).Returns("foo");
Assert.NotEqual("foo", mock.Object.Execute(null));
}

[Fact]
public void MatchesEvenNumbersWithLambdaMatching()
{
Expand Down
38 changes: 38 additions & 0 deletions UnitTests/Regressions/IssueReportsFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,44 @@ protected Foo(SerializationInfo info, StreamingContext context) { }

#endregion

#region 383

public class Issue383
{
[Fact]
public void AsInterface_CanSetupMethodOfInterfaceThatClassDoesNotImplement()
{
var mock = new Mock<DoesNotImplementI>();
mock.As<I>().Setup(x => x.Foo()).Returns(42);
Assert.Equal(42, (mock.Object as I).Foo());
}

[Fact]
public void AsInterface_CanSetupMethodOfInterfaceThatClassImplementsAsNonVirtual()
{
var mock = new Mock<ImplementsI>();
mock.As<I>().Setup(x => x.Foo()).Returns(42);
Assert.Equal(42, (mock.Object as I).Foo());
}

public class DoesNotImplementI
{
public int Foo() => 13;
}

public interface I
{
int Foo();
}

public class ImplementsI : I
{
public int Foo() => 13;
}
}

#endregion

// Old @ Google Code

#region #47
Expand Down

0 comments on commit acd1165

Please sign in to comment.