Remove usage of IsolatedStorage (#2710)

This commit is contained in:
Matthew Leibowitz 2021-11-10 20:18:36 +02:00 коммит произвёл GitHub
Родитель c905fd6260
Коммит 0a3229912d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 116 добавлений и 660 удалений

Просмотреть файл

@ -15,7 +15,7 @@
]
},
"microsoft.dotnet.xharness.cli": {
"version": "1.0.0-prerelease.21425.2",
"version": "1.0.0-prerelease.21558.2",
"commands": [
"xharness"
]

Просмотреть файл

@ -1,4 +1,4 @@
using System.IO.IsolatedStorage;
using System;
using Microsoft.Maui.Controls.Compatibility.ControlGallery;
using IOPath = System.IO.Path;
@ -8,22 +8,7 @@ namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.Android
{
public void ClearImageCache()
{
DeleteFilesInDirectory("ImageLoaderCache");
}
static void DeleteFilesInDirectory(string directory)
{
using (IsolatedStorageFile isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isolatedStorage.DirectoryExists(directory))
{
var files = isolatedStorage.GetFileNames(IOPath.Combine(directory, "*"));
foreach (string file in files)
{
isolatedStorage.DeleteFile(IOPath.Combine(directory, file));
}
}
}
throw new NotImplementedException("TODO: CACHING https://github.com/dotnet/runtime/issues/52332");
}
}
}

Просмотреть файл

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
@ -665,24 +664,6 @@ namespace Microsoft.Maui.Controls.Compatibility
return null;
}
public async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
{
using (var client = new HttpClient())
{
// Do not remove this await otherwise the client will dispose before
// the stream even starts
var result = await StreamWrapper.GetStreamAsync(uri, cancellationToken, client).ConfigureAwait(false);
return result;
}
}
public IIsolatedStorageFile GetUserStoreForApplication()
{
throw new NotImplementedException("GetUserStoreForApplication currently not available https://github.com/dotnet/runtime/issues/52332");
//return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication());
}
public bool IsInvokeRequired
{
get
@ -783,49 +764,6 @@ namespace Microsoft.Maui.Controls.Compatibility
};
}
}
public class _IsolatedStorageFile : IIsolatedStorageFile
{
readonly IsolatedStorageFile _isolatedStorageFile;
public _IsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
{
_isolatedStorageFile = isolatedStorageFile;
}
public Task CreateDirectoryAsync(string path)
{
_isolatedStorageFile.CreateDirectory(path);
return Task.FromResult(true);
}
public Task<bool> GetDirectoryExistsAsync(string path)
{
return Task.FromResult(_isolatedStorageFile.DirectoryExists(path));
}
public Task<bool> GetFileExistsAsync(string path)
{
return Task.FromResult(_isolatedStorageFile.FileExists(path));
}
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
{
return Task.FromResult(_isolatedStorageFile.GetLastWriteTime(path));
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
{
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access);
return Task.FromResult(stream);
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
{
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access, share);
return Task.FromResult(stream);
}
}
}
}
}

Просмотреть файл

@ -147,11 +147,6 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
}
}
public IIsolatedStorageFile GetUserStoreForApplication()
{
return new WindowsIsolatedStorage(ApplicationData.Current.LocalFolder);
}
public bool IsInvokeRequired => !_dispatcher?.HasThreadAccess ?? true;
public string RuntimePlatform => Device.UWP;

Просмотреть файл

