Skip to content

Commit 06bb2b9

Browse files
Add unit test to confirm change token is disposed during (#53827)
* Add unit test to confirm change token is disposed during razer hot reload. * Per Makinnon's feedback, switch to a callback model to create the wrapped disposable for this unit test. * Update src/Components/Endpoints/test/HotReloadServiceTests.cs --------- Co-authored-by: Mackinnon Buck <[email protected]>
1 parent 22ec418 commit 06bb2b9

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ internal class RazorComponentEndpointDataSource<[DynamicallyAccessedMembers(Comp
2828
private List<Endpoint>? _endpoints;
2929
private CancellationTokenSource _cancellationTokenSource;
3030
private IChangeToken _changeToken;
31-
private IDisposable? _disposable; // THREADING: protected by _lock
31+
private IDisposable? _disposableChangeToken; // THREADING: protected by _lock
32+
33+
public Func<IDisposable, IDisposable> SetDisposableChangeTokenAction = disposableChangeToken => disposableChangeToken;
3234

3335
// Internal for testing.
3436
internal ComponentApplicationBuilder Builder => _builder;
@@ -144,8 +146,8 @@ private void UpdateEndpoints()
144146
oldCancellationTokenSource?.Dispose();
145147
if (_hotReloadService is { MetadataUpdateSupported : true })
146148
{
147-
_disposable?.Dispose();
148-
_disposable = ChangeToken.OnChange(_hotReloadService.GetChangeToken, UpdateEndpoints);
149+
_disposableChangeToken?.Dispose();
150+
_disposableChangeToken = SetDisposableChangeTokenAction(ChangeToken.OnChange(_hotReloadService.GetChangeToken, UpdateEndpoints));
149151
}
150152
}
151153
}
@@ -154,8 +156,8 @@ public void OnHotReloadClearCache(Type[]? types)
154156
{
155157
lock (_lock)
156158
{
157-
_disposable?.Dispose();
158-
_disposable = null;
159+
_disposableChangeToken?.Dispose();
160+
_disposableChangeToken = null;
159161
}
160162
}
161163

src/Components/Endpoints/test/HotReloadServiceTests.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,52 @@ public void NotifiesCompositeEndpointDataSource()
137137
Assert.Empty(compositeEndpointDataSource.Endpoints);
138138
}
139139

140+
private sealed class WrappedChangeTokenDisposable : IDisposable
141+
{
142+
public bool IsDisposed { get; private set; }
143+
private readonly IDisposable _innerDisposable;
144+
145+
public WrappedChangeTokenDisposable(IDisposable innerDisposable)
146+
{
147+
_innerDisposable = innerDisposable;
148+
}
149+
150+
public void Dispose()
151+
{
152+
IsDisposed = true;
153+
_innerDisposable.Dispose();
154+
}
155+
}
156+
157+
[Fact]
158+
public void ConfirmChangeTokenDisposedHotReload()
159+
{
160+
// Arrange
161+
var builder = CreateBuilder(typeof(ServerComponent));
162+
var services = CreateServices(typeof(MockEndpointProvider));
163+
var endpointDataSource = CreateDataSource<App>(builder, services);
164+
165+
WrappedChangeTokenDisposable wrappedChangeTokenDisposable = null;
166+
167+
endpointDataSource.SetDisposableChangeTokenAction = (IDisposable disposableChangeToken) => {
168+
wrappedChangeTokenDisposable = new WrappedChangeTokenDisposable(disposableChangeToken);
169+
return wrappedChangeTokenDisposable;
170+
};
171+
172+
var endpoint = Assert.IsType<RouteEndpoint>(Assert.Single(endpointDataSource.Endpoints));
173+
Assert.Equal("/server", endpoint.RoutePattern.RawText);
174+
Assert.DoesNotContain(endpoint.Metadata, (element) => element is TestMetadata);
175+
176+
// Make a modification and then perform a hot reload.
177+
endpointDataSource.Conventions.Add(builder =>
178+
builder.Metadata.Add(new TestMetadata()));
179+
HotReloadService.UpdateApplication(null);
180+
HotReloadService.ClearCache(null);
181+
182+
// Confirm the change token is disposed after ClearCache
183+
Assert.True(wrappedChangeTokenDisposable.IsDisposed);
184+
}
185+
140186
private class TestMetadata { }
141187

142188
private ComponentApplicationBuilder CreateBuilder(params Type[] types)

0 commit comments

Comments
 (0)