/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Components/Shared/test/TestRenderer.cs
168 строк
6 KB
Steve Sanderson
Named events tracking (#49298)
12 июл 2023, 19:51
Не верифицирован
12 июл 2023, 19:51
c8caba5
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.ExceptionServices; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.Components.Test.Helpers; public class TestRenderer : Renderer { public TestRenderer() : this(new TestServiceProvider()) { } public TestRenderer(Dispatcher dispatcher) : base(new TestServiceProvider(), NullLoggerFactory.Instance) { Dispatcher = dispatcher; } public TestRenderer(IServiceProvider serviceProvider) : base(serviceProvider, NullLoggerFactory.Instance) { Dispatcher = Dispatcher.CreateDefault(); } public TestRenderer(IServiceProvider serviceProvider, IComponentActivator componentActivator) : base(serviceProvider, NullLoggerFactory.Instance, componentActivator) { Dispatcher = Dispatcher.CreateDefault(); } public override Dispatcher Dispatcher { get; } public Action OnExceptionHandled { get; set; } public Action<RenderBatch> OnUpdateDisplay { get; set; } public Action OnUpdateDisplayComplete { get; set; } public List<CapturedBatch> Batches { get; } = new List<CapturedBatch>(); public List<Exception> HandledExceptions { get; } = new List<Exception>(); public bool ShouldHandleExceptions { get; set; } public Task NextRenderResultTask { get; set; } = Task.CompletedTask; private HashSet<TestRendererComponentState> UndisposedComponentStates { get; } = new(); public new int AssignRootComponentId(IComponent component) => base.AssignRootComponentId(component); public new void RemoveRootComponent(int componentId) => base.RemoveRootComponent(componentId); public new ArrayRange<RenderTreeFrame> GetCurrentRenderTreeFrames(int componentId) => base.GetCurrentRenderTreeFrames(componentId); public void RenderRootComponent(int componentId, ParameterView? parameters = default) { var task = Dispatcher.InvokeAsync(() => base.RenderRootComponentAsync(componentId, parameters ?? ParameterView.Empty)); UnwrapTask(task); } public new Task RenderRootComponentAsync(int componentId) => Dispatcher.InvokeAsync(() => base.RenderRootComponentAsync(componentId)); public new Task RenderRootComponentAsync(int componentId, ParameterView parameters) => Dispatcher.InvokeAsync(() => base.RenderRootComponentAsync(componentId, parameters)); public Task DispatchEventAsync(ulong eventHandlerId, EventArgs args) => Dispatcher.InvokeAsync(() => base.DispatchEventAsync(eventHandlerId, null, args)); public new Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo eventFieldInfo, EventArgs args) => Dispatcher.InvokeAsync(() => base.DispatchEventAsync(eventHandlerId, eventFieldInfo, args)); private static Task UnwrapTask(Task task) { // This should always be run synchronously Assert.True(task.IsCompleted); if (task.IsFaulted) { var exception = task.Exception.Flatten().InnerException; while (exception is AggregateException e) { exception = e.InnerException; } ExceptionDispatchInfo.Capture(exception).Throw(); } return task; } public IComponent InstantiateComponent<T>() => InstantiateComponent(typeof(T)); protected override void HandleException(Exception exception) { if (!ShouldHandleExceptions) { ExceptionDispatchInfo.Capture(exception).Throw(); } HandledExceptions.Add(exception); OnExceptionHandled?.Invoke(); } protected override Task UpdateDisplayAsync(in RenderBatch renderBatch) { OnUpdateDisplay?.Invoke(renderBatch); var capturedBatch = new CapturedBatch(); Batches.Add(capturedBatch); for (var i = 0; i < renderBatch.UpdatedComponents.Count; i++) { ref var renderTreeDiff = ref renderBatch.UpdatedComponents.Array[i]; capturedBatch.AddDiff(renderTreeDiff); } // Clone other data, as underlying storage will get reused by later batches capturedBatch.ReferenceFrames = renderBatch.ReferenceFrames.AsEnumerable().ToArray(); capturedBatch.DisposedComponentIDs = renderBatch.DisposedComponentIDs.AsEnumerable().ToList(); // This renderer updates the UI synchronously, like the WebAssembly one. // To test async UI updates, subclass TestRenderer and override UpdateDisplayAsync. OnUpdateDisplayComplete?.Invoke(); return NextRenderResultTask; } public new void ProcessPendingRender() => base.ProcessPendingRender(); protected override ComponentState CreateComponentState(int componentId, IComponent component, ComponentState parentComponentState) => new TestRendererComponentState(this, componentId, component, parentComponentState); protected override void Dispose(bool disposing) { base.Dispose(disposing); if (UndisposedComponentStates.Count > 0) { throw new InvalidOperationException("Did not dispose all the ComponentState instances. This could lead to ArrayBuffer not returning buffers to its pool."); } } class TestRendererComponentState : ComponentState, IAsyncDisposable { private readonly TestRenderer _renderer; public TestRendererComponentState(Renderer renderer, int componentId, IComponent component, ComponentState parentComponentState) : base(renderer, componentId, component, parentComponentState) { _renderer = (TestRenderer)renderer; _renderer.UndisposedComponentStates.Add(this); } public override ValueTask DisposeAsync() { _renderer.UndisposedComponentStates.Remove(this); return base.DisposeAsync(); } } }