-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
191 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
|
||
namespace Microsoft.Maui.Handlers.Memory | ||
{ | ||
public class MemoryTestFixture : IDisposable | ||
{ | ||
Dictionary<Type, (WeakReference handler, WeakReference view)> _handlers | ||
= new Dictionary<Type, (WeakReference handler, WeakReference view)>(); | ||
|
||
public MemoryTestFixture() | ||
{ | ||
} | ||
|
||
public void AddReferences(Type handlerType, (WeakReference handler, WeakReference view) value) => | ||
_handlers.Add(handlerType, value); | ||
|
||
public bool HasType(Type handlerType) => _handlers.ContainsKey(handlerType); | ||
|
||
public bool DoReferencesStillExist(Type handlerType) | ||
{ | ||
WeakReference weakHandler; | ||
WeakReference weakView; | ||
(weakHandler, weakView) = _handlers[handlerType]; | ||
|
||
|
||
if (weakHandler.Target != null || | ||
weakHandler.IsAlive || | ||
weakView.Target != null || | ||
weakView.IsAlive) | ||
{ | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_handlers.Clear(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Xunit.Abstractions; | ||
using Xunit.Sdk; | ||
|
||
|
||
namespace Microsoft.Maui.Handlers.Memory | ||
{ | ||
public class MemoryTestOrdering : ITestCaseOrderer | ||
{ | ||
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) | ||
where TTestCase : ITestCase | ||
{ | ||
var result = testCases.ToList(); | ||
|
||
|
||
if (result.Count > 2) | ||
throw new InvalidOperationException("Add new test to sort if you want it to run"); | ||
|
||
return new List<TTestCase>() | ||
{ | ||
result.First(x=> x.TestMethod.Method.Name == nameof(MemoryTests.Allocate)), | ||
result.First(x=> x.TestMethod.Method.Name == nameof(MemoryTests.CheckAllocation)), | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using Microsoft.Maui.DeviceTests.Stubs; | ||
|
||
|
||
namespace Microsoft.Maui.Handlers.Memory | ||
{ | ||
public class MemoryTestTypes : IEnumerable<object[]> | ||
{ | ||
public IEnumerator<object[]> GetEnumerator() | ||
{ | ||
yield return new object[] { (typeof(DatePickerStub), typeof(DatePickerHandler)) }; | ||
yield return new object[] { (typeof(EditorStub), typeof(EditorHandler)) }; | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Maui.DeviceTests; | ||
using Microsoft.Maui.DeviceTests.Stubs; | ||
using Microsoft.Maui.Graphics; | ||
using Microsoft.Maui.Media; | ||
using Xunit; | ||
using Xunit.Abstractions; | ||
using Xunit.Sdk; | ||
|
||
|
||
namespace Microsoft.Maui.Handlers.Memory | ||
{ | ||
/// <summary> | ||
/// Trying to allocate and then rely on the GC to collect within the same test was a bit unreliable when running | ||
/// tests from the xHarness CLI. | ||
/// For example, if you call Thread.Sleep it will block the entire test run from moving forward whereas locally | ||
/// it's able to still run in parallel. So, I broke the allocate and check allocate into two steps | ||
/// which seems to make Android and WinUI happier. Low APIs on Android still have issues running via xHarness | ||
/// which is why we only currently run these on API 30+ | ||
/// </summary> | ||
[TestCaseOrderer("Microsoft.Maui.Handlers.Memory.MemoryTestOrdering", "Microsoft.Maui.Core.DeviceTests")] | ||
public class MemoryTests : HandlerTestBase, IClassFixture<MemoryTestFixture> | ||
{ | ||
MemoryTestFixture _fixture; | ||
public MemoryTests(MemoryTestFixture fixture) | ||
{ | ||
_fixture = fixture; | ||
} | ||
|
||
[Theory] | ||
[ClassData(typeof(MemoryTestTypes))] | ||
public async Task Allocate((Type ViewType, Type HandlerType) data) | ||
{ | ||
if (!OperatingSystem.IsAndroidVersionAtLeast(30)) | ||
return; | ||
|
||
var handler = await InvokeOnMainThreadAsync(() => CreateHandler((IElement)Activator.CreateInstance(data.ViewType), data.HandlerType)); | ||
WeakReference weakHandler = new WeakReference(handler); | ||
_fixture.AddReferences(data.HandlerType, (weakHandler, new WeakReference(handler.VirtualView))); | ||
handler = null; | ||
|
||
GC.Collect(); | ||
GC.WaitForPendingFinalizers(); | ||
GC.Collect(); | ||
GC.WaitForPendingFinalizers(); | ||
} | ||
|
||
[Theory] | ||
[ClassData(typeof(MemoryTestTypes))] | ||
public async Task CheckAllocation((Type ViewType, Type HandlerType) data) | ||
{ | ||
if (!OperatingSystem.IsAndroidVersionAtLeast(30)) | ||
return; | ||
|
||
// This is mainly relevant when running inside the visual runner as a single test | ||
if (!_fixture.HasType(data.HandlerType)) | ||
await Allocate(data); | ||
|
||
await AssertionExtensions.Wait(() => | ||
{ | ||
GC.Collect(); | ||
GC.WaitForPendingFinalizers(); | ||
GC.Collect(); | ||
GC.WaitForPendingFinalizers(); | ||
if (_fixture.DoReferencesStillExist(data.HandlerType)) | ||
{ | ||
return false; | ||
} | ||
return true; | ||
}, 5000); | ||
|
||
if (_fixture.DoReferencesStillExist(data.HandlerType)) | ||
{ | ||
Assert.True(false, $"{data.HandlerType} failed to collect."); | ||
} | ||
} | ||
} | ||
} |