Skip to content

Commit c21c76c

Browse files
kevinvangelderrozele
authored andcommitted
feat(Portable): Migrate Timing & Dependencies (microsoft#755)
* migrated timing to shared project * minor tweaks to improve (but not fix) tests * fixed UWP build error * fixed migrated tests * migrated ReactContextNativeModuleBase tests * migrated ReactContextNativeModuleBase tests * implemented @rozele and @matthargett test fixes * cleanup for PR * fix IsOnThreadCore check * simplify OnDispatch watcher
1 parent 6f244af commit c21c76c

18 files changed

+366
-29
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
using NMock;
2+
using NUnit.Framework;
3+
using ReactNative.Bridge.Queue;
4+
using System;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using System.Windows;
8+
using static ReactNative.Tests.DispatcherHelpers;
9+
10+
namespace ReactNative.Tests.Bridge.Queue
11+
{
12+
[TestFixture]
13+
public class MessageQueueThreadTests
14+
{
15+
[Test]
16+
public void MessageQueueThread_ArgumentChecks()
17+
{
18+
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(
19+
() => { MessageQueueThread.Create(null, ex2 => { }); }
20+
);
21+
Assert.AreEqual("spec", ex.ParamName);
22+
23+
ArgumentNullException ex3 = Assert.Throws<ArgumentNullException>(
24+
() => { MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, null); }
25+
);
26+
Assert.AreEqual("handler", ex3.ParamName);
27+
}
28+
29+
[Test]
30+
public void MessageQueueThread_CreateUiThread_ThrowsNotSupported()
31+
{
32+
Assert.Throws<NotSupportedException>(
33+
() => { MessageQueueThreadSpec.Create("ui", MessageQueueThreadKind.DispatcherThread); }
34+
);
35+
}
36+
37+
[SetUp]
38+
public void SetUp()
39+
{
40+
var waitForApplicationRun = new ManualResetEventSlim();
41+
Task.Run(() =>
42+
{
43+
var app = new Application();
44+
app.Startup += (s, e) => { waitForApplicationRun.Set(); };
45+
app.Run();
46+
});
47+
}
48+
49+
[Test]
50+
public async Task MessageQueueThread_IsOnThread()
51+
{
52+
var thrown = 0;
53+
var uiThread = await CallOnDispatcherAsync(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => thrown++));
54+
var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), ex => thrown++);
55+
var taskPoolThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), ex => thrown++);
56+
57+
var queueThreads = new[]
58+
{
59+
uiThread,
60+
backgroundThread,
61+
taskPoolThread
62+
};
63+
64+
var countdown = new CountdownEvent(queueThreads.Length);
65+
foreach (var queueThread in queueThreads)
66+
{
67+
queueThread.RunOnQueue(() =>
68+
{
69+
Assert.IsTrue(queueThread.IsOnThread());
70+
countdown.Signal();
71+
});
72+
}
73+
74+
Assert.IsTrue(countdown.Wait(5000));
75+
Assert.AreEqual(0, thrown);
76+
}
77+
78+
[Test]
79+
public async Task MessageQueueThread_HandlesException()
80+
{
81+
var exception = new Exception();
82+
var countdown = new CountdownEvent(1);
83+
var handler = new Action<Exception>(ex =>
84+
{
85+
Assert.AreSame(exception, ex);
86+
countdown.Signal();
87+
});
88+
89+
var uiThread = await CallOnDispatcherAsync(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, handler));
90+
var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), handler);
91+
var taskPoolThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), handler);
92+
93+
var queueThreads = new[]
94+
{
95+
uiThread,
96+
backgroundThread,
97+
taskPoolThread
98+
};
99+
100+
countdown.AddCount(queueThreads.Length - 1);
101+
102+
foreach (var queueThread in queueThreads)
103+
{
104+
queueThread.RunOnQueue(() => { throw exception; });
105+
}
106+
107+
Assert.IsTrue(countdown.Wait(5000));
108+
}
109+
110+
[Test]
111+
public async Task MessageQueueThread_OneAtATime()
112+
{
113+
var uiThread = await CallOnDispatcherAsync(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => { Assert.Fail(); }));
114+
var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), ex => { Assert.Fail(); });
115+
var taskPoolThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), ex => { Assert.Fail(); });
116+
117+
var enter = new AutoResetEvent(false);
118+
var exit = new AutoResetEvent(false);
119+
120+
var queueThreads = new[]
121+
{
122+
uiThread,
123+
backgroundThread,
124+
taskPoolThread
125+
};
126+
127+
foreach (var queueThread in queueThreads)
128+
{
129+
var count = 10;
130+
for (var i = 0; i < count; ++i)
131+
{
132+
queueThread.RunOnQueue(() => { enter.Set(); exit.WaitOne(); });
133+
}
134+
135+
for (var i = 0; i < count; ++i)
136+
{
137+
Assert.IsTrue(enter.WaitOne());
138+
Assert.IsFalse(enter.WaitOne(100));
139+
exit.Set();
140+
}
141+
}
142+
}
143+
144+
[Test]
145+
public async Task MessageQueueThread_Dispose()
146+
{
147+
var uiThread = await CallOnDispatcherAsync(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => { Assert.Fail(); }));
148+
var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), ex => { Assert.Fail(); });
149+
var taskPoolThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), ex => { Assert.Fail(); });
150+
151+
var queueThreads = new[]
152+
{
153+
uiThread,
154+
backgroundThread,
155+
taskPoolThread
156+
};
157+
158+
var waitHandle = new AutoResetEvent(false);
159+
160+
foreach (var queueThread in queueThreads)
161+
{
162+
queueThread.Dispose();
163+
queueThread.RunOnQueue(() =>
164+
{
165+
waitHandle.Set();
166+
});
167+
168+
Assert.IsFalse(waitHandle.WaitOne(500));
169+
}
170+
Assert.True(true);
171+
}
172+
173+
[TearDown]
174+
public void TearDown()
175+
{
176+
var waitForApplicationExit = new ManualResetEventSlim();
177+
Task.Run(() =>
178+
{
179+
Application.Current.Exit += (s, e) => { waitForApplicationExit.Set(); };
180+
Application.Current.Shutdown();
181+
});
182+
}
183+
}
184+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using System.Windows.Threading;
5+
using System.Windows;
6+
7+
namespace ReactNative.Tests
8+
{
9+
static class DispatcherHelpers
10+
{
11+
public static async Task RunOnDispatcherAsync(Action action)
12+
{
13+
Dispatcher dispatcher = Application.Current != null
14+
? Application.Current.Dispatcher
15+
: Dispatcher.CurrentDispatcher;
16+
if (Thread.CurrentThread == Dispatcher.CurrentDispatcher.Thread)
17+
{
18+
dispatcher.Invoke(action);
19+
}
20+
else
21+
{
22+
await dispatcher.InvokeAsync(action).Task.ConfigureAwait(false);
23+
}
24+
}
25+
26+
public static async Task<T> CallOnDispatcherAsync<T>(Func<T> func)
27+
{
28+
var tcs = new TaskCompletionSource<T>();
29+
30+
await RunOnDispatcherAsync(() =>
31+
{
32+
var result = func();
33+
34+
Task.Run(() => tcs.SetResult(result));
35+
}).ConfigureAwait(false);
36+
37+
return await tcs.Task.ConfigureAwait(false);
38+
}
39+
40+
public static async Task CallOnDispatcherAsync(Func<Task> asyncFunc)
41+
{
42+
var tcs = new TaskCompletionSource<bool>();
43+
44+
await RunOnDispatcherAsync(async () =>
45+
{
46+
await asyncFunc();
47+
await Task.Run(() => tcs.SetResult(true));
48+
}).ConfigureAwait(false);
49+
50+
await tcs.Task.ConfigureAwait(false);
51+
}
52+
}
53+
}

