@@ -65,6 +65,7 @@ const CFT_MACOS_APP_NAME: &str =
6565 "Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing" ;
6666const MIN_CHROME_VERSION_CFT : i32 = 113 ;
6767const MIN_CHROMEDRIVER_VERSION_CFT : i32 = 115 ;
68+ const MIN_CHROME_VERSION_LINUX_ARM64 : i32 = 153 ;
6869const CHROMIUM_SNAP_LINK : & str = "/snap/bin/chromium" ;
6970const CHROMIUM_SNAP_BINARY : & str = "/snap/chromium/current/usr/lib/chromium-browser/chrome" ;
7071
@@ -132,6 +133,23 @@ impl ChromeManager {
132133 self . create_cft_url ( & self . get_driver_mirror_url_or_default ( CFT_URL ) , endpoint)
133134 }
134135
136+ fn find_platform_url ( & self , downloads : & [ PlatformUrl ] ) -> Option < String > {
137+ downloads
138+ . iter ( )
139+ . find ( |p| p. platform . eq_ignore_ascii_case ( self . get_platform_label ( ) ) )
140+ . map ( |p| p. url . to_string ( ) )
141+ }
142+
143+ // CfT publishes Linux arm64 binaries later than the rest of the platforms, so the minimum
144+ // version available for download is higher there than the general CfT minimum.
145+ fn min_version_for_platform ( & self , min_cft_version : i32 ) -> i32 {
146+ if LINUX . is ( self . get_os ( ) ) && ARM64 . is ( self . get_arch ( ) ) {
147+ MIN_CHROME_VERSION_LINUX_ARM64 . max ( min_cft_version)
148+ } else {
149+ min_cft_version
150+ }
151+ }
152+
135153 fn request_driver_version_from_latest ( & self , driver_url : & str ) -> Result < String , Error > {
136154 self . log . debug ( format ! (
137155 "Reading {} version from {}" ,
@@ -169,18 +187,16 @@ impl ChromeManager {
169187 return self . request_driver_version_from_latest ( & self . create_latest_release_url ( ) ) ;
170188 }
171189
172- let platform_url: Vec < & PlatformUrl > = chromedriver
173- . as_ref ( )
174- . unwrap ( )
175- . iter ( )
176- . filter ( |p| p. platform . eq_ignore_ascii_case ( self . get_platform_label ( ) ) )
177- . collect ( ) ;
190+ let platform_url = self . find_platform_url ( chromedriver. as_ref ( ) . unwrap ( ) ) ;
178191 self . log . trace ( format ! (
179- "CfT URLs for downloading {}: {:?}" ,
192+ "CfT URL for downloading {}: {:?}" ,
180193 self . get_driver_name( ) ,
181194 platform_url
182195 ) ) ;
183- self . driver_url = Some ( platform_url. first ( ) . unwrap ( ) . url . to_string ( ) ) ;
196+ match platform_url {
197+ Some ( url) => self . driver_url = Some ( url) ,
198+ None => return self . unavailable_discovery ( ) ,
199+ }
184200
185201 Ok ( stable_channel. version )
186202 }
@@ -203,10 +219,19 @@ impl ChromeManager {
203219 let good_versions_url = self . create_cft_url_for_drivers ( GOOD_VERSIONS_ENDPOINT ) ;
204220 let all_versions =
205221 self . request_versions_from_online :: < VersionsWithDownloads > ( & good_versions_url) ?;
222+ // Not every version is published for every platform, so the availability of a download
223+ // for the current platform is part of the filter (and not a later check) to let the most
224+ // recent usable version win
206225 let filtered_versions: Vec < Version > = all_versions
207226 . versions
208227 . into_iter ( )
209- . filter ( |r| r. version . starts_with ( version_for_filtering. as_str ( ) ) )
228+ . filter ( |r| {
229+ r. version . starts_with ( version_for_filtering. as_str ( ) )
230+ && r. downloads
231+ . chromedriver
232+ . as_deref ( )
233+ . is_some_and ( |d| self . find_platform_url ( d) . is_some ( ) )
234+ } )
210235 . collect ( ) ;
211236 if filtered_versions. is_empty ( ) {
212237 return Err ( anyhow ! ( format!(
@@ -216,23 +241,25 @@ impl ChromeManager {
216241 self . get_driver_name( ) ,
217242 version_for_filtering. as_str( ) ,
218243 self . get_arch( ) ,
219- & MIN_CHROMEDRIVER_VERSION_CFT . to_string( ) ,
244+ & self
245+ . min_version_for_platform( MIN_CHROMEDRIVER_VERSION_CFT )
246+ . to_string( ) ,
220247 ) ,
221248 CFT_URL
222249 ) ) ) ;
223250 }
224251
225252 let driver_version = filtered_versions. last ( ) . unwrap ( ) ;
226- let url: Vec < & PlatformUrl > = driver_version
253+ let url = driver_version
227254 . downloads
228255 . chromedriver
229- . as_ref ( )
230- . unwrap ( )
231- . iter ( )
232- . filter ( |p| p . platform . eq_ignore_ascii_case ( self . get_platform_label ( ) ) )
233- . collect ( ) ;
234- self . log . trace ( format ! ( "URLs for CfT: {:?}" , url ) ) ;
235- self . driver_url = Some ( url . first ( ) . unwrap ( ) . url . to_string ( ) ) ;
256+ . as_deref ( )
257+ . and_then ( |d| self . find_platform_url ( d ) ) ;
258+ self . log . trace ( format ! ( "URL for CfT: {:?}" , url ) ) ;
259+ match url {
260+ Some ( url ) => self . driver_url = Some ( url ) ,
261+ None => return self . unavailable_discovery ( ) ,
262+ }
236263
237264 Ok ( driver_version. version . to_string ( ) )
238265 }
@@ -420,9 +447,12 @@ impl SeleniumManager for ChromeManager {
420447 "mac64"
421448 }
422449 } else if LINUX . is ( os) && ARM64 . is ( arch) {
423- return Err ( anyhow ! (
424- "Linux arm64 is not supported yet by Google Chrome. Please try another browser."
425- ) ) ;
450+ // This URL scheme only serves chromedriver 114-, which was never published for Linux
451+ // arm64. Newer versions are served by the CfT endpoints and never reach this point.
452+ return Err ( anyhow ! ( format!(
453+ "Linux arm64 requires {} {} or above" ,
454+ self . driver_name, MIN_CHROME_VERSION_LINUX_ARM64
455+ ) ) ) ;
426456 } else {
427457 "linux64"
428458 } ;
@@ -484,6 +514,8 @@ impl SeleniumManager for ChromeManager {
484514 } else {
485515 "mac-x64"
486516 }
517+ } else if ARM64 . is ( arch) {
518+ "linux-arm64"
487519 } else {
488520 "linux64"
489521 }
@@ -494,14 +526,6 @@ impl SeleniumManager for ChromeManager {
494526 _browser_version : & str ,
495527 ) -> Result < String , Error > {
496528 let browser_name = self . browser_name ;
497- let os = self . get_os ( ) ;
498- let arch = self . get_arch ( ) ;
499- if LINUX . is ( os) && ARM64 . is ( arch) {
500- return Err ( anyhow ! ( format!(
501- "Linux arm64 is not supported yet by {}. Please try another browser." ,
502- browser_name
503- ) ) ) ;
504- }
505529 self . get_logger ( ) . trace ( format ! (
506530 "Using Chrome for Testing (CfT) endpoints to find out latest stable {} version" ,
507531 browser_name
@@ -511,29 +535,27 @@ impl SeleniumManager for ChromeManager {
511535 let versions_with_downloads =
512536 self . request_versions_from_online :: < LatestVersionsWithDownloads > ( & latest_versions_url) ?;
513537 let stable_channel = versions_with_downloads. channels . stable ;
514- let chrome = stable_channel. downloads . chrome ;
515538
516- let platform_url: Vec < & PlatformUrl > = chrome
517- . iter ( )
518- . filter ( |p| p. platform . eq_ignore_ascii_case ( self . get_platform_label ( ) ) )
519- . collect ( ) ;
539+ let platform_url = self . find_platform_url ( & stable_channel. downloads . chrome ) ;
520540 self . log . trace ( format ! (
521- "CfT URLs for downloading {}: {:?}" ,
541+ "CfT URL for downloading {}: {:?}" ,
522542 self . get_browser_name( ) ,
523543 platform_url
524544 ) ) ;
525- let browser_version = stable_channel. version ;
526- self . browser_url = Some ( platform_url. first ( ) . unwrap ( ) . url . to_string ( ) ) ;
545+ match platform_url {
546+ Some ( url) => self . browser_url = Some ( url) ,
547+ None => return self . unavailable_discovery ( ) ,
548+ }
527549
528- Ok ( browser_version )
550+ Ok ( stable_channel . version )
529551 }
530552
531553 fn request_fixed_browser_version_from_online (
532554 & mut self ,
533555 _browser_version : & str ,
534556 ) -> Result < String , Error > {
535557 let browser_name = self . browser_name ;
536- let mut browser_version = self . get_browser_version ( ) . to_string ( ) ;
558+ let browser_version = self . get_browser_version ( ) . to_string ( ) ;
537559 let major_browser_version = self . get_major_browser_version ( ) ;
538560 self . get_logger ( ) . trace ( format ! (
539561 "Using Chrome for Testing (CfT) endpoints to find out {} {}" ,
@@ -553,48 +575,50 @@ impl SeleniumManager for ChromeManager {
553575 } else {
554576 versions_with_downloads. channels . canary
555577 } ;
556- browser_version = channel. version ;
557- let platform_url: Vec < & PlatformUrl > = channel
558- . downloads
559- . chrome
560- . iter ( )
561- . filter ( |p| p. platform . eq_ignore_ascii_case ( self . get_platform_label ( ) ) )
562- . collect ( ) ;
563- self . browser_url = Some ( platform_url. first ( ) . unwrap ( ) . url . to_string ( ) ) ;
578+ let platform_url = self . find_platform_url ( & channel. downloads . chrome ) ;
579+ match platform_url {
580+ Some ( url) => self . browser_url = Some ( url) ,
581+ None => return self . unavailable_discovery ( ) ,
582+ }
564583
565- Ok ( browser_version )
584+ Ok ( channel . version )
566585 } else {
567586 let good_versions_url = self . create_cft_url_for_browsers ( GOOD_VERSIONS_ENDPOINT ) ;
568587 let all_versions =
569588 self . request_versions_from_online :: < VersionsWithDownloads > ( & good_versions_url) ?;
570- let iter_versions = all_versions. versions . into_iter ( ) ;
571- let filtered_versions: Vec < Version > = if self . is_browser_version_specific ( ) {
572- iter_versions
573- . filter ( |r| r. version . eq ( browser_version. as_str ( ) ) )
574- . collect ( )
575- } else {
576- iter_versions
577- . filter ( |r| r. version . starts_with ( major_browser_version. as_str ( ) ) )
578- . collect ( )
589+ let version_matches = |r : & Version | {
590+ if self . is_browser_version_specific ( ) {
591+ r. version . eq ( browser_version. as_str ( ) )
592+ } else {
593+ r. version . starts_with ( major_browser_version. as_str ( ) )
594+ }
579595 } ;
596+ // Not every version is published for every platform, so the availability of a download
597+ // for the current platform is part of the filter (and not a later check) to let the
598+ // most recent usable version win
599+ let filtered_versions: Vec < Version > = all_versions
600+ . versions
601+ . into_iter ( )
602+ . filter ( |r| {
603+ version_matches ( r) && self . find_platform_url ( & r. downloads . chrome ) . is_some ( )
604+ } )
605+ . collect ( ) ;
580606 if filtered_versions. is_empty ( ) {
581607 return self . unavailable_download ( ) ;
582608 }
583609 let last_browser = filtered_versions. last ( ) . unwrap ( ) ;
584- let platform_url: Vec < & PlatformUrl > = last_browser
585- . downloads
586- . chrome
587- . iter ( )
588- . filter ( |p| p. platform . eq_ignore_ascii_case ( self . get_platform_label ( ) ) )
589- . collect ( ) ;
590- self . browser_url = Some ( platform_url. first ( ) . unwrap ( ) . url . to_string ( ) ) ;
610+ let platform_url = self . find_platform_url ( & last_browser. downloads . chrome ) ;
611+ match platform_url {
612+ Some ( url) => self . browser_url = Some ( url) ,
613+ None => return self . unavailable_discovery ( ) ,
614+ }
591615
592616 Ok ( last_browser. version . to_string ( ) )
593617 }
594618 }
595619
596620 fn get_min_browser_version_for_download ( & self ) -> Result < i32 , Error > {
597- Ok ( MIN_CHROME_VERSION_CFT )
621+ Ok ( self . min_version_for_platform ( MIN_CHROME_VERSION_CFT ) )
598622 }
599623
600624 fn get_browser_binary_path ( & mut self , _browser_version : & str ) -> Result < PathBuf , Error > {
0 commit comments