Files
sbox-public/engine/Sandbox.Compiling.Test/Tests/FastPathTest.Helpers.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

198 lines
4.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
#nullable enable
namespace TestCompiler;
public interface IProgram
{
int Main( StringWriter output );
}
internal record BuildResult(
Assembly Assembly,
Assembly MethodBodyAssembly,
bool ILHotloadSupported,
bool HasSupportedAttribute,
(MethodBase Old, MethodBase New)[] ChangedMethods )
{
public IProgram CreateProgram( string typeName = "TestPackage.Program" )
{
var type = Assembly.GetType( typeName );
Assert.IsNotNull( type );
return (IProgram)Activator.CreateInstance( type )!;
}
}
internal class FastPathTestCompiler : IDisposable
{
public string Name { get; }
public ILHotload Hotload { get; }
public CompileGroup Group { get; }
public Compiler Compiler { get; }
public BaseFileSystem FileSystem { get; }
public BuildResult? LastResult { get; private set; }
public Compiler.Configuration Config
{
get => Compiler.GetConfiguration();
set => Compiler.SetConfiguration( value );
}
public FastPathTestCompiler( string sourceName )
{
Name = Path.GetFileNameWithoutExtension( sourceName );
Hotload = new ILHotload( "Test" );
Group = new CompileGroup( "Test" ) { AllowFastHotload = true };
Compiler = new Compiler( Group, Name );
Config = new Compiler.Configuration();
FileSystem = new MemoryFileSystem();
Compiler.AddSourceLocation( FileSystem );
Compiler.AddReference( "Sandbox.Compiling.Test" );
}
public async Task<BuildResult> BuildAsync( int version = 1, bool expectNotSupported = false )
{
var compileSuccessCallback = false;
Group.OnCompileSuccess = () => compileSuccessCallback = true;
var srcFile = new FileInfo( Path.Combine( "data", "code", "fastpath", $"{Name}.{version}.cs" ) );
await using ( var dstStream = FileSystem.OpenWrite( $"{Name}.cs" ) )
await using ( var srcStream = srcFile.OpenRead() )
{
await srcStream.CopyToAsync( dstStream );
}
Compiler.MarkForRecompile();
Assert.IsTrue( Compiler.NeedsBuild );
Assert.IsFalse( compileSuccessCallback );
await Group.BuildAsync();
Assert.IsTrue( compileSuccessCallback );
Assert.IsFalse( Compiler.NeedsBuild );
Assert.IsNotNull( Group.BuildResult );
Assert.IsTrue( Group.BuildResult.Success, Group.BuildResult.BuildDiagnosticsString() );
Assert.AreEqual( Group.BuildResult.Output.Count(), 1 );
var output = Group.BuildResult.Output.First();
var asm = Assembly.Load( output.AssemblyData );
var supported = ILHotload.TryFindChangedMethods( LastResult?.Assembly, LastResult?.MethodBodyAssembly, asm,
out var changes, out var unexpected, out var hasAttribute );
var result = new BuildResult( asm, asm, supported, hasAttribute, changes );
Console.WriteLine( $"Built VERSION {version}:" );
Console.WriteLine( $" ILHotloadSupported: {result.ILHotloadSupported}" );
Console.WriteLine( $" HasSupportedAttribute: {result.HasSupportedAttribute}" );
Console.WriteLine( $" ChangedMethods: {(result.ChangedMethods.Length == 0 ? "None" : "")}" );
foreach ( var (oldMethod, newMethod) in result.ChangedMethods )
{
Console.WriteLine( $" {newMethod.ToSimpleString()}" );
}
Console.WriteLine( $" UnexpectedChanges: {(unexpected.Length == 0 ? "None" : "")}" );
foreach ( var change in unexpected )
{
Console.WriteLine( $" {change.ToSimpleString()}" );
}
ILHotload.IgnoreAttachedDebugger = true;
try
{
if ( LastResult is null )
{
return result;
}
if ( Hotload.Replace( LastResult.Assembly, LastResult.MethodBodyAssembly, result.Assembly ) )
{
Assert.IsTrue( result.ILHotloadSupported );
Assert.IsFalse( expectNotSupported );
result = result with { Assembly = LastResult.Assembly };
}
else
{
if ( result.ILHotloadSupported && System.Diagnostics.Debugger.IsAttached )
{
throw new Exception( "Can't perform ILHotload while debugging" );
}
if ( !expectNotSupported )
{
Assert.IsFalse( result.ILHotloadSupported );
}
}
return result;
}
finally
{
LastResult = result;
}
}
public void Dispose()
{
Compiler.Dispose();
Group.Dispose();
}
}
public partial class FastPathTest
{
private static int TestProgram( IProgram program, IEnumerable<string> expectedOutputLines )
{
return TestProgram( program, string.Join( "", expectedOutputLines.Select( x => $"{x}{Environment.NewLine}" ) ) );
}
private static int TestProgram( IProgram program, params string[] expectedOutputLines )
{
return TestProgram( program, expectedOutputLines.AsEnumerable() );
}
private static int TestProgram( IProgram program, string expectedOutput = "" )
{
var writer = new StringWriter();
try
{
var exitCode = program.Main( writer );
Console.WriteLine( $"Exit code: {exitCode}" );
Assert.AreEqual( expectedOutput, writer.ToString() );
return exitCode;
}
finally
{
Console.WriteLine( "Output:" );
foreach ( var line in writer.ToString().Split( Environment.NewLine ) )
{
Console.WriteLine( $" {line}" );
}
}
}
}