ReactWindows/ReactNative.Net46.Tests/ReactNative.Net46.Tests.csproj

+12-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<DebugSymbols>true</DebugSymbols>
1414
<DebugType>full</DebugType>
1515
<Optimize>false</Optimize>
16-
<DefineConstants>DEBUG;</DefineConstants>
16+
<DefineConstants>DEBUG;NET46</DefineConstants>
1717
<ErrorReport>prompt</ErrorReport>
1818
<WarningLevel>4</WarningLevel>
1919
<ConsolePause>false</ConsolePause>
@@ -76,6 +76,8 @@
7676
<PlatformTarget>ARM</PlatformTarget>
7777
</PropertyGroup>
7878
<ItemGroup>
79+
<Reference Include="PresentationCore" />
80+
<Reference Include="PresentationFramework" />
7981
<Reference Include="System" />
8082
<Reference Include="nunit.framework">
8183
<HintPath>..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll</HintPath>
@@ -107,8 +109,16 @@
107109
<Reference Include="Newtonsoft.Json">
108110
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
109111
</Reference>
112+
<Reference Include="System.Windows.Presentation" />
113+
<Reference Include="System.Xaml" />
114+
<Reference Include="WindowsBase">
115+
<HintPath>..\packages\WindowsBase.4.6.1055.0\lib\WindowsBase.dll</HintPath>
116+
<Private>True</Private>
117+
</Reference>
110118
</ItemGroup>
111119
<ItemGroup>
120+
<Compile Include="Bridge\Queue\MessageQueueThreadTests.cs" />
121+
<Compile Include="Internal\DispatcherHelpers.cs" />
112122
<Compile Include="Properties\AssemblyInfo.cs" />
113123
</ItemGroup>
114124
<ItemGroup>
@@ -123,6 +133,7 @@
123133
<Name>ReactNative.Net46</Name>
124134
</ProjectReference>
125135
</ItemGroup>
136+
<ItemGroup />
126137
<Import Project="..\ReactNative.Shared.Tests\ReactNative.Shared.Tests.projitems" Label="Shared" />
127138
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
128139
</Project>