@ -1,119 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
//#if !UWP_16299
//using FileMode = Microsoft.Maui.Controls.Compatibility.FileMode;
//using FileAccess = Microsoft.Maui.Controls.Compatibility.Internals.FileAccess;
//using FileShare = Microsoft.Maui.Controls.Compatibility.Internals.FileShare;
//#endif
namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
{
internal class WindowsIsolatedStorage : Internals.IIsolatedStorageFile
{
StorageFolder _folder;
public WindowsIsolatedStorage(StorageFolder folder)
{
if (folder == null)
throw new ArgumentNullException("folder");
_folder = folder;
}
public Task CreateDirectoryAsync(string path)
{
return _folder.CreateFolderAsync(path).AsTask();
}
public async Task<bool> GetDirectoryExistsAsync(string path)
{
try
{
await _folder.GetFolderAsync(path).AsTask().ConfigureAwait(false);
return true;
}
catch (FileNotFoundException)
{
return false;
}
}
public async Task<bool> GetFileExistsAsync(string path)
{
try
{
await _folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
return true;
}
catch (FileNotFoundException)
{
return false;
}
}
public async Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
{
StorageFile file = await _folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
BasicProperties properties = await file.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false);
return properties.DateModified;
}
public async Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
{
StorageFile file;
switch (mode)
{
case FileMode.CreateNew:
file = await _folder.CreateFileAsync(path, CreationCollisionOption.FailIfExists).AsTask().ConfigureAwait(false);
break;
case FileMode.Create:
case FileMode.Truncate: // TODO See if ReplaceExisting already truncates
file = await _folder.CreateFileAsync(path, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
break;
case FileMode.OpenOrCreate:
case FileMode.Append:
file = await _folder.CreateFileAsync(path, CreationCollisionOption.OpenIfExists).AsTask().ConfigureAwait(false);
break;
case FileMode.Open:
file = await _folder.GetFileAsync(path);
break;
default:
throw new ArgumentException("mode was an invalid FileMode", "mode");
}
switch (access)
{
case FileAccess.Read:
return await file.OpenStreamForReadAsync().ConfigureAwait(false);
case FileAccess.Write:
Stream stream = await file.OpenStreamForWriteAsync().ConfigureAwait(false);
if (mode == FileMode.Append)
stream.Position = stream.Length;
return stream;
case FileAccess.ReadWrite:
IRandomAccessStream randStream = await file.OpenAsync(FileAccessMode.ReadWrite).AsTask().ConfigureAwait(false);
return randStream.AsStream();
default:
throw new ArgumentException("access was an invalid FileAccess", "access");
}
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
{
return OpenFileAsync(path, mode, access);
}
}
}

Просмотреть файл

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
@ -637,11 +636,6 @@ namespace Microsoft.Maui.Controls.Compatibility
}
}
public IIsolatedStorageFile GetUserStoreForApplication()
{
return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication());
}
public bool IsInvokeRequired => !NSThread.IsMain;
#if __MOBILE__
@ -679,49 +673,6 @@ namespace Microsoft.Maui.Controls.Compatibility
return 'a' + v - 10;
}
public class _IsolatedStorageFile : IIsolatedStorageFile
{
readonly IsolatedStorageFile _isolatedStorageFile;
public _IsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
{
_isolatedStorageFile = isolatedStorageFile;
}
public Task CreateDirectoryAsync(string path)
{
_isolatedStorageFile.CreateDirectory(path);
return Task.FromResult(true);
}
public Task<bool> GetDirectoryExistsAsync(string path)
{
return Task.FromResult(_isolatedStorageFile.DirectoryExists(path));
}
public Task<bool> GetFileExistsAsync(string path)
{
return Task.FromResult(_isolatedStorageFile.FileExists(path));
}
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
{
return Task.FromResult(_isolatedStorageFile.GetLastWriteTime(path));
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
{
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access);
return Task.FromResult(stream);
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
{
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access, share);
return Task.FromResult(stream);
}
}
#if !__MOBILE__
public void QuitApplication()
{

Просмотреть файл

@ -2,7 +2,6 @@ using System;
using System.Threading.Tasks;
using System.Threading;
using System.Reflection;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using Microsoft.Maui.Controls;
using System.Security.Cryptography;
@ -116,53 +115,6 @@ namespace Microsoft.Maui.Controls.DualScreen.UnitTests
return AppDomain.CurrentDomain.GetAssemblies();
}
public Internals.IIsolatedStorageFile GetUserStoreForApplication()
{
return new MockIsolatedStorageFile(IsolatedStorageFile.GetUserStoreForAssembly());
}
public class MockIsolatedStorageFile : Internals.IIsolatedStorageFile
{
readonly IsolatedStorageFile isolatedStorageFile;
public MockIsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
{
this.isolatedStorageFile = isolatedStorageFile;
}
public Task<bool> GetDirectoryExistsAsync(string path)
{
return Task.FromResult(isolatedStorageFile.DirectoryExists(path));
}
public Task CreateDirectoryAsync(string path)
{
isolatedStorageFile.CreateDirectory(path);
return Task.FromResult(true);
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
{
Stream stream = isolatedStorageFile.OpenFile(path, mode, access);
return Task.FromResult(stream);
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
{
Stream stream = isolatedStorageFile.OpenFile(path, mode, access, share);
return Task.FromResult(stream);
}
public Task<bool> GetFileExistsAsync(string path)
{
return Task.FromResult(isolatedStorageFile.FileExists(path));
}
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
{
return Task.FromResult(isolatedStorageFile.GetLastWriteTime(path));
}
}
public SizeRequest GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
{
if (getNativeSizeFunc != null)

Просмотреть файл

@ -169,11 +169,6 @@ namespace Microsoft.Maui.Controls
return PlatformServices.GetNamedColor(name);
}
internal static Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
{
return PlatformServices.GetStreamAsync(uri, cancellationToken);
}
public static class Styles
{
public static readonly string TitleStyleKey = "TitleStyle";

Просмотреть файл

@ -1,20 +0,0 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
namespace Microsoft.Maui.Controls.Internals
{
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IIsolatedStorageFile
{
Task CreateDirectoryAsync(string path);
Task<bool> GetDirectoryExistsAsync(string path);
Task<bool> GetFileExistsAsync(string path);
Task<DateTimeOffset> GetLastWriteTimeAsync(string path);
Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access);
Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share);
}
}

Просмотреть файл

@ -23,10 +23,6 @@ namespace Microsoft.Maui.Controls.Internals
OSAppTheme RequestedTheme { get; }
Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken);
IIsolatedStorageFile GetUserStoreForApplication();
void StartTimer(TimeSpan interval, Func<bool> callback);
string RuntimePlatform { get; }

