Skip to content

Commit 5685dd6

Browse files
Merge pull request #92 from telerik/rusev/async-context
Rusev/async context
2 parents 7f1604c + 1ec0a60 commit 5685dd6

13 files changed

+293
-30
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
JustMock Lite
3+
Copyright © 2010-2015,2019 Progress Software Corporation
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
using System;
19+
using System.Reflection;
20+
21+
namespace Telerik.JustMock.Core.Context
22+
{
23+
/// <summary>
24+
/// Class for automatically resolving the context of async method calls.
25+
/// </summary>
26+
public class AsyncContextResolver
27+
{
28+
/// <summary>
29+
/// Returns the actual test method from where the async call starts.
30+
/// </summary>
31+
/// <returns>The test method</returns>
32+
public static MethodBase GetContext()
33+
{
34+
return null;
35+
}
36+
}
37+
}

Telerik.JustMock.Portable/Core/Context/MockingContextResolverBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ limitations under the License.
1818
using System;
1919
using System.Collections.Generic;
2020
using System.Linq;
21+
using System.Reflection;
2122
using System.Text;
2223

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

3738
public abstract bool RetireRepository();
3839

40+
public abstract MethodBase GetTestMethod();
41+
3942
public Action<string, Exception> GetFailMethod()
4043
{
4144
return LocalMockingContextResolver.GetFailMethod(Type.GetType(this.assertFailedExceptionTypeName));

Telerik.JustMock.Portable/Telerik.JustMock.Portable.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@
484484
<Compile Include="..\Telerik.JustMock\Core\JMDebug.cs">
485485
<Link>Core\JMDebug.cs</Link>
486486
</Compile>
487+
<Compile Include="Core\Context\AsyncContextResolver.cs" />
487488
<Compile Include="Core\Context\LocalMockingContextResolver.cs" />
488489
<Compile Include="Core\Context\MockingContextResolverBase.cs" />
489490
<Compile Include="Core\Context\VisualStudioPortableContextResolver.cs" />
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
JustMock Lite
3+
Copyright © 2019 Progress Software Corporation
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
using System.ComponentModel;
18+
using System.Reflection;
19+
20+
namespace Telerik.JustMock.Core.Context
21+
{
22+
[EditorBrowsable(EditorBrowsableState.Never)]
23+
public static class AsyncContextResolver
24+
{
25+
#if NETCORE
26+
static IAsyncContextResolver resolver = new AsyncLocalWrapper();
27+
#else
28+
static IAsyncContextResolver resolver = new CallContextWrapper();
29+
#endif
30+
public static MethodBase GetContext()
31+
{
32+
return ProfilerInterceptor.GuardInternal(() =>
33+
resolver.GetContext()
34+
);
35+
}
36+
37+
public static void CaptureContext()
38+
{
39+
ProfilerInterceptor.GuardInternal(() =>
40+
resolver.CaptureContext()
41+
);
42+
}
43+
}
44+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
JustMock Lite
3+
Copyright © 2019 Progress Software Corporation
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
#if NETCORE
19+
20+
using System;
21+
using System.Reflection;
22+
using System.Threading;
23+
using System.Threading.Tasks;
24+
25+
namespace Telerik.JustMock.Core.Context
26+
{
27+
internal class AsyncLocalWrapper : IAsyncContextResolver
28+
{
29+
static AsyncLocal<MethodBase> asyncCallPattern = new AsyncLocal<MethodBase>();
30+
public void CaptureContext()
31+
{
32+
MethodBase testMethod = MockingContext.GetTestMethod();
33+
asyncCallPattern.Value = testMethod;
34+
}
35+
36+
public MethodBase GetContext()
37+
{
38+
return asyncCallPattern.Value;
39+
}
40+
}
41+
}
42+
#endif
43+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
JustMock Lite
3+
Copyright © 2019 Progress Software Corporation
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
#if !NETCORE
19+
using System;
20+
using System.Reflection;
21+
using System.Runtime.Remoting.Messaging;
22+
23+
namespace Telerik.JustMock.Core.Context
24+
{
25+
internal class CallContextWrapper : IAsyncContextResolver
26+
{
27+
private static readonly string key = Guid.NewGuid().ToString("N");
28+
29+
public MethodBase GetContext()
30+
{
31+
MethodBase methodBase = CallContext.LogicalGetData(key) as MethodBase;
32+
return methodBase;
33+
}
34+
35+
public void CaptureContext()
36+
{
37+
MethodBase testMethod = MockingContext.GetTestMethod();
38+
SetData(testMethod);
39+
}
40+
41+
private void SetData(MethodBase methodBase)
42+
{
43+
if (methodBase != null)
44+
{
45+
CallContext.LogicalSetData(key, methodBase);
46+
}
47+
}
48+
}
49+
}
50+
#endif

Telerik.JustMock/Core/Context/HierarchicalTestFrameworkContextResolver.cs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,25 @@ public override MocksRepository ResolveRepository(UnresolvedContextBehavior unre
3939
{
4040
lock (this.repositorySync)
4141
{
42-
RepositoryOperationsBase entryOps = null;
4342
int repoIdx;
43+
RepositoryOperationsBase entryOps = null;
4444
var testMethod = FindTestMethod(out repoIdx, out entryOps);
45-
if (testMethod == null)
45+
if (testMethod == null || entryOps == null)
46+
{
4647
return null;
48+
}
4749

4850
object entryKey = entryOps.GetKey(testMethod);
49-
5051
MocksRepository repo = this.FindRepositoryInOps(entryOps, entryKey);
5152
if (repo != null)
53+
{
5254
return repo;
55+
}
56+
5357
if (unresolvedContextBehavior == UnresolvedContextBehavior.DoNotCreateNew)
58+
{
5459
return null;
60+
}
5561

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

6674
object parentKey = ops.GetKey(testMethod);
6775
if (ops.IsUsedOnAllThreads)
76+
{
6877
parentRepo = ops.FindRepositoryFromAnyThread(parentKey);
78+
}
6979
else
80+
{
7081
parentRepo = ops.FindRepository(parentKey) ?? ops.FindRepositoryToInherit(testMethod);
82+
}
7183
}
7284
}
7385

@@ -110,11 +122,7 @@ public override bool RetireRepository()
110122
}
111123
}
112124

113-
protected virtual void OnMocksRepositoryCreated(MocksRepository repo)
114-
{
115-
}
116-
117-
private MethodBase FindTestMethod(out int repoIdx, out RepositoryOperationsBase entryOps)
125+
public override MethodBase GetTestMethod()
118126
{
119127
var stackTrace = new StackTrace();
120128
var q = from method in stackTrace.EnumerateFrames()
@@ -124,11 +132,22 @@ where repoOperations.Any(repo => repo.MatchesMethod(method))
124132
var allTestMethods = q.Distinct().ToArray();
125133
if (allTestMethods.Length > 1)
126134
{
127-
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;
135+
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;
128136
DebugView.DebugTrace(message);
129137
}
130-
var testMethod = allTestMethods.FirstOrDefault();
131138

139+
MethodBase testMethod = allTestMethods.FirstOrDefault();
140+
141+
return testMethod;
142+
}
143+
144+
protected virtual void OnMocksRepositoryCreated(MocksRepository repo)
145+
{
146+
}
147+
148+
private MethodBase FindTestMethod(out int repoIdx, out RepositoryOperationsBase entryOps)
149+
{
150+
MethodBase testMethod = this.GetTestMethod();
132151
if (testMethod != null)
133152
{
134153
var disableAttr = Attribute.GetCustomAttribute(testMethod, typeof(DisableAutomaticRepositoryResetAttribute)) as DisableAutomaticRepositoryResetAttribute;
@@ -137,6 +156,10 @@ where repoOperations.Any(repo => repo.MatchesMethod(method))
137156
&& !disableAttr.AllowMocking)
138157
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.");
139158
}
159+
else
160+
{
161+
testMethod = AsyncContextResolver.GetContext();
162+
}
140163

141164
repoIdx = 0;
142165
entryOps = null;
@@ -152,7 +175,7 @@ where repoOperations.Any(repo => repo.MatchesMethod(method))
152175
}
153176
}
154177

