22
22
using System . Collections . Generic ;
23
23
using System . Diagnostics ;
24
24
using System . IO ;
25
+ using System . Linq ;
25
26
using System . Runtime . InteropServices ;
26
27
using System . Text ;
27
28
using System . Text . Json ;
@@ -41,56 +42,120 @@ public static class SeleniumManager
41
42
42
43
private static readonly ILogger _logger = Log . GetLogger ( typeof ( SeleniumManager ) ) ;
43
44
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.
44
48
private static readonly Lazy < string > _lazyBinaryFullPath = new ( ( ) =>
45
49
{
46
50
string ? binaryFullPath = Environment . GetEnvironmentVariable ( "SE_MANAGER_PATH" ) ;
47
- if ( binaryFullPath == null )
48
- {
49
- SupportedPlatform ? platform = null ;
50
51
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 ) )
61
55
{
62
- platform = SupportedPlatform . MacOS ;
56
+ throw new FileNotFoundException ( $ "Unable to locate provided Selenium Manager binary at ' { binaryFullPath } '." ) ;
63
57
}
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
+ }
64
77
#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
+ }
77
90
#endif
78
91
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 ;
80
102
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 )
82
110
{
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
+ }
89
121
}
90
122
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 )
92
129
{
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 ( ) ) ;
94
159
}
95
160
96
161
return binaryFullPath ;
@@ -113,7 +178,7 @@ public static Dictionary<string, string> BinaryPaths(string arguments)
113
178
argsBuilder . Append ( " --debug" ) ;
114
179
}
115
180
116
- var smCommandResult = RunCommand ( _lazyBinaryFullPath . Value , argsBuilder . ToString ( ) ) ;
181
+ var smCommandResult = RunCommand ( argsBuilder . ToString ( ) ) ;
117
182
Dictionary < string , string > binaryPaths = new ( )
118
183
{
119
184
{ BrowserPathKey , smCommandResult . BrowserPath } ,
@@ -132,12 +197,11 @@ public static Dictionary<string, string> BinaryPaths(string arguments)
132
197
/// <summary>
133
198
/// Executes a process with the given arguments.
134
199
/// </summary>
135
- /// <param name="fileName">The path to the Selenium Manager.</param>
136
200
/// <param name="arguments">The switches to be used by Selenium Manager.</param>
137
201
/// <returns>
138
202
/// the standard output of the execution.
139
203
/// </returns>
140
- private static ResultResponse RunCommand ( string fileName , string arguments )
204
+ private static ResultResponse RunCommand ( string arguments )
141
205
{
142
206
Process process = new Process ( ) ;
143
207
process . StartInfo . FileName = _lazyBinaryFullPath . Value ;
@@ -171,7 +235,7 @@ private static ResultResponse RunCommand(string fileName, string arguments)
171
235
{
172
236
// 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
173
237
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 } ") ;
175
239
176
240
if ( ! string . IsNullOrWhiteSpace ( errorOutputBuilder . ToString ( ) ) )
177
241
{
@@ -194,7 +258,7 @@ private static ResultResponse RunCommand(string fileName, string arguments)
194
258
}
195
259
catch ( Exception ex )
196
260
{
197
- throw new WebDriverException ( $ "Error starting process: { fileName } { arguments } ", ex ) ;
261
+ throw new WebDriverException ( $ "Error starting process: { process . StartInfo . FileName } { arguments } ", ex ) ;
198
262
}
199
263
finally
200
264
{
0 commit comments