Просмотреть файл

@ -1,76 +1,44 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Maui.Controls.Internals;
using IOPath = System.IO.Path;
namespace Microsoft.Maui.Controls
{
// TODO: CACHING https://github.com/dotnet/runtime/issues/52332
public sealed partial class UriImageSource : ImageSource, IStreamImageSource
{
internal const string CacheName = "ImageLoaderCache";
public static readonly BindableProperty UriProperty = BindableProperty.Create(
nameof(Uri), typeof(Uri), typeof(UriImageSource), default(Uri),
propertyChanged: (bindable, oldvalue, newvalue) => ((UriImageSource)bindable).OnUriChanged(),
validateValue: (bindable, value) => value == null || ((Uri)value).IsAbsoluteUri);
public static readonly BindableProperty UriProperty = BindableProperty.Create("Uri", typeof(Uri), typeof(UriImageSource), default(Uri),
propertyChanged: (bindable, oldvalue, newvalue) => ((UriImageSource)bindable).OnUriChanged(), validateValue: (bindable, value) => value == null || ((Uri)value).IsAbsoluteUri);
public static readonly BindableProperty CacheValidityProperty = BindableProperty.Create(
nameof(CacheValidity), typeof(TimeSpan), typeof(UriImageSource), TimeSpan.FromDays(1));
// https://github.com/dotnet/runtime/issues/52332
// static readonly Microsoft.Maui.Controls.Internals.IIsolatedStorageFile Store = Device.PlatformServices.GetUserStoreForApplication();
static readonly object s_syncHandle = new object();
static readonly Dictionary<string, LockingSemaphore> s_semaphores = new Dictionary<string, LockingSemaphore>();
TimeSpan _cacheValidity = TimeSpan.FromDays(1);
bool _cachingEnabled = true;
static UriImageSource()
{
/*if (!Store.GetDirectoryExistsAsync(CacheName).Result)
Store.CreateDirectoryAsync(CacheName).Wait();*/
}
public static readonly BindableProperty CachingEnabledProperty = BindableProperty.Create(
nameof(CachingEnabled), typeof(bool), typeof(UriImageSource), true);
public override bool IsEmpty => Uri == null;
public TimeSpan CacheValidity
{
get { return _cacheValidity; }
set
{
if (_cacheValidity == value)
return;
OnPropertyChanging();
_cacheValidity = value;
OnPropertyChanged();
}
get => (TimeSpan)GetValue(CacheValidityProperty);
set => SetValue(CacheValidityProperty, value);
}
public bool CachingEnabled
{
get
{
return false;
//return _cachingEnabled;
}
set
{
if (_cachingEnabled == value)
return;
OnPropertyChanging();
_cachingEnabled = value;
OnPropertyChanged();
}
get => (bool)GetValue(CachingEnabledProperty);
set => SetValue(CachingEnabledProperty, value);
}
[System.ComponentModel.TypeConverter(typeof(UriTypeConverter))]
public Uri Uri
{
get { return (Uri)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
get => (Uri)GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
async Task<Stream> IStreamImageSource.GetStreamAsync(CancellationToken userToken)
@ -94,7 +62,7 @@ namespace Microsoft.Maui.Controls
}
catch (Exception ex)
{
Microsoft.Maui.Controls.Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
throw;
}
@ -106,23 +74,6 @@ namespace Microsoft.Maui.Controls
return $"Uri: {Uri}";
}
Task<bool> GetHasLocallyCachedCopyAsync(string key, bool checkValidity = true)
{
return Task.FromResult(false);
//DateTime now = DateTime.UtcNow;
//DateTime? lastWriteTime = await GetLastWriteTimeUtcAsync(key).ConfigureAwait(false);
//return lastWriteTime.HasValue && now - lastWriteTime.Value < CacheValidity;
}
//static async Task<DateTime?> GetLastWriteTimeUtcAsync(string key)
//{
// string path = IOPath.Combine(CacheName, key);
// if (!await Store.GetFileExistsAsync(path).ConfigureAwait(false))
// return null;
// return (await Store.GetLastWriteTimeAsync(path).ConfigureAwait(false)).UtcDateTime;
//}
async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
@ -130,127 +81,45 @@ namespace Microsoft.Maui.Controls
Stream stream = null;
if (CachingEnabled)
stream = await GetStreamFromCacheAsync(uri, cancellationToken).ConfigureAwait(false);
if (stream == null)
{
try
{
stream = await Device.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
Microsoft.Maui.Controls.Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
stream = null;
}
// TODO: CACHING https://github.com/dotnet/runtime/issues/52332
// var key = GetKey();
// var cached = TryGetFromCache(key, out stream)
if (stream is null)
stream = await DownloadStreamAsync(uri, cancellationToken).ConfigureAwait(false);
// if (!cached)
// Cache(key, stream)
}
else
{
stream = await DownloadStreamAsync(uri, cancellationToken).ConfigureAwait(false);
}
return stream;
}
async Task<Stream> GetStreamAsyncUnchecked(string key, Uri uri, CancellationToken cancellationToken)
async Task<Stream> DownloadStreamAsync(Uri uri, CancellationToken cancellationToken)
{
//if (await GetHasLocallyCachedCopyAsync(key).ConfigureAwait(false))
//{
// var retry = 5;
// while (retry >= 0)
// {
// int backoff;
// try
// {
// Stream result = await Store.OpenFileAsync(IOPath.Combine(CacheName, key), FileMode.Open, FileAccess.Read).ConfigureAwait(false);
// return result;
// }
// catch (IOException)
// {
// // iOS seems to not like 2 readers opening the file at the exact same time, back off for random amount of time
// backoff = new Random().Next(1, 5);
// retry--;
// }
// if (backoff > 0)
// {
// await Task.Delay(backoff);
// }
// }
// return null;
//}
Stream stream;
try
{
stream = await Device.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false);
if (stream == null)
return null;
using var client = new HttpClient();
// Do not remove this await otherwise the client will dispose before
// the stream even starts
return await StreamWrapper.GetStreamAsync(uri, cancellationToken, client).ConfigureAwait(false);
}
catch (Exception ex)
{
Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
return null;
}
if (stream == null || !stream.CanRead)
{
stream?.Dispose();
return null;
}
return stream;
//try
//{
// Stream writeStream = await Store.OpenFileAsync(IOPath.Combine(CacheName, key), FileMode.Create, FileAccess.Write).ConfigureAwait(false);
// await stream.CopyToAsync(writeStream, 16384, cancellationToken).ConfigureAwait(false);
// if (writeStream != null)
// writeStream.Dispose();
// stream.Dispose();
// return await Store.OpenFileAsync(IOPath.Combine(CacheName, key), FileMode.Open, FileAccess.Read).ConfigureAwait(false);
//}
//catch (Exception ex)
//{
// Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
// return null;
//}
}
async Task<Stream> GetStreamFromCacheAsync(Uri uri, CancellationToken cancellationToken)
{
string key = Crc64.ComputeHashString(uri.AbsoluteUri);
LockingSemaphore sem;
lock (s_syncHandle)
{
if (s_semaphores.ContainsKey(key))
sem = s_semaphores[key];
else
s_semaphores.Add(key, sem = new LockingSemaphore(1));
}
try
{
await sem.WaitAsync(cancellationToken);
Stream stream = await GetStreamAsyncUnchecked(key, uri, cancellationToken);
if (stream == null || stream.Length == 0 || !stream.CanRead)
{
sem.Release();
return null;
}
var wrapped = new StreamWrapper(stream);
wrapped.Disposed += (o, e) => sem.Release();
return wrapped;
}
catch (OperationCanceledException)
{
sem.Release();
throw;
}
}
void OnUriChanged()
{
if (CancellationTokenSource != null)
CancellationTokenSource.Cancel();
CancellationTokenSource?.Cancel();
OnSourceChanged();
}
}

