Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Core] Missing await in Device.InvokeOnMainThreadAsync(Func<Task>) #6718

Merged
merged 6 commits into from
Jul 16, 2019
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
145 changes: 145 additions & 0 deletions Xamarin.Forms.Core.UnitTests/DeviceUnitTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System;
using System.Threading.Tasks;
using NUnit.Framework;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms.Core.UnitTests
{
[TestFixture]
public class DeviceUnitTests : BaseTestFixture
{
[Test]
public void TestBeginInvokeOnMainThread()
{
bool calledFromMainThread = false;
Device.PlatformServices = MockPlatformServices(() => calledFromMainThread = true);

bool invoked = false;
Device.BeginInvokeOnMainThread(() => invoked = true);

Assert.True(invoked, "Action not invoked.");
Assert.True(calledFromMainThread, "Action not invoked from main thread.");
}

[Test]
public async Task TestInvokeOnMainThreadWithSyncFunc()
{
bool calledFromMainThread = false;
Device.PlatformServices = MockPlatformServices(() => calledFromMainThread = true);

bool invoked = false;
var result = await Device.InvokeOnMainThreadAsync(() => { invoked = true; return true; });

Assert.True(invoked, "Action not invoked.");
Assert.True(calledFromMainThread, "Action not invoked from main thread.");
Assert.True(result, "Unexpected result.");
}

[Test]
public async Task TestInvokeOnMainThreadWithSyncAction()
{
bool calledFromMainThread = false;
Device.PlatformServices = MockPlatformServices(() => calledFromMainThread = true);

bool invoked = false;
await Device.InvokeOnMainThreadAsync(() => { invoked = true; });

Assert.True(invoked, "Action not invoked.");
Assert.True(calledFromMainThread, "Action not invoked from main thread.");
}

[Test]
public async Task TestInvokeOnMainThreadWithAsyncFunc()
{
bool calledFromMainThread = false;
Device.PlatformServices = MockPlatformServices(() => calledFromMainThread = true,
invokeOnMainThread: action => Task.Delay(50).ContinueWith(_ => action()));

bool invoked = false;
var task = Device.InvokeOnMainThreadAsync(async () => { invoked = true; return true; });
Assert.True(calledFromMainThread, "Action not invoked from main thread.");
Assert.False(invoked, "Action invoked early.");

var result = await task;
Assert.True(invoked, "Action not invoked.");
Assert.True(result, "Unexpected result.");
}

[Test]
public async Task TestInvokeOnMainThreadWithAsyncFuncError()
{
bool calledFromMainThread = false;
Device.PlatformServices = MockPlatformServices(() => calledFromMainThread = true,
invokeOnMainThread: action => Task.Delay(50).ContinueWith(_ => action()));

bool invoked = false;
async Task<bool> boom() { invoked = true; throw new ApplicationException(); }
var task = Device.InvokeOnMainThreadAsync(boom);
Assert.True(calledFromMainThread, "Action not invoked from main thread.");
Assert.False(invoked, "Action invoked early.");

var aggregateEx = Assert.Throws<AggregateException>(() => task.Wait(100));
Assert.IsInstanceOf<ApplicationException>(aggregateEx.InnerException);
Assert.True(invoked, "Action not invoked.");
}

[Test]
public async Task TestInvokeOnMainThreadWithAsyncAction()
{
bool calledFromMainThread = false;
Device.PlatformServices = MockPlatformServices(() => calledFromMainThread = true,
invokeOnMainThread: action => Task.Delay(50).ContinueWith(_ => action()));

bool invoked = false;
var task = Device.InvokeOnMainThreadAsync(async () => { invoked = true; });
Assert.True(calledFromMainThread, "Action not invoked from main thread.");
Assert.False(invoked, "Action invoked early.");

await task;
Assert.True(invoked, "Action not invoked.");
}

[Test]
public async Task TestInvokeOnMainThreadWithAsyncActionError()
{
bool calledFromMainThread = false;
Device.PlatformServices = MockPlatformServices(() => calledFromMainThread = true,
invokeOnMainThread: action => Task.Delay(50).ContinueWith(_ => action()));

bool invoked = false;
async Task boom() { invoked = true; throw new ApplicationException(); }
var task = Device.InvokeOnMainThreadAsync(boom);
Assert.True(calledFromMainThread, "Action not invoked from main thread.");
Assert.False(invoked, "Action invoked early.");

var aggregateEx = Assert.Throws<AggregateException>(() => task.Wait(100));
Assert.IsInstanceOf<ApplicationException>(aggregateEx.InnerException);
Assert.True(invoked, "Action not invoked.");
}

[Test]
public void InvokeOnMainThreadThrowsWhenNull()
{
Device.PlatformServices = null;
Assert.Throws<InvalidOperationException>(() => Device.BeginInvokeOnMainThread(() => { }));
Assert.Throws<InvalidOperationException>(() => Device.InvokeOnMainThreadAsync(() => { }).Wait(100));
Assert.Throws<InvalidOperationException>(() => Device.InvokeOnMainThreadAsync(() => true).Wait(100));
Assert.Throws<InvalidOperationException>(() => Device.InvokeOnMainThreadAsync(async () => { }).Wait(100));
Assert.Throws<InvalidOperationException>(() => Device.InvokeOnMainThreadAsync(async () => true).Wait(100));
}

private IPlatformServices MockPlatformServices(Action onInvokeOnMainThread, Action<Action> invokeOnMainThread = null)
{
return new MockPlatformServices(
invokeOnMainThread: action =>
{
onInvokeOnMainThread();

if (invokeOnMainThread == null)
action();
else
invokeOnMainThread(action);
});
}
}
}
18 changes: 0 additions & 18 deletions Xamarin.Forms.Core.UnitTests/ViewUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -563,24 +563,6 @@ public void TestUnFocusedEvent ()
Assert.True (fired);
}

