Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #20 - adds unit tests for CatalystInstance #25

Closed
wants to merge 1 commit into from
Closed
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
190 changes: 190 additions & 0 deletions ReactWindows/ReactNative.Tests/Bridge/CatalystInstanceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Newtonsoft.Json.Linq;
using ReactNative.Bridge;
using ReactNative.Bridge.Queue;
using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.System.Threading;

namespace ReactNative.Tests.Bridge
{
[TestClass]
public class CatalystInstanceTests
{
[TestMethod]
public async Task CatalystInstance_GetModules()
{
var module = new TestNativeModule();

var registry = new NativeModuleRegistry.Builder()
.Add(module)
.Build();

var jsConfig = new JavaScriptModulesConfig.Builder()
.Add<TestJavaScriptModule>()
.Build();

var executor = new MockJavaScriptExecutor((p0, p1, p2) => JValue.CreateNull());
var builder = new CatalystInstance.Builder()
{
QueueConfigurationSpec = CatalystQueueConfigurationSpec.Default,
Registry = registry,
JavaScriptModulesConfig = jsConfig,
JavaScriptExecutor = executor,
NativeModuleCallExceptionHandler = _ => { }
};

var instance = await DispatcherHelpers.CallOnDispatcherAsync(() => builder.Build());

var actualModule = instance.GetNativeModule<TestNativeModule>();
Assert.AreSame(module, actualModule);

var firstJSModule = instance.GetJavaScriptModule<TestJavaScriptModule>();
var secondJSModule = instance.GetJavaScriptModule<TestJavaScriptModule>();
Assert.AreSame(firstJSModule, secondJSModule);
}

[TestMethod]
public async Task CatalystInstance_Initialize_Dispose()
{
var module = new TestNativeModule();

var registry = new NativeModuleRegistry.Builder()
.Add(module)
.Build();

var jsConfig = new JavaScriptModulesConfig.Builder().Build();

var executor = new MockJavaScriptExecutor((p0, p1, p2) => JValue.CreateNull());
var builder = new CatalystInstance.Builder()
{
QueueConfigurationSpec = CatalystQueueConfigurationSpec.Default,
Registry = registry,
JavaScriptModulesConfig = jsConfig,
JavaScriptExecutor = executor,
NativeModuleCallExceptionHandler = _ => { },
};

var instance = await DispatcherHelpers.CallOnDispatcherAsync(() => builder.Build());
await DispatcherHelpers.RunOnDispatcherAsync(() => instance.Initialize());
await AssertEx.ThrowsAsync<InvalidOperationException>(() =>
DispatcherHelpers.RunOnDispatcherAsync(() => instance.Initialize()));

Assert.AreEqual(1, module.InitializeCalls);

await DispatcherHelpers.RunOnDispatcherAsync(() => instance.Dispose());
Assert.AreEqual(1, module.OnCatalystInstanceDisposeCalls);

// Dispose is idempotent
await DispatcherHelpers.RunOnDispatcherAsync(() => instance.Dispose());
Assert.AreEqual(1, module.OnCatalystInstanceDisposeCalls);

Assert.IsTrue(instance.IsDisposed);
}

[TestMethod]
public async Task CatalystInstance_ExceptionHandled_Disposes()
{
var eventHandler = new AutoResetEvent(false);
var module = new OnDisposeNativeModule(() => eventHandler.Set());
var registry = new NativeModuleRegistry.Builder()
.Add(module)
.Build();

var jsConfig = new JavaScriptModulesConfig.Builder().Build();
var executor = new MockJavaScriptExecutor((p0, p1, p2) => JValue.CreateNull());

var exception = new Exception();
var tcs = new TaskCompletionSource<Exception>();
var handler = new Action<Exception>(ex =>
{
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
ThreadPool.RunAsync(_ => tcs.SetResult(ex));
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
});

var builder = new CatalystInstance.Builder()
{
QueueConfigurationSpec = CatalystQueueConfigurationSpec.Default,
Registry = registry,
JavaScriptModulesConfig = jsConfig,
JavaScriptExecutor = executor,
NativeModuleCallExceptionHandler = handler,
};

var instance = await DispatcherHelpers.CallOnDispatcherAsync(() => builder.Build());
instance.QueueConfiguration.JSQueueThread.RunOnQueue(() =>
{
throw exception;
});

var actualException = await tcs.Task;
Assert.AreSame(exception, actualException);

Assert.IsTrue(eventHandler.WaitOne());
Assert.IsTrue(instance.IsDisposed);
}


class TestNativeModule : NativeModuleBase
{
public int InitializeCalls
{
get;
set;
}

public int OnCatalystInstanceDisposeCalls
{
get;
set;
}

public override string Name
{
get
{
return "Test";
}
}

public override void Initialize()
{
InitializeCalls++;
}

public override void OnCatalystInstanceDispose()
{
OnCatalystInstanceDisposeCalls++;
}
}

class OnDisposeNativeModule : NativeModuleBase
{
private readonly Action _onDispose;

public OnDisposeNativeModule(Action onDispose)
{
_onDispose = onDispose;
}

public override string Name
{
get
{
return "Test";
}
}

public override void OnCatalystInstanceDispose()
{
_onDispose();
}
}

class TestJavaScriptModule : JavaScriptModuleBase
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ public void MessageQueueThread_CreateUiThread_ThrowsNotSupported()
public async Task MessageQueueThread_IsOnThread()
{
var thrown = 0;
var uiThread = default(IMessageQueueThread);
await RunOnDispatcherAsync(() => uiThread = MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => thrown++));
var uiThread = await CallOnDispatcherAsync(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => thrown++));
var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), ex => thrown++);
var taskPoolThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), ex => thrown++);

