2222using System . Collections . Generic ;
2323using System . Diagnostics ;
2424using System . IO ;
25+ using System . Linq ;
2526using System . Runtime . InteropServices ;
2627using System . Text ;
2728using System . Text . Json ;
@@ -41,56 +42,120 @@ public static class SeleniumManager
4142
4243 private static readonly ILogger _logger = Log . GetLogger ( typeof ( SeleniumManager ) ) ;
4344
45+ // This logic to find Selenium Manager binary is complex and strange.
46+ // As soon as Selenium Manager will be real native library (dll ,so, dynlib),
47+ // we will be able to use it directly from the .NET bindings, and this logic will be removed.
4448 private static readonly Lazy < string > _lazyBinaryFullPath = new ( ( ) =>
4549 {
4650 string ? binaryFullPath = Environment . GetEnvironmentVariable ( "SE_MANAGER_PATH" ) ;
47- if ( binaryFullPath == null )
48- {
49- SupportedPlatform ? platform = null ;
5051
51- #if NET8_0_OR_GREATER
52- if ( OperatingSystem . IsWindows ( ) )
53- {
54- platform = SupportedPlatform . Windows ;
55- }
56- else if ( OperatingSystem . IsLinux ( ) || OperatingSystem . IsFreeBSD ( ) )
57- {
58- platform = SupportedPlatform . Linux ;
59- }
60- else if ( OperatingSystem . IsMacOS ( ) || OperatingSystem . IsMacCatalyst ( ) )
52+ if ( binaryFullPath is not null )
53+ {
54+ if ( ! File . Exists ( binaryFullPath ) )
6155 {
62- platform = SupportedPlatform . MacOS ;
56+ throw new FileNotFoundException ( $ "Unable to locate provided Selenium Manager binary at ' { binaryFullPath } '." ) ;
6357 }
58+
59+ return binaryFullPath ;
60+ }
61+
62+ SupportedPlatform ? platform = null ;
63+
64+ #if NET8_0_OR_GREATER
65+ if ( OperatingSystem . IsWindows ( ) )
66+ {
67+ platform = SupportedPlatform . Windows ;
68+ }
69+ else if ( OperatingSystem . IsLinux ( ) || OperatingSystem . IsFreeBSD ( ) )
70+ {
71+ platform = SupportedPlatform . Linux ;
72+ }
73+ else if ( OperatingSystem . IsMacOS ( ) || OperatingSystem . IsMacCatalyst ( ) )
74+ {
75+ platform = SupportedPlatform . MacOS ;
76+ }
6477#else
65- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
66- {
67- platform = SupportedPlatform . Windows ;
68- }
69- else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
70- {
71- platform = SupportedPlatform . Linux ;
72- }
73- else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
74- {
75- platform = SupportedPlatform . MacOS ;
76- }
78+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
79+ {
80+ platform = SupportedPlatform . Windows ;
81+ }
82+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
83+ {
84+ platform = SupportedPlatform . Linux ;
85+ }
86+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
87+ {
88+ platform = SupportedPlatform . MacOS ;
89+ }
7790#endif
7891
79- var currentDirectory = AppContext . BaseDirectory ;
92+ var seleniumManagerFileName = platform switch
93+ {
94+ SupportedPlatform . Windows => "selenium-manager.exe" ,
95+ SupportedPlatform . Linux => "selenium-manager" ,
96+ SupportedPlatform . MacOS => "selenium-manager" ,
97+ _ => throw new PlatformNotSupportedException (
98+ $ "Selenium Manager doesn't support your runtime platform: { RuntimeInformation . OSDescription } ") ,
99+ } ;
100+
101+ var baseDirectory = AppContext . BaseDirectory ;
80102
81- binaryFullPath = platform switch
103+ List < string > probingPaths = [ ] ;
104+
105+ if ( baseDirectory is not null )
106+ {
107+ probingPaths . Add ( Path . Combine ( baseDirectory , seleniumManagerFileName ) ) ;
108+
109+ switch ( platform )
82110 {
83- SupportedPlatform . Windows => Path . Combine ( currentDirectory , "selenium-manager" , "windows" , "selenium-manager.exe" ) ,
84- SupportedPlatform . Linux => Path . Combine ( currentDirectory , "selenium-manager" , "linux" , "selenium-manager" ) ,
85- SupportedPlatform . MacOS => Path . Combine ( currentDirectory , "selenium-manager" , "macos" , "selenium-manager" ) ,
86- _ => throw new PlatformNotSupportedException (
87- $ "Selenium Manager doesn't support your runtime platform: { RuntimeInformation . OSDescription } ") ,
88- } ;
111+ case SupportedPlatform . Windows :
112+ probingPaths . Add ( Path . Combine ( baseDirectory , "runtimes" , "win" , "native" , seleniumManagerFileName ) ) ;
113+ break ;
114+ case SupportedPlatform . Linux :
115+ probingPaths . Add ( Path . Combine ( baseDirectory , "runtimes" , "linux" , "native" , seleniumManagerFileName ) ) ;
116+ break ;
117+ case SupportedPlatform . MacOS :
118+ probingPaths . Add ( Path . Combine ( baseDirectory , "runtimes" , "osx" , "native" , seleniumManagerFileName ) ) ;
119+ break ;
120+ }
89121 }
90122
91- if ( ! File . Exists ( binaryFullPath ) )
123+ // Supporting .NET5+ applications deployed as bundled applications (single file or AOT).
124+ // In this case bootstrapper extracts the native libraries into a temporary directory.
125+ // Most interesting build properties: "IncludeNativeLibrariesForSelfExtract" and "IncludeAllContentForSelfExtract".
126+ var nativeDllSearchDirectories = AppContext . GetData ( "NATIVE_DLL_SEARCH_DIRECTORIES" ) ? . ToString ( ) ;
127+
128+ if ( nativeDllSearchDirectories is not null )
92129 {
93- throw new WebDriverException ( $ "Unable to locate or obtain Selenium Manager binary at { binaryFullPath } ") ;
130+ probingPaths . AddRange ( nativeDllSearchDirectories . Split ( new char [ ] { Path . PathSeparator } , StringSplitOptions . RemoveEmptyEntries ) . Select ( path => Path . Combine ( path , seleniumManagerFileName ) ) ) ;
131+ }
132+
133+ // Covering the case when the application is hosted by another application, most likely
134+ // we can find Selenium Manager in the assembly location, because "AppContext.BaseDirectory"
135+ // might return the path of the host application.
136+ var assemblyDirectory = Path . GetDirectoryName ( typeof ( SeleniumManager ) . Assembly . Location ) ;
137+
138+ if ( assemblyDirectory is not null )
139+ {
140+ probingPaths . Add ( Path . Combine ( assemblyDirectory , seleniumManagerFileName ) ) ;
141+ }
142+
143+ probingPaths = [ .. probingPaths . Distinct ( ) ] ;
144+
145+ binaryFullPath = probingPaths . FirstOrDefault ( File . Exists ) ;
146+
147+ if ( binaryFullPath is null )
148+ {
149+ var messageBuilder = new StringBuilder ( ) ;
150+ messageBuilder . AppendFormat ( "Selenium Manager binary '{0}' was not found in the following paths:" , seleniumManagerFileName ) ;
151+
152+ foreach ( var probingPath in probingPaths )
153+ {
154+ messageBuilder . AppendLine ( ) ;
155+ messageBuilder . AppendFormat ( " - {0}" , probingPath ) ;
156+ }
157+
158+ throw new FileNotFoundException ( messageBuilder . ToString ( ) ) ;
94159 }
95160
96161 return binaryFullPath ;
@@ -113,7 +178,7 @@ public static Dictionary<string, string> BinaryPaths(string arguments)
113178 argsBuilder . Append ( " --debug" ) ;
114179 }
115180
116- var smCommandResult = RunCommand ( _lazyBinaryFullPath . Value , argsBuilder . ToString ( ) ) ;
181+ var smCommandResult = RunCommand ( argsBuilder . ToString ( ) ) ;
117182 Dictionary < string , string > binaryPaths = new ( )
118183 {
119184 { BrowserPathKey , smCommandResult . BrowserPath } ,
@@ -132,12 +197,11 @@ public static Dictionary<string, string> BinaryPaths(string arguments)
132197 /// <summary>
133198 /// Executes a process with the given arguments.
134199 /// </summary>
135- /// <param name="fileName">The path to the Selenium Manager.</param>
136200 /// <param name="arguments">The switches to be used by Selenium Manager.</param>
137201 /// <returns>
138202 /// the standard output of the execution.
139203 /// </returns>
140- private static ResultResponse RunCommand ( string fileName , string arguments )
204+ private static ResultResponse RunCommand ( string arguments )
141205 {
142206 Process process = new Process ( ) ;
143207 process . StartInfo . FileName = _lazyBinaryFullPath . Value ;
@@ -171,7 +235,7 @@ private static ResultResponse RunCommand(string fileName, string arguments)
171235 {
172236 // We do not log any warnings coming from Selenium Manager like the other bindings, as we don't have any logging in the .NET bindings
173237
174- var exceptionMessageBuilder = new StringBuilder ( $ "Selenium Manager process exited abnormally with { process . ExitCode } code: { fileName } { arguments } ") ;
238+ var exceptionMessageBuilder = new StringBuilder ( $ "Selenium Manager process exited abnormally with { process . ExitCode } code: { process . StartInfo . FileName } { arguments } ") ;
175239
176240 if ( ! string . IsNullOrWhiteSpace ( errorOutputBuilder . ToString ( ) ) )
177241 {
@@ -194,7 +258,7 @@ private static ResultResponse RunCommand(string fileName, string arguments)
194258 }
195259 catch ( Exception ex )
196260 {
197- throw new WebDriverException ( $ "Error starting process: { fileName } { arguments } ", ex ) ;
261+ throw new WebDriverException ( $ "Error starting process: { process . StartInfo . FileName } { arguments } ", ex ) ;
198262 }
199263 finally
200264 {
0 commit comments