[Test]
public void TestBeginInvokeOnMainThread ()
{
Device.PlatformServices = new MockPlatformServices (invokeOnMainThread: action => action ());

bool invoked = false;
Device.BeginInvokeOnMainThread (() => invoked = true);

Assert.True (invoked);
}

[Test]
public void InvokeOnMainThreadThrowsWhenNull ()
{
Device.PlatformServices = null;
Assert.Throws<InvalidOperationException>(() => Device.BeginInvokeOnMainThread (() => { }));
}

[Test]
public void TestOpenUriAction ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<Compile Include="CommandSourceTests.cs" />
<Compile Include="CommandTests.cs" />
<Compile Include="DependencyResolutionTests.cs" />
<Compile Include="DeviceUnitTests.cs" />
<Compile Include="EffectiveFlowDirectionExtensions.cs" />
<Compile Include="ShellTestBase.cs" />
<Compile Include="ShellUriHandlerTests.cs" />
Expand Down
8 changes: 4 additions & 4 deletions Xamarin.Forms.Core/Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ public static Task<T> InvokeOnMainThreadAsync<T>(Func<T> func)

public static Task InvokeOnMainThreadAsync(Action action)
{
Func<object> dummyFunc = () => { action(); return null; };
return InvokeOnMainThreadAsync(dummyFunc);
string wrapAction() { action(); return null; }
return InvokeOnMainThreadAsync(wrapAction);
}

public static Task<T> InvokeOnMainThreadAsync<T>(Func<Task<T>> funcTask)
Expand All @@ -151,8 +151,8 @@ public static Task<T> InvokeOnMainThreadAsync<T>(Func<Task<T>> funcTask)

public static Task InvokeOnMainThreadAsync(Func<Task> funcTask)
{
Func<Task<object>> dummyFunc = async () => { await funcTask().ConfigureAwait(false); return null; };
return InvokeOnMainThreadAsync(dummyFunc);
async Task<object> wrapFunction() { await funcTask().ConfigureAwait(false); return null; }
return InvokeOnMainThreadAsync(wrapFunction);
}

public static async Task<SynchronizationContext> GetMainThreadSynchronizationContextAsync()
Expand Down