using System.Runtime.CompilerServices;
using XFESystem.Collections;
namespace XFESystem.Browser.Tests;
[Collection("Browser lifetime ownership")]
public sealed class EngineWeakCacheTests
{
[Fact]
public void ShapeFactoryMatchesOriginalConditionalWeakTableReentrancyNullAndFailureSemantics()
{
var original=new ConditionalWeakTable<object,object>();var scope=new WeakCollectionScope();
var owned=new OwnedWeakTable<object,object>(scope);var key=new object();var existing=new object();var discarded=new object();
Assert.Same(existing,original.GetValue(key,k=>{original.Add(k,existing);return discarded;}));
Assert.Same(existing,owned.GetValue(key,k=>{owned.Add(k,existing);return discarded;}));
Assert.Throws<ArgumentNullException>(()=>original.GetValue(key,null!));
Assert.Throws<ArgumentNullException>(()=>owned.GetValue(key,null!));
Assert.Equal(Assert.Throws<ArgumentNullException>(()=>original.GetValue(null!,null!)).ParamName,
Assert.Throws<ArgumentNullException>(()=>owned.GetValue(null!,null!)).ParamName);
Assert.Throws<ArgumentNullException>(()=>original.GetValue(null!,_=>new object()));
Assert.Throws<ArgumentNullException>(()=>owned.GetValue(null!,_=>new object()));
var nullKey=new object();Assert.Null(original.GetValue(nullKey,_=>null!));Assert.Null(owned.GetValue(nullKey,_=>null!));
Assert.True(original.TryGetValue(nullKey,out var nullOriginal));Assert.Null(nullOriginal);
Assert.True(owned.TryGetValue(nullKey,out var nullOwned));Assert.Null(nullOwned);
int calls=0;Assert.Null(owned.GetValue(nullKey,_=>{calls++;return new();}));Assert.Equal(0,calls);
var failedKey=new object();var known=new InvalidOperationException("factory failed");
Assert.Same(known,Assert.Throws<InvalidOperationException>(()=>owned.GetValue(failedKey,_=>throw known)));
Assert.False(owned.TryGetValue(failedKey,out _));
Assert.Same(existing,owned.GetOrCreateValue(failedKey,()=>{owned.Add(failedKey,existing);return discarded;}));
scope.Release();var released=scope.Lifetime;
Assert.Throws<ObjectDisposedException>(()=>owned.GetValue(key,_=>new()));
Assert.Throws<ObjectDisposedException>(()=>owned.GetOrCreateValue(key,()=>new()));
Assert.Equal(released,scope.Lifetime);Assert.Equal(released.Created,released.Disposed);
}
[Fact]
public void ActualShapeAndPrivateClassIntrinsicsReleaseEveryOwnedCacheWithoutGarbageCollection()
{
for(int iteration=0;iteration<4;iteration++)
{
var result=BrowserWeakCollectionsRegression.Run();
Assert.True(result.Passed,System.Text.Json.JsonSerializer.Serialize(result));Assert.True(result.EngineCachesExercised);
Assert.True(result.ConstructorExceptionPreserved);Assert.True(result.ConstructorFailureReleased);
Assert.Equal(result.ConstructorAfterRelease.Created,result.ConstructorAfterRelease.Disposed);
Assert.Equal(result.ConstructorAfterRelease.TableHandlesCreated,result.ConstructorAfterRelease.TableHandlesDisposed);
Assert.InRange(result.BeforeRelease.Created,130,long.MaxValue);
}
}
[Fact]
public void OriginalMonacoShapeCacheIsIncludedInExplicitPageRelease()
{
var browser=new BrowserService();browser.ResizeViewport(796,404);
browser.NavigateEditor("using System;\nConsole.WriteLine(\"XFESystem\");\n","Hello.cs");
for(int step=0;step<4;step++)browser.AdvanceTime(step*100);
Assert.Contains("XFESystem",browser.ReadEditorText(),StringComparison.Ordinal);
var runtime=(JavaScriptRuntime)typeof(BrowserService).GetField("runtime",System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance)!.GetValue(browser)!;
var scope=((IWeakCollectionOwner)(object)runtime.Engine).WeakCollections;
var table=typeof(Jint.Engine).GetField("_emptyShapes",System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance)!.GetValue(runtime.Engine);
Assert.NotNull(table);Assert.Equal(typeof(OwnedWeakTable<,>),table.GetType().GetGenericTypeDefinition());
var before=scope.Lifetime;Assert.InRange(before.Created,190,long.MaxValue);
browser.Release();var after=scope.Lifetime;
Assert.Equal(0,after.Tables);Assert.Equal(0,after.Entries);Assert.Equal(after.Created,after.Disposed);
Assert.Equal(after.TableHandlesCreated,after.TableHandlesDisposed);
GC.KeepAlive(runtime);GC.KeepAlive(table);
}
}
using System.Runtime.CompilerServices;
using XFESystem.Collections;
namespace XFESystem.Browser.Tests;
[Collection("Browser lifetime ownership")]
public sealed class EngineWeakCacheTests
{
[Fact]
public void ShapeFactoryMatchesOriginalConditionalWeakTableReentrancyNullAndFailureSemantics()
{
var original=new ConditionalWeakTable<object,object>();var scope=new WeakCollectionScope();
var owned=new OwnedWeakTable<object,object>(scope);var key=new object();var existing=new object();var discarded=new object();
Assert.Same(existing,original.GetValue(key,k=>{original.Add(k,existing);return discarded;}));
Assert.Same(existing,owned.GetValue(key,k=>{owned.Add(k,existing);return discarded;}));
Assert.Throws<ArgumentNullException>(()=>original.GetValue(key,null!));
Assert.Throws<ArgumentNullException>(()=>owned.GetValue(key,null!));
Assert.Equal(Assert.Throws<ArgumentNullException>(()=>original.GetValue(null!,null!)).ParamName,
Assert.Throws<ArgumentNullException>(()=>owned.GetValue(null!,null!)).ParamName);
Assert.Throws<ArgumentNullException>(()=>original.GetValue(null!,_=>new object()));
Assert.Throws<ArgumentNullException>(()=>owned.GetValue(null!,_=>new object()));
var nullKey=new object();Assert.Null(original.GetValue(nullKey,_=>null!));Assert.Null(owned.GetValue(nullKey,_=>null!));
Assert.True(original.TryGetValue(nullKey,out var nullOriginal));Assert.Null(nullOriginal);
Assert.True(owned.TryGetValue(nullKey,out var nullOwned));Assert.Null(nullOwned);
int calls=0;Assert.Null(owned.GetValue(nullKey,_=>{calls++;return new();}));Assert.Equal(0,calls);
var failedKey=new object();var known=new InvalidOperationException("factory failed");
Assert.Same(known,Assert.Throws<InvalidOperationException>(()=>owned.GetValue(failedKey,_=>throw known)));
Assert.False(owned.TryGetValue(failedKey,out _));
Assert.Same(existing,owned.GetOrCreateValue(failedKey,()=>{owned.Add(failedKey,existing);return discarded;}));
scope.Release();var released=scope.Lifetime;
Assert.Throws<ObjectDisposedException>(()=>owned.GetValue(key,_=>new()));
Assert.Throws<ObjectDisposedException>(()=>owned.GetOrCreateValue(key,()=>new()));
Assert.Equal(released,scope.Lifetime);Assert.Equal(released.Created,released.Disposed);
}
[Fact]
public void ActualShapeAndPrivateClassIntrinsicsReleaseEveryOwnedCacheWithoutGarbageCollection()
{
for(int iteration=0;iteration<4;iteration++)
{
var result=BrowserWeakCollectionsRegression.Run();
Assert.True(result.Passed,System.Text.Json.JsonSerializer.Serialize(result));Assert.True(result.EngineCachesExercised);
Assert.True(result.ConstructorExceptionPreserved);Assert.True(result.ConstructorFailureReleased);
Assert.Equal(result.ConstructorAfterRelease.Created,result.ConstructorAfterRelease.Disposed);
Assert.Equal(result.ConstructorAfterRelease.TableHandlesCreated,result.ConstructorAfterRelease.TableHandlesDisposed);
Assert.InRange(result.BeforeRelease.Created,130,long.MaxValue);
}
}
[Fact]
public void OriginalMonacoShapeCacheIsIncludedInExplicitPageRelease()
{
var browser=new BrowserService();browser.ResizeViewport(796,404);
browser.NavigateEditor("using System;\nConsole.WriteLine(\"XFESystem\");\n","Hello.cs");
for(int step=0;step<4;step++)browser.AdvanceTime(step*100);
Assert.Contains("XFESystem",browser.ReadEditorText(),StringComparison.Ordinal);
var runtime=(JavaScriptRuntime)typeof(BrowserService).GetField("runtime",System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance)!.GetValue(browser)!;
var scope=((IWeakCollectionOwner)(object)runtime.Engine).WeakCollections;
var table=typeof(Jint.Engine).GetField("_emptyShapes",System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance)!.GetValue(runtime.Engine);
Assert.NotNull(table);Assert.Equal(typeof(OwnedWeakTable<,>),table.GetType().GetGenericTypeDefinition());
var before=scope.Lifetime;Assert.InRange(before.Created,190,long.MaxValue);
browser.Release();var after=scope.Lifetime;
Assert.Equal(0,after.Tables);Assert.Equal(0,after.Entries);Assert.Equal(after.Created,after.Disposed);
Assert.Equal(after.TableHandlesCreated,after.TableHandlesDisposed);
GC.KeepAlive(runtime);GC.KeepAlive(table);
}
}