Skip to content
Draft
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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
Expand Up @@ -352,6 +352,7 @@
<Compile Include="FSharpChecker\SymbolUse.fs" />
<Compile Include="FSharpChecker\FindReferences.fs" />
<Compile Include="Attributes\AttributeCtorSetPropAccess.fs" />
<Compile Include="CompilerCompatibilityTests.fs" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions tests/projects/CompilerCompat/.gitignore
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
30 changes: 30 additions & 0 deletions tests/projects/CompilerCompat/CompilerCompatApp/AppBuildInfo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace CompilerCompatApp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
This is nice, but do not check it to the repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed AppBuildInfo.fs from the repository. It's now generated at build time only and excluded via .gitignore. Commit 8294e80


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
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>
50 changes: 50 additions & 0 deletions tests/projects/CompilerCompat/CompilerCompatApp/Program.fs
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>
30 changes: 30 additions & 0 deletions tests/projects/CompilerCompat/CompilerCompatLib/LibBuildInfo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace CompilerCompatLib

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
17 changes: 17 additions & 0 deletions tests/projects/CompilerCompat/CompilerCompatLib/Library.fs
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
Loading