ReactWindows/ReactNative.Net46.Tests/packages.config

+1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
<package id="NMock3" version="3.5.44" targetFramework="net46" />
55
<package id="NUnit" version="3.4.1" targetFramework="net46" />
66
<package id="NUnit3TestAdapter" version="3.4.1" targetFramework="net46" />
7+
<package id="WindowsBase" version="4.6.1055.0" targetFramework="net46" />
78
</packages>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using System.Windows.Threading;
5+
6+
namespace ReactNative.Bridge
7+
{
8+
static class DispatcherHelpers
9+
{
10+
public static void AssertOnDispatcher()
11+
{
12+
if (!IsOnDispatcher())
13+
{
14+
throw new InvalidOperationException("Thread does not have dispatcher access.");
15+
}
16+
}
17+
18+
public static bool IsOnDispatcher()
19+
{
20+
return Thread.CurrentThread == Dispatcher.CurrentDispatcher.Thread;
21+
}
22+
23+
public static async void RunOnDispatcher(Action action)
24+
{
25+
await Dispatcher.CurrentDispatcher.InvokeAsync(action).Task.ConfigureAwait(false);
26+
}
27+
28+
public static Task<T> CallOnDispatcher<T>(Func<T> func)
29+
{
30+
var taskCompletionSource = new TaskCompletionSource<T>();
31+
32+
RunOnDispatcher(() =>
33+
{
34+
var result = func();
35+
36+
// TaskCompletionSource<T>.SetResult can call continuations
37+
// on the awaiter of the task completion source.
38+
Task.Run(() => taskCompletionSource.SetResult(result));
39+
});
40+
41+
return taskCompletionSource.Task;
42+
}
43+
}
44+
}

ReactWindows/ReactNative.Net46/ReactNative.Net46.csproj

+27-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
1616
<PlatformTarget>x86</PlatformTarget>
1717
<OutputPath>bin\x86\Debug\</OutputPath>
18-
<DefineConstants>DEBUG</DefineConstants>
18+
<DefineConstants>DEBUG;NET46</DefineConstants>
1919
<DocumentationFile>bin\x86\Debug\ReactNative.Net46.XML</DocumentationFile>
2020
</PropertyGroup>
2121
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
@@ -56,16 +56,42 @@
5656
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
5757
<Private>True</Private>
5858
</Reference>
59+
<Reference Include="PresentationCore" />
5960
<Reference Include="System" />
6061
<Reference Include="System.Core" />
62+
<Reference Include="System.Reactive.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
63+
<HintPath>..\packages\System.Reactive.Core.3.0.0\lib\net46\System.Reactive.Core.dll</HintPath>
64+
<Private>True</Private>
65+
</Reference>
66+
<Reference Include="System.Reactive.Interfaces, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
67+
<HintPath>..\packages\System.Reactive.Interfaces.3.0.0\lib\net45\System.Reactive.Interfaces.dll</HintPath>
68+
<Private>True</Private>
69+
</Reference>
70+
<Reference Include="System.Reactive.Linq, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
71+
<HintPath>..\packages\System.Reactive.Linq.3.0.0\lib\net46\System.Reactive.Linq.dll</HintPath>
72+
<Private>True</Private>
73+
</Reference>
74+
<Reference Include="System.Reactive.PlatformServices, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
75+
<HintPath>..\packages\System.Reactive.PlatformServices.3.0.0\lib\net46\System.Reactive.PlatformServices.dll</HintPath>
76+
<Private>True</Private>
77+
</Reference>
78+
<Reference Include="System.Reactive.Windows.Threading, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
79+
<HintPath>..\packages\System.Reactive.Windows.Threading.3.0.0\lib\net45\System.Reactive.Windows.Threading.dll</HintPath>
80+
<Private>True</Private>
81+
</Reference>
6182
<Reference Include="System.Xml.Linq" />
6283
<Reference Include="System.Data.DataSetExtensions" />
6384
<Reference Include="Microsoft.CSharp" />
6485
<Reference Include="System.Data" />
6586
<Reference Include="System.Net.Http" />
6687
<Reference Include="System.Xml" />
88+
<Reference Include="WindowsBase">
89+
<HintPath>..\packages\WindowsBase.4.6.1055.0\lib\WindowsBase.dll</HintPath>
90+
<Private>True</Private>
91+
</Reference>
6792
</ItemGroup>
6893
<ItemGroup>
94+
<Compile Include="Bridge\DispatcherHelpers.cs" />
6995
<Compile Include="Properties\AssemblyInfo.cs" />
7096
<Compile Include="Tracing\NullLoggingActivityBuilder.cs" />
7197
<Compile Include="Tracing\Tracer.cs" />

0 commit comments

Comments
 (0)