155-
JMDebug.Assert(entryOps != null);
178+
JMDebug.Assert(entryOps != null);
156179
}
157180

158181
return testMethod;
@@ -162,7 +185,7 @@ private MocksRepository FindRepositoryInOps(RepositoryOperationsBase entryOps, o
162185
{
163186
if (entryOps.IsUsedOnAllThreads)
164187
{
165-
MocksRepository repo = entryOps.FindRepositoryFromAnyThread(entryKey);
188+
MocksRepository repo = entryOps.FindRepositoryFromAnyThread(entryKey);
166189
if (repo != null)
167190
{
168191
if (repo.IsRetired)
@@ -211,7 +234,7 @@ protected void AddRepositoryOperations(Func<MethodBase, bool> matchesMethod, Fun
211234
if (isInheritingContext == null)
212235
isInheritingContext = (_, __) => false;
213236

214-
RepositoryOperationsBase ops = this.CreateRepositoryOperations(getKey, matchesMethod, isLeaf, isUsedOnAllThreads, isInheritingContext);
237+
RepositoryOperationsBase ops = this.CreateRepositoryOperations(getKey, matchesMethod, isLeaf, isUsedOnAllThreads, isInheritingContext);
215238

216239
this.repoOperations.Add(ops);
217240
}
@@ -282,10 +305,10 @@ protected void SetupStandardHierarchicalTestStructure(
282305
break;
283306
}
284307

285-
if (assemblySetupAttrs != null)
286-
{
287-
this.AddRepositoryOperations(assemblySetupAttrs, method => method.DeclaringType.Assembly, null, false, true);
288-
}
308+
if (assemblySetupAttrs != null)
309+
{
310+
this.AddRepositoryOperations(assemblySetupAttrs, method => method.DeclaringType.Assembly, null, false, true);
311+
}
289312
}
290313

291314
private static bool IsTypeAssignableIgnoreGenericArgs(Type typeToCheck, Type derivedType)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
JustMock Lite
3+
Copyright © 2019 Progress Software Corporation
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
using System.Reflection;
19+
20+
namespace Telerik.JustMock.Core.Context
21+
{
22+
internal interface IAsyncContextResolver
23+
{
24+
void CaptureContext();
25+
MethodBase GetContext();
26+
}
27+
}

Telerik.JustMock/Core/Context/IMockingContextResolver.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616
*/
1717

1818
using System;
19+
using System.Reflection;
1920

2021
namespace Telerik.JustMock.Core.Context
2122
{
@@ -26,5 +27,7 @@ internal interface IMockingContextResolver
2627
bool RetireRepository();
2728

2829
Action<string, Exception> GetFailMethod();
30+
31+
MethodBase GetTestMethod();
2932
}
3033
}

0 commit comments

Comments
 (0)