Skip to content
8 changes: 8 additions & 0 deletions tests/FSharp.Test.Utilities/VSInstallDiscovery.fs
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,11 @@ 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."
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,7 @@ 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 vsInstallDir = getVSInstallDirOrFail ()

let probingPaths =
[|
Expand All @@ -40,9 +23,9 @@ module AssemblyResolver =
|]

let addResolver () =
AppDomain.CurrentDomain.add_AssemblyResolve (fun h args ->
AppDomain.CurrentDomain.add_AssemblyResolve (fun _ args ->
let found () =
(probingPaths)
probingPaths
|> Seq.tryPick (fun p ->
try
let name = AssemblyName(args.Name)
Expand All @@ -52,7 +35,7 @@ module AssemblyResolver =
name.CodeBase <- codebase
name.CultureInfo <- Unchecked.defaultof<CultureInfo>
name.Version <- Unchecked.defaultof<Version>
Some(name)
Some name
else
None
with _ ->
Expand Down
12 changes: 2 additions & 10 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,16 +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 =
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 vsInstallDir = getVSInstallDirOrFail ()

let CreateEditorCatalog() =
let thisAssembly = Assembly.GetExecutingAssembly().Location
Expand Down
51 changes: 21 additions & 30 deletions vsintegration/tests/UnitTests/AssemblyResolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,36 @@ 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 vsInstallDir = 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")
|]
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 ->
AppDomain.CurrentDomain.add_AssemblyResolve(fun _ args ->
let found () =
(probingPaths ) |> Seq.tryPick(fun p ->
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
Some name
else
None
with _ ->
None)

match found () with
| None -> Unchecked.defaultof<Assembly>
| Some name -> Assembly.Load(name) )
| Some name -> Assembly.Load(name))
Loading