Expand Down Expand Up @@ -69,8 +68,7 @@ public async Task MessageQueueThread_HandlesException()
countdown.Signal();
});

var uiThread = default(IMessageQueueThread);
await RunOnDispatcherAsync(() => uiThread = MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, handler));
var uiThread = await CallOnDispatcherAsync(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, handler));
var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), handler);
var taskPoolThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), handler);

Expand All @@ -92,8 +90,7 @@ public async Task MessageQueueThread_HandlesException()
[TestMethod]
public async Task MessageQueueThread_OneAtATime()
{
var uiThread = default(IMessageQueueThread);
await RunOnDispatcherAsync(() => uiThread = MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => { Assert.Fail(); }));
var uiThread = await CallOnDispatcherAsync(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => { Assert.Fail(); }));
var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), ex => { Assert.Fail(); });
var taskPoolThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), ex => { Assert.Fail(); });

Expand Down
23 changes: 23 additions & 0 deletions ReactWindows/ReactNative.Tests/Internal/AssertEx.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using System;
using System.Threading.Tasks;

namespace ReactNative.Tests
{
Expand All @@ -26,5 +27,27 @@ public static void Throws<T>(Action action, Action<T> assert)

Assert.Fail("Excepted exception of type '{0}'.", typeof(T));
}

public static Task ThrowsAsync<T>(Func<Task> action)
where T : Exception
{
return ThrowsAsync<T>(action, _ => { });
}

public static async Task ThrowsAsync<T>(Func<Task> action, Action<T> assert)
where T : Exception
{
try
{
await action();
}
catch (T ex)
{
assert(ex);
return;
}

Assert.Fail("Excepted exception of type '{0}'.", typeof(T));
}
}
}
16 changes: 16 additions & 0 deletions ReactWindows/ReactNative.Tests/Internal/DispatcherHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using Windows.System.Threading;
using Windows.UI.Core;

namespace ReactNative.Tests
Expand All @@ -10,5 +11,20 @@ public static async Task RunOnDispatcherAsync(Action action)
{
await App.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(action));
}

public static async Task<T> CallOnDispatcherAsync<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();

await RunOnDispatcherAsync(() =>
{
var result = func();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
ThreadPool.RunAsync(_ => tcs.SetResult(result));
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
});

return await tcs.Task;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ public T GetNativeModule<T>() where T : INativeModule
throw new NotImplementedException();
}

public Task InitializeAsync()
public T GetJavaScriptModule<T>() where T : IJavaScriptModule
{
return Task.FromResult(true);
throw new NotImplementedException();
}

public void Initialize()
{
throw new NotImplementedException();
}

public void InvokeCallback(int callbackId, JArray arguments)
Expand Down
45 changes: 45 additions & 0 deletions ReactWindows/ReactNative.Tests/Internal/MockJavaScriptExecutor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using Newtonsoft.Json.Linq;
using ReactNative.Bridge;

namespace ReactNative.Tests
{
class MockJavaScriptExecutor : IJavaScriptExecutor
{
private readonly Func<string, string, JArray, JToken> _onCall;
private readonly Action<string, JToken> _onSetGlobalVariable;
private readonly Action _onDispose;

public MockJavaScriptExecutor(
Func<string, string, JArray, JToken> onCall)
: this(onCall, (_, __) => { }, () => { })
{
}

public MockJavaScriptExecutor(
Func<string, string, JArray, JToken> onCall,
Action<string, JToken> onSetGlobalVariable,
Action onDispose)
{
_onCall = onCall;
_onSetGlobalVariable = onSetGlobalVariable;
_onDispose = onDispose;
}

public JToken Call(string moduleName, string methodName, JArray arguments)
{
return _onCall(moduleName, methodName, arguments);
}

public void SetGlobalVariable(string propertyName, JToken value)
{
_onSetGlobalVariable(propertyName, value);
}

public void Dispose()
{
_onDispose();
}

}
}
2 changes: 2 additions & 0 deletions ReactWindows/ReactNative.Tests/ReactNative.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
<SDKReference Include="TestPlatform.Universal, Version=$(UnitTestPlatformVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="Bridge\CatalystInstanceTests.cs" />
<Compile Include="Bridge\JavaScriptModuleBaseTests.cs" />
<Compile Include="Bridge\JavaScriptModuleRegistryTests.cs" />
<Compile Include="Bridge\JavaScriptModulesConfigTests.cs" />
Expand All @@ -108,6 +109,7 @@
<Compile Include="Internal\DispatcherHelpers.cs" />
<Compile Include="Internal\MockCatalystInstance.cs" />
<Compile Include="Internal\MockInvocationHandler.cs" />
<Compile Include="Internal\MockJavaScriptExecutor.cs" />
<Compile Include="Modules\Core\RCTNativeAppEventEmitterTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UIManager\AppRegistryTests.cs" />
Expand Down
Loading