Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourceVersion)" />
</ItemGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);NU1510</NoWarn> <!-- NU1510: Project is explicitly referencing the runtime assembly 'System.Collections.Immutable', however, if we remove it, it tries to find it on the wrong path. Also, local NoWarn does not help - This is just me trying to enforce it -->
<NoWarn>$(NoWarn);NU1510;44</NoWarn> <!-- NU1510: Project is explicitly referencing the runtime assembly 'System.Collections.Immutable', however, if we remove it, it tries to find it on the wrong path. Also, local NoWarn does not help - This is just me trying to enforce it -->
<!-- 44: AssemblyName.CodeBase is deprecated but needed for assembly resolution in VS integration tests -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" GeneratePathProperty="true" />
Expand Down
52 changes: 52 additions & 0 deletions tests/FSharp.Test.Utilities/VSInstallDiscovery.fs
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,55 @@ module VSInstallDiscovery =
| NotFound reason ->
logAction $"Visual Studio installation not found: {reason}"
None

/// Gets the VS installation directory or fails with a detailed error message.
/// This is the recommended method for test scenarios that require VS to be installed.
let getVSInstallDirOrFail () : string =
match tryFindVSInstallation () with
| Found (path, _) -> path
| NotFound reason ->
failwith $"Visual Studio installation not found: {reason}. Ensure VS is installed or environment variables (VSAPPIDDIR, VS*COMNTOOLS) are set."

/// Assembly resolver for Visual Studio test infrastructure.
/// Provides centralized assembly resolution for VS integration tests.
module VSAssemblyResolver =
open System
open System.IO
open System.Reflection
open System.Globalization

/// Adds an assembly resolver that probes Visual Studio installation directories.
/// This should be called early in test initialization to ensure VS assemblies can be loaded.
let addResolver () =
let vsInstallDir = VSInstallDiscovery.getVSInstallDirOrFail ()

let probingPaths =
[|
Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\Editor")
Path.Combine(vsInstallDir, @"IDE\PublicAssemblies")
Path.Combine(vsInstallDir, @"IDE\PrivateAssemblies")
Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\ManagedLanguages\VBCSharp\LanguageServices")
Path.Combine(vsInstallDir, @"IDE\Extensions\Microsoft\CodeSense\Framework")
Path.Combine(vsInstallDir, @"IDE")
|]

AppDomain.CurrentDomain.add_AssemblyResolve(fun _ args ->
let found () =
probingPaths
|> Seq.tryPick (fun p ->
try
let name = AssemblyName(args.Name)
let codebase = Path.GetFullPath(Path.Combine(p, name.Name) + ".dll")
if File.Exists(codebase) then
name.CodeBase <- codebase
name.CultureInfo <- Unchecked.defaultof<CultureInfo>
name.Version <- Unchecked.defaultof<Version>
Some name
else
None
with _ ->
None)

match found () with
| None -> Unchecked.defaultof<Assembly>
| Some name -> Assembly.Load(name))
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,9 @@

namespace FSharp.Editor.Tests.Helpers

open System
open System.IO
open System.Reflection

module AssemblyResolver =
open System.Globalization
open FSharp.Test.VSInstallDiscovery

let vsInstallDir =
// Use centralized VS installation discovery with graceful fallback
match tryGetVSInstallDir () with
| Some dir -> dir
| None ->
// Fallback to legacy behavior for backward compatibility
let vsvar =
let var = Environment.GetEnvironmentVariable("VS170COMNTOOLS")

if String.IsNullOrEmpty var then
Environment.GetEnvironmentVariable("VSAPPIDDIR")
else
var

if String.IsNullOrEmpty vsvar then
failwith "VS170COMNTOOLS and VSAPPIDDIR environment variables not found."

Path.Combine(vsvar, "..")

let probingPaths =
[|
Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\Editor")
Path.Combine(vsInstallDir, @"IDE\PublicAssemblies")
Path.Combine(vsInstallDir, @"IDE\PrivateAssemblies")
Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\ManagedLanguages\VBCSharp\LanguageServices")
Path.Combine(vsInstallDir, @"IDE\Extensions\Microsoft\CodeSense\Framework")
Path.Combine(vsInstallDir, @"IDE")
|]

let addResolver () =
AppDomain.CurrentDomain.add_AssemblyResolve (fun h args ->
let found () =
(probingPaths)
|> Seq.tryPick (fun p ->
try
let name = AssemblyName(args.Name)
let codebase = Path.GetFullPath(Path.Combine(p, name.Name) + ".dll")

if File.Exists(codebase) then
name.CodeBase <- codebase
name.CultureInfo <- Unchecked.defaultof<CultureInfo>
name.Version <- Unchecked.defaultof<Version>
Some(name)
else
None
with _ ->
None)
open FSharp.Test.VSAssemblyResolver

match found () with
| None -> Unchecked.defaultof<Assembly>
| Some name -> Assembly.Load(name))
/// Adds an assembly resolver that probes Visual Studio installation directories.
/// This is a compatibility shim that delegates to the centralized implementation.
let addResolver = addResolver
3 changes: 3 additions & 0 deletions vsintegration/tests/Salsa/VisualFSharp.Salsa.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
<Compile Include="..\unittests\TestLib.Utils.fs">
<Link>UnitTests.TestLib.Utils.fs</Link>
</Compile>
<Compile Include="..\..\..\tests\FSharp.Test.Utilities\VSInstallDiscovery.fs">
<Link>VSInstallDiscovery.fs</Link>
</Compile>
<Compile Include="FSharpLanguageServiceTestable.fs" />
<Compile Include="VsMocks.fs" />
<Compile Include="Salsa.fs" />
Expand Down
18 changes: 2 additions & 16 deletions vsintegration/tests/Salsa/VsMocks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,7 @@ module internal VsActual =
open System.ComponentModel.Composition.Primitives
open Microsoft.VisualStudio.Text
open Microsoft.VisualStudio.Threading
open FSharp.Test.VSInstallDiscovery

type TestExportJoinableTaskContext () =

Expand All @@ -1650,22 +1651,7 @@ module internal VsActual =
[<System.ComponentModel.Composition.Export(typeof<JoinableTaskContext>)>]
member public _.JoinableTaskContext : JoinableTaskContext = jtc

let vsInstallDir =
// use the environment variable to find the VS installdir
let vsvar =
// Try VS180COMNTOOLS first, then VS170COMNTOOLS, then VSAPPIDDIR
// TODO : use tryGetVSInstallDir from test utils instead
let var18 = Environment.GetEnvironmentVariable("VS180COMNTOOLS")
if String.IsNullOrEmpty var18 then
let var17 = Environment.GetEnvironmentVariable("VS170COMNTOOLS")
if String.IsNullOrEmpty var17 then
Environment.GetEnvironmentVariable("VSAPPIDDIR")
else
var17
else
var18
if String.IsNullOrEmpty vsvar then failwith "VS180COMNTOOLS, VS170COMNTOOLS and VSAPPIDDIR environment variables not found."
Path.Combine(vsvar, "..")
let vsInstallDir = getVSInstallDirOrFail ()

let CreateEditorCatalog() =
let thisAssembly = Assembly.GetExecutingAssembly().Location
Expand Down
52 changes: 4 additions & 48 deletions vsintegration/tests/UnitTests/AssemblyResolver.fs
Original file line number Diff line number Diff line change
@@ -1,52 +1,8 @@
namespace Microsoft.VisualStudio.FSharp

open System
open System.IO
open System.Reflection

module AssemblyResolver =
open System.Globalization
open FSharp.Test.VSInstallDiscovery

let vsInstallDir =
// Use centralized VS installation discovery with graceful fallback
match tryGetVSInstallDir () with
| Some dir -> dir
| None ->
// Fallback to legacy behavior for backward compatibility
let vsvar =
let var = Environment.GetEnvironmentVariable("VS170COMNTOOLS")
if String.IsNullOrEmpty var then
Environment.GetEnvironmentVariable("VSAPPIDDIR")
else
var
if String.IsNullOrEmpty vsvar then failwith "VS170COMNTOOLS and VSAPPIDDIR environment variables not found."
Path.Combine(vsvar, "..")

let probingPaths = [|
Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\Editor")
Path.Combine(vsInstallDir, @"IDE\PublicAssemblies")
Path.Combine(vsInstallDir, @"IDE\PrivateAssemblies")
Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\ManagedLanguages\VBCSharp\LanguageServices")
Path.Combine(vsInstallDir, @"IDE\Extensions\Microsoft\CodeSense\Framework")
Path.Combine(vsInstallDir, @"IDE")
|]
open FSharp.Test.VSAssemblyResolver

let addResolver () =
AppDomain.CurrentDomain.add_AssemblyResolve(fun h args ->
let found () =
(probingPaths ) |> Seq.tryPick(fun p ->
try
let name = AssemblyName(args.Name)
let codebase = Path.GetFullPath(Path.Combine(p, name.Name) + ".dll")
if File.Exists(codebase) then
name.CodeBase <- codebase
name.CultureInfo <- Unchecked.defaultof<CultureInfo>
name.Version <- Unchecked.defaultof<Version>
Some (name)
else None
with | _ -> None
)
match found() with
| None -> Unchecked.defaultof<Assembly>
| Some name -> Assembly.Load(name) )
/// Adds an assembly resolver that probes Visual Studio installation directories.
/// This is a compatibility shim that delegates to the centralized implementation.
let addResolver = addResolver
Loading