Просмотреть файл

@ -7,23 +7,8 @@ using NUnit.Framework;
namespace Microsoft.Maui.Controls.Core.UnitTests
{
[TestFixture]
public class ImageButtonTests
: CommandSourceTests<ImageButton>
public class ImageButtonTests : CommandSourceTests<ImageButton>
{
[SetUp]
public override void Setup()
{
base.Setup();
Device.PlatformServices = new MockPlatformServices(getStreamAsync: GetStreamAsync);
}
[TearDown]
public override void TearDown()
{
base.TearDown();
Device.PlatformServices = null;
}
[Test]
public void TestSizing()
{
@ -205,38 +190,40 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void TestImageSourceToNullCancelsLoading()
{
var cancelled = false;
var image = new ImageButton();
var mockImageRenderer = new MockImageRenderer(image);
var loader = new UriImageSource { Uri = new Uri("http://www.public-domain-image.com/free-images/miscellaneous/big-high-border-fence.jpg") };
var loader = new StreamImageSource { Stream = GetStreamAsync };
image.Source = loader;
Assert.IsTrue(image.IsLoading);
image.Source = null;
Assert.IsFalse(image.IsLoading);
Assert.IsTrue(cancelled);
}
static bool cancelled;
static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
{
try
async Task<Stream> GetStreamAsync(CancellationToken cancellationToken)
{
await Task.Delay(5000, cancellationToken);
}
catch (TaskCanceledException ex)
{
cancelled = true;
throw ex;
}
try
{
await Task.Delay(5000, cancellationToken);
}
catch (TaskCanceledException ex)
{
cancelled = true;
throw ex;
}
if (cancellationToken.IsCancellationRequested)
{
cancelled = true;
throw new TaskCanceledException();
}
if (cancellationToken.IsCancellationRequested)
{
cancelled = true;
throw new TaskCanceledException();
}
var stream = typeof(ImageTests).Assembly.GetManifestResourceStream(uri.LocalPath.Substring(1));
return stream;
var stream = typeof(ImageTests).Assembly.GetManifestResourceStream("dummy");
return stream;
}
}
class MockImageRenderer
@ -258,9 +245,19 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
if (initialLoad && Element.Source != null)
{
initialLoad = false;
Element.SetIsLoading(true);
await ((IStreamImageSource)Element.Source).GetStreamAsync();
Element.SetIsLoading(false);
try
{
Element.SetIsLoading(true);
await ((IStreamImageSource)Element.Source).GetStreamAsync();
}
catch (OperationCanceledException)
{
// this is expected
}
finally
{
Element.SetIsLoading(false);
}
}
}

Просмотреть файл

@ -11,20 +11,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[TestFixture]
public class ImageTests : BaseTestFixture
{
[SetUp]
public override void Setup()
{
base.Setup();
Device.PlatformServices = new MockPlatformServices(getStreamAsync: GetStreamAsync);
}
[TearDown]
public override void TearDown()
{
base.TearDown();
Device.PlatformServices = null;
}
[Test]
public void TestSizing()
{
@ -204,38 +190,39 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void TestImageSourceToNullCancelsLoading()
{
var cancelled = false;
var image = new Image();
var mockImageRenderer = new MockImageRenderer(image);
var loader = new UriImageSource { Uri = new Uri("http://www.public-domain-image.com/free-images/miscellaneous/big-high-border-fence.jpg") };
var loader = new StreamImageSource { Stream = GetStreamAsync };
image.Source = loader;
Assert.IsTrue(image.IsLoading);
image.Source = null;
Assert.IsFalse(image.IsLoading);
Assert.IsTrue(cancelled);
}
static bool cancelled;
static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
{
try
async Task<Stream> GetStreamAsync(CancellationToken cancellationToken)
{
await Task.Delay(5000, cancellationToken);
}
catch (TaskCanceledException ex)
{
cancelled = true;
throw ex;
}
try
{
await Task.Delay(5000, cancellationToken);
}
catch (TaskCanceledException)
{
cancelled = true;
throw;
}
if (cancellationToken.IsCancellationRequested)
{
cancelled = true;
throw new TaskCanceledException();
}
if (cancellationToken.IsCancellationRequested)
{
cancelled = true;
throw new TaskCanceledException();
}
var stream = typeof(ImageTests).Assembly.GetManifestResourceStream(uri.LocalPath.Substring(1));
return stream;
return typeof(ImageTests).Assembly.GetManifestResourceStream("dummy");
}
}
class MockImageRenderer
@ -258,9 +245,19 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
{
initialLoad = false;
var controller = (IImageController)Element;
controller.SetIsLoading(true);
await ((IStreamImageSource)Element.Source).GetStreamAsync();
controller.SetIsLoading(false);
try
{
controller.SetIsLoading(true);
await ((IStreamImageSource)Element.Source).GetStreamAsync();
}
catch (OperationCanceledException)
{
// this is expected
}
finally
{
controller.SetIsLoading(false);
}
}
}

Просмотреть файл

@ -305,11 +305,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
throw new NotImplementedException();
}
public IIsolatedStorageFile GetUserStoreForApplication()
{
throw new NotImplementedException();
}
public void QuitApplication()
{
throw new NotImplementedException();

Просмотреть файл

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
@ -24,18 +23,15 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
internal class MockPlatformServices : Internals.IPlatformServices
{
Action<Action> invokeOnMainThread;
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync;
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc;
readonly bool useRealisticLabelMeasure;
readonly bool _isInvokeRequired;
public MockPlatformServices(Action<Action> invokeOnMainThread = null,
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync = null,
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc = null,
bool useRealisticLabelMeasure = false, bool isInvokeRequired = false)
{
this.invokeOnMainThread = invokeOnMainThread;
this.getStreamAsync = getStreamAsync;
this.getNativeSizeFunc = getNativeSizeFunc;
this.useRealisticLabelMeasure = useRealisticLabelMeasure;
_isInvokeRequired = isInvokeRequired;
@ -105,65 +101,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
timer = new Timer(onTimeout, null, interval, interval);
}
public Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
{
if (getStreamAsync == null)
throw new NotImplementedException();
return getStreamAsync(uri, cancellationToken);
}
public Assembly[] GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
public Internals.IIsolatedStorageFile GetUserStoreForApplication()
{
return new MockIsolatedStorageFile(IsolatedStorageFile.GetUserStoreForAssembly());
}
public class MockIsolatedStorageFile : Internals.IIsolatedStorageFile
{
readonly IsolatedStorageFile isolatedStorageFile;
public MockIsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
{
this.isolatedStorageFile = isolatedStorageFile;
}
public Task<bool> GetDirectoryExistsAsync(string path)
{
return Task.FromResult(isolatedStorageFile.DirectoryExists(path));
}
public Task CreateDirectoryAsync(string path)
{
isolatedStorageFile.CreateDirectory(path);
return Task.FromResult(true);
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
{
Stream stream = isolatedStorageFile.OpenFile(path, mode, access);
return Task.FromResult(stream);
}
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
{
Stream stream = isolatedStorageFile.OpenFile(path, mode, access, share);
return Task.FromResult(stream);
}
public Task<bool> GetFileExistsAsync(string path)
{
return Task.FromResult(isolatedStorageFile.FileExists(path));
}
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
{
return Task.FromResult(isolatedStorageFile.GetLastWriteTime(path));
}
}
public SizeRequest GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
{
if (getNativeSizeFunc != null)

Просмотреть файл

@ -1,6 +1,5 @@
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
@ -11,14 +10,11 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[TestFixture]
public class UriImageSourceTests : BaseTestFixture
{
IsolatedStorageFile NativeStore { get; set; }
[SetUp]
public override void Setup()
{
base.Setup();
Device.PlatformServices = new MockPlatformServices(getStreamAsync: GetStreamAsync);
NativeStore = IsolatedStorageFile.GetUserStoreForAssembly();
Device.PlatformServices = new MockPlatformServices();
networkcalls = 0;
}
@ -27,14 +23,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
{
base.TearDown();
Device.PlatformServices = null;
string cacheName = "ImageLoaderCache";
if (NativeStore.DirectoryExists(cacheName))
{
foreach (var f in NativeStore.GetFileNames(cacheName + "/*"))
NativeStore.DeleteFile(IOPath.Combine(cacheName, f));
}
NativeStore.Dispose();
NativeStore = null;
}
static Random rnd = new Random();

Просмотреть файл

@ -19,7 +19,7 @@
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.utility" Version="2.4.1" />
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21425.2" />
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21558.2" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -15,7 +15,7 @@
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.utility" Version="2.4.1" />
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21425.2" />
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21558.2" />
</ItemGroup>
<ItemGroup>