Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Telerik.JustMock.Portable/Core/Context/AsyncContextResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
JustMock Lite
Copyright © 2010-2015,2019 Progress Software Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Reflection;

namespace Telerik.JustMock.Core.Context
{
/// <summary>
/// Class for automatically resolving the context of async method calls.
/// </summary>
public class AsyncContextResolver
{
/// <summary>
/// Returns the actual test method from where the async call starts.
/// </summary>
/// <returns>The test method</returns>
public static MethodBase GetContext()
{
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Telerik.JustMock.Core.Context
Expand All @@ -36,6 +37,8 @@ public MockingContextResolverBase(string assertFailedExceptionTypeName, params s

public abstract bool RetireRepository();

public abstract MethodBase GetTestMethod();

public Action<string, Exception> GetFailMethod()
{
return LocalMockingContextResolver.GetFailMethod(Type.GetType(this.assertFailedExceptionTypeName));
Expand Down
1 change: 1 addition & 0 deletions Telerik.JustMock.Portable/Telerik.JustMock.Portable.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@
<Compile Include="..\Telerik.JustMock\Core\JMDebug.cs">
<Link>Core\JMDebug.cs</Link>
</Compile>
<Compile Include="Core\Context\AsyncContextResolver.cs" />
<Compile Include="Core\Context\LocalMockingContextResolver.cs" />
<Compile Include="Core\Context\MockingContextResolverBase.cs" />
<Compile Include="Core\Context\VisualStudioPortableContextResolver.cs" />
Expand Down
44 changes: 44 additions & 0 deletions Telerik.JustMock/Core/Context/AsyncContextResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
JustMock Lite
Copyright © 2019 Progress Software Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System.ComponentModel;
using System.Reflection;

namespace Telerik.JustMock.Core.Context
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static class AsyncContextResolver
{
#if NETCORE
static IAsyncContextResolver resolver = new AsyncLocalWrapper();
#else
static IAsyncContextResolver resolver = new CallContextWrapper();
#endif
public static MethodBase GetContext()
{
return ProfilerInterceptor.GuardInternal(() =>
resolver.GetContext()
);
}

public static void CaptureContext()
{
ProfilerInterceptor.GuardInternal(() =>
resolver.CaptureContext()
);
}
}
}
43 changes: 43 additions & 0 deletions Telerik.JustMock/Core/Context/AsyncLocalWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
JustMock Lite
Copyright © 2019 Progress Software Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#if NETCORE

using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace Telerik.JustMock.Core.Context
{
internal class AsyncLocalWrapper : IAsyncContextResolver
{
static AsyncLocal<MethodBase> asyncCallPattern = new AsyncLocal<MethodBase>();
public void CaptureContext()
{
MethodBase testMethod = MockingContext.GetTestMethod();
asyncCallPattern.Value = testMethod;
}

public MethodBase GetContext()
{
return asyncCallPattern.Value;
}
}
}
#endif

50 changes: 50 additions & 0 deletions Telerik.JustMock/Core/Context/CallContextWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
JustMock Lite
Copyright © 2019 Progress Software Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#if !NETCORE
using System;
using System.Reflection;
using System.Runtime.Remoting.Messaging;

namespace Telerik.JustMock.Core.Context
{
internal class CallContextWrapper : IAsyncContextResolver
{
private static readonly string key = Guid.NewGuid().ToString("N");

public MethodBase GetContext()
{
MethodBase methodBase = CallContext.LogicalGetData(key) as MethodBase;
return methodBase;
}

public void CaptureContext()
{
MethodBase testMethod = MockingContext.GetTestMethod();
SetData(testMethod);
}

private void SetData(MethodBase methodBase)
{
if (methodBase != null)
{
CallContext.LogicalSetData(key, methodBase);
}
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,25 @@ public override MocksRepository ResolveRepository(UnresolvedContextBehavior unre
{
lock (this.repositorySync)
{
RepositoryOperationsBase entryOps = null;
int repoIdx;
RepositoryOperationsBase entryOps = null;
var testMethod = FindTestMethod(out repoIdx, out entryOps);
if (testMethod == null)
if (testMethod == null || entryOps == null)
{
return null;
}

object entryKey = entryOps.GetKey(testMethod);

MocksRepository repo = this.FindRepositoryInOps(entryOps, entryKey);
if (repo != null)
{
return repo;
}

if (unresolvedContextBehavior == UnresolvedContextBehavior.DoNotCreateNew)
{
return null;
}

//Check if this is the same kind of method but from a derived class, thus building context.
MocksRepository parentRepo = entryOps.FindRepositoryToInherit(testMethod);
Expand All @@ -61,13 +67,19 @@ public override MocksRepository ResolveRepository(UnresolvedContextBehavior unre
{
var ops = this.repoOperations[repoIdxParent];
if (ops.IsLeaf)
{
continue;
}

object parentKey = ops.GetKey(testMethod);
if (ops.IsUsedOnAllThreads)
{
parentRepo = ops.FindRepositoryFromAnyThread(parentKey);
}
else
{
parentRepo = ops.FindRepository(parentKey) ?? ops.FindRepositoryToInherit(testMethod);
}
}
}

Expand Down Expand Up @@ -110,11 +122,7 @@ public override bool RetireRepository()
}
}

protected virtual void OnMocksRepositoryCreated(MocksRepository repo)
{
}

private MethodBase FindTestMethod(out int repoIdx, out RepositoryOperationsBase entryOps)
public override MethodBase GetTestMethod()
{
var stackTrace = new StackTrace();
var q = from method in stackTrace.EnumerateFrames()
Expand All @@ -124,11 +132,22 @@ where repoOperations.Any(repo => repo.MatchesMethod(method))
var allTestMethods = q.Distinct().ToArray();
if (allTestMethods.Length > 1)
{
var message = "Calling one test method from another could result in unexpected behavior and must be avoided. Extract common mocking logic to a non-test method. At:\n" + stackTrace;
string message = "Calling one test method from another could result in unexpected behavior and must be avoided. Extract common mocking logic to a non-test method. At:\n" + stackTrace;
DebugView.DebugTrace(message);
}
var testMethod = allTestMethods.FirstOrDefault();

MethodBase testMethod = allTestMethods.FirstOrDefault();

return testMethod;
}

protected virtual void OnMocksRepositoryCreated(MocksRepository repo)
{
}

private MethodBase FindTestMethod(out int repoIdx, out RepositoryOperationsBase entryOps)
{
MethodBase testMethod = this.GetTestMethod();
if (testMethod != null)
{
var disableAttr = Attribute.GetCustomAttribute(testMethod, typeof(DisableAutomaticRepositoryResetAttribute)) as DisableAutomaticRepositoryResetAttribute;
Expand All @@ -137,6 +156,10 @@ where repoOperations.Any(repo => repo.MatchesMethod(method))
&& !disableAttr.AllowMocking)
throw new MockException("Using the mocking API in a test method decorated with DisableAutomaticRepositoryResetAttribute is unsafe. Read the documentation of the DisableAutomaticRepositoryResetAttribute class for further information and possible solutions.");
}
else
{
testMethod = AsyncContextResolver.GetContext();
}

repoIdx = 0;
entryOps = null;
Expand All @@ -152,7 +175,7 @@ where repoOperations.Any(repo => repo.MatchesMethod(method))
}
}

JMDebug.Assert(entryOps != null);
JMDebug.Assert(entryOps != null);
}

return testMethod;
Expand All @@ -162,7 +185,7 @@ private MocksRepository FindRepositoryInOps(RepositoryOperationsBase entryOps, o
{
if (entryOps.IsUsedOnAllThreads)
{
MocksRepository repo = entryOps.FindRepositoryFromAnyThread(entryKey);
MocksRepository repo = entryOps.FindRepositoryFromAnyThread(entryKey);
if (repo != null)
{
if (repo.IsRetired)
Expand Down Expand Up @@ -211,7 +234,7 @@ protected void AddRepositoryOperations(Func<MethodBase, bool> matchesMethod, Fun
if (isInheritingContext == null)
isInheritingContext = (_, __) => false;

RepositoryOperationsBase ops = this.CreateRepositoryOperations(getKey, matchesMethod, isLeaf, isUsedOnAllThreads, isInheritingContext);
RepositoryOperationsBase ops = this.CreateRepositoryOperations(getKey, matchesMethod, isLeaf, isUsedOnAllThreads, isInheritingContext);

this.repoOperations.Add(ops);
}
Expand Down Expand Up @@ -282,10 +305,10 @@ protected void SetupStandardHierarchicalTestStructure(
break;
}

if (assemblySetupAttrs != null)
{
this.AddRepositoryOperations(assemblySetupAttrs, method => method.DeclaringType.Assembly, null, false, true);
}
if (assemblySetupAttrs != null)
{
this.AddRepositoryOperations(assemblySetupAttrs, method => method.DeclaringType.Assembly, null, false, true);
}
}

private static bool IsTypeAssignableIgnoreGenericArgs(Type typeToCheck, Type derivedType)
Expand Down
27 changes: 27 additions & 0 deletions Telerik.JustMock/Core/Context/IAsyncContextResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
JustMock Lite
Copyright © 2019 Progress Software Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System.Reflection;

namespace Telerik.JustMock.Core.Context
{
internal interface IAsyncContextResolver
{
void CaptureContext();
MethodBase GetContext();
}
}
3 changes: 3 additions & 0 deletions Telerik.JustMock/Core/Context/IMockingContextResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.
*/

using System;
using System.Reflection;

namespace Telerik.JustMock.Core.Context
{
Expand All @@ -26,5 +27,7 @@ internal interface IMockingContextResolver
bool RetireRepository();

Action<string, Exception> GetFailMethod();

MethodBase GetTestMethod();
}
}
Loading