-
Notifications
You must be signed in to change notification settings - Fork 829
Add compiler compatibility test suite for anonymous records with MSBuild-generated build verification #18913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 10 commits
c628564
e8d910f
bbefae5
13ca28d
c5a2700
360ae63
9ea30da
86a9207
2720a6f
91da72a
9846cef
194b5fe
8fc9d44
f3cd733
8294e80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
module FSharp.Compiler.ComponentTests.CompilerCompatibilityTests | ||
|
||
open System | ||
open System.IO | ||
open Xunit | ||
open FSharp.Test | ||
open FSharp.Test.Assert | ||
open TestFramework | ||
|
||
type CompilerCompatibilityTests() = | ||
|
||
let projectsPath = Path.GetFullPath(Path.Combine(__SOURCE_DIRECTORY__, "../projects/CompilerCompat")) | ||
let libProjectPath = Path.Combine(projectsPath, "CompilerCompatLib") | ||
let appProjectPath = Path.Combine(projectsPath, "CompilerCompatApp") | ||
|
||
let runDotnetBuild projectPath compilerVersion = | ||
let args = | ||
match compilerVersion with | ||
| "local" -> "build -c Release -p:LoadLocalFSharpBuild=True" | ||
| _ -> "build -c Release" | ||
|
||
let (exitCode, output, error) = Commands.executeProcess "dotnet" args projectPath | ||
|
||
if exitCode <> 0 then | ||
let outputStr = String.concat "\n" output | ||
let errorStr = String.concat "\n" error | ||
failwith $"Build failed with exit code {exitCode}. Output: {outputStr}. Error: {errorStr}" | ||
|
||
String.concat "\n" output | ||
|
||
let runApp appBinaryPath = | ||
let (exitCode, output, error) = Commands.executeProcess "dotnet" appBinaryPath (Path.GetDirectoryName(appBinaryPath)) | ||
(exitCode, String.concat "\n" output, String.concat "\n" error) | ||
|
||
let cleanBinObjDirectories projectPath = | ||
let binPath = Path.Combine(projectPath, "bin") | ||
let objPath = Path.Combine(projectPath, "obj") | ||
|
||
if Directory.Exists(binPath) then | ||
Directory.Delete(binPath, true) | ||
if Directory.Exists(objPath) then | ||
Directory.Delete(objPath, true) | ||
|
||
let getAppDllPath () = | ||
// The app is built to artifacts directory due to Directory.Build.props | ||
Path.Combine(__SOURCE_DIRECTORY__, "..", "..", "artifacts", "bin", "CompilerCompatApp", "Release", "net8.0", "CompilerCompatApp.dll") | ||
|
||
[<Theory>] | ||
[<InlineData("local", "local", "Baseline scenario - Both library and app built with local compiler")>] | ||
[<InlineData("latest", "local", "Forward compatibility - Library built with latest SDK, app with local compiler")>] | ||
[<InlineData("local", "latest", "Backward compatibility - Library built with local compiler, app with latest SDK")>] | ||
member _.``Compiler compatibility test``(libCompilerVersion: string, appCompilerVersion: string, scenarioDescription: string) = | ||
// Clean previous builds | ||
T-Gro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
cleanBinObjDirectories libProjectPath | ||
cleanBinObjDirectories appProjectPath | ||
|
||
// Build library with specified compiler version | ||
let libOutput = runDotnetBuild libProjectPath libCompilerVersion | ||
Assert.Contains("CompilerCompatLib -> ", libOutput) | ||
|
||
// Build app with specified compiler version | ||
let appOutput = runDotnetBuild appProjectPath appCompilerVersion | ||
T-Gro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Assert.Contains("CompilerCompatApp -> ", appOutput) | ||
|
||
// Run app and verify it works | ||
let appDllPath = getAppDllPath() | ||
Assert.True(File.Exists(appDllPath), $"App DLL not found at {appDllPath} for scenario: {scenarioDescription}") | ||
|
||
let (exitCode, output, _error) = runApp appDllPath | ||
Assert.Equal(0, exitCode) | ||
Assert.Contains("SUCCESS: All compiler compatibility tests passed", output) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Ignore test-generated files | ||
global.json | ||
commandline.txt | ||
StandardOutput.txt | ||
StandardError.txt |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
namespace CompilerCompatApp | ||
|
||
|
||
open System | ||
open System.Reflection | ||
open System.Diagnostics | ||
|
||
module AppBuildInfo = | ||
let sdkVersion = | ||
try | ||
// Try to get .NET version from runtime | ||
Environment.Version.ToString() | ||
with _ -> "Unknown" | ||
|
||
let fscPath = | ||
try | ||
// Check if we're using local compiler by looking at assembly location | ||
let assembly = Assembly.GetExecutingAssembly() | ||
let location = assembly.Location | ||
if location.Contains("artifacts") then | ||
$"Local compiler (artifacts): {location}" | ||
else | ||
$"SDK compiler: {location}" | ||
with _ -> "Unknown" | ||
|
||
let isLocalBuild = | ||
try | ||
// Additional check to see if we're using local build | ||
let assembly = Assembly.GetExecutingAssembly() | ||
assembly.Location.Contains("artifacts") | ||
with _ -> false | ||
T-Gro marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<DirectoryBuildPropsPath Condition="'$(LoadLocalFSharpBuild)' == 'True'">../../../../UseLocalCompiler.Directory.Build.props</DirectoryBuildPropsPath> | ||
<DirectoryBuildPropsPath Condition="'$(LoadLocalFSharpBuild)' != 'True'"></DirectoryBuildPropsPath> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="AppBuildInfo.fs" /> | ||
<Compile Include="Program.fs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="../CompilerCompatLib/CompilerCompatLib.fsproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
open CompilerCompatLib | ||
open CompilerCompatApp | ||
open System | ||
|
||
[<EntryPoint>] | ||
let main _argv = | ||
try | ||
// Print detailed build information to verify which compiler was used | ||
printfn "=== BUILD VERIFICATION ===" | ||
printfn "Library Build Info:" | ||
printfn " SDK Version: %s" LibBuildInfo.sdkVersion | ||
printfn " FSC Path: %s" LibBuildInfo.fscPath | ||
printfn " Is Local Build: %b" LibBuildInfo.isLocalBuild | ||
printfn "Application Build Info:" | ||
printfn " SDK Version: %s" AppBuildInfo.sdkVersion | ||
printfn " FSC Path: %s" AppBuildInfo.fscPath | ||
printfn " Is Local Build: %b" AppBuildInfo.isLocalBuild | ||
printfn "==========================" | ||
|
||
// Test basic anonymous record functionality | ||
let record = Library.getAnonymousRecord() | ||
printfn "Basic record: X=%d, Y=%s" record.X record.Y | ||
|
||
// Verify expected values | ||
if record.X <> 42 || record.Y <> "hello" then | ||
printfn "ERROR: Basic record values don't match expected" | ||
1 | ||
else | ||
printfn "SUCCESS: Basic record test passed" | ||
|
||
// Test complex anonymous record functionality | ||
let complex = Library.getComplexAnonymousRecord() | ||
printfn "Complex record: Simple.A=%d, Simple.B=%s" complex.Simple.A complex.Simple.B | ||
printfn "Complex record: List has %d items" complex.List.Length | ||
printfn "Complex record: Tuple=(%d, Value=%f)" (fst complex.Tuple) (snd complex.Tuple).Value | ||
|
||
// Test function that takes anonymous record | ||
let processed = Library.processAnonymousRecord({| X = 123; Y = "test" |}) | ||
printfn "Processed result: %s" processed | ||
|
||
if processed = "Processed: X=123, Y=test" then | ||
printfn "SUCCESS: All compiler compatibility tests passed" | ||
0 | ||
else | ||
printfn "ERROR: Processed result doesn't match expected" | ||
1 | ||
|
||
with ex -> | ||
printfn "ERROR: Exception occurred: %s" ex.Message | ||
1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<DirectoryBuildPropsPath Condition="'$(LoadLocalFSharpBuild)' == 'True'">../../../../UseLocalCompiler.Directory.Build.props</DirectoryBuildPropsPath> | ||
<DirectoryBuildPropsPath Condition="'$(LoadLocalFSharpBuild)' != 'True'"></DirectoryBuildPropsPath> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="LibBuildInfo.fs" /> | ||
<Compile Include="Library.fs" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
namespace CompilerCompatLib | ||
T-Gro marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
open System | ||
open System.Reflection | ||
open System.Diagnostics | ||
|
||
module LibBuildInfo = | ||
let sdkVersion = | ||
try | ||
// Try to get .NET version from runtime | ||
Environment.Version.ToString() | ||
with _ -> "Unknown" | ||
|
||
let fscPath = | ||
try | ||
// Check if we're using local compiler by looking at assembly location | ||
let assembly = Assembly.GetExecutingAssembly() | ||
let location = assembly.Location | ||
if location.Contains("artifacts") then | ||
$"Local compiler (artifacts): {location}" | ||
else | ||
$"SDK compiler: {location}" | ||
with _ -> "Unknown" | ||
|
||
let isLocalBuild = | ||
try | ||
// Additional check to see if we're using local build | ||
let assembly = Assembly.GetExecutingAssembly() | ||
assembly.Location.Contains("artifacts") | ||
with _ -> false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
namespace CompilerCompatLib | ||
|
||
module Library = | ||
/// Returns an anonymous record to test compiler compatibility | ||
let getAnonymousRecord () = {| X = 42; Y = "hello" |} | ||
|
||
/// Returns a more complex anonymous record with nested structure | ||
let getComplexAnonymousRecord () = | ||
{| | ||
Simple = {| A = 1; B = "test" |}; | ||
List = [ {| Id = 1; Name = "first" |}; {| Id = 2; Name = "second" |} ]; | ||
Tuple = (42, {| Value = 3.14; Label = "pi" |}) | ||
|} | ||
|
||
/// Function that takes an anonymous record as parameter | ||
let processAnonymousRecord (record: {| X: int; Y: string |}) = | ||
sprintf "Processed: X=%d, Y=%s" record.X record.Y |
Uh oh!
There was an error while loading. Please reload this page.