diff --git a/rust/src/chrome.rs b/rust/src/chrome.rs index 61224379a30f6..2c6583ba8442e 100644 --- a/rust/src/chrome.rs +++ b/rust/src/chrome.rs @@ -22,7 +22,8 @@ use crate::downloads::{parse_json_from_url, read_version_from_link}; use crate::files::{BrowserPath, compose_driver_path_in_cache}; use crate::logger::Logger; use crate::metadata::{ - create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, + create_driver_metadata, get_driver_version_from_metadata, get_metadata, + should_cache_driver_version, write_metadata, }; use crate::{ BETA, DASH_DASH_VERSION, DEV, NIGHTLY, OFFLINE_REQUEST_ERR_MSG, REG_VERSION_ARG, STABLE, @@ -339,7 +340,7 @@ impl SeleniumManager for ChromeManager { }; let driver_ttl = self.get_ttl(); - if driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty() + if should_cache_driver_version(driver_ttl, major_browser_version, &driver_version) { metadata.drivers.push(create_driver_metadata( major_browser_version, diff --git a/rust/src/config.rs b/rust/src/config.rs index 60973a0c7da9e..1cb274e8fab58 100644 --- a/rust/src/config.rs +++ b/rust/src/config.rs @@ -188,8 +188,8 @@ pub enum ARCH { impl ARCH { pub fn to_str_vector(&self) -> Vec<&str> { match self { - ARCH::X32 => vec![ARCH_X86, "i386", "x32"], - ARCH::X64 => vec![ARCH_X64, "amd64", "x64", "i686", "ia64"], + ARCH::X32 => vec![ARCH_X86, "i386", "x32", "i686"], + ARCH::X64 => vec![ARCH_X64, "amd64", "x64", "ia64"], ARCH::ARM64 => vec![ARCH_ARM64, "aarch64", "arm"], ARCH::ARMV7 => vec![ARCH_ARM7L, "armv7l"], } @@ -269,6 +269,31 @@ fn get_env_name(suffix: &str) -> String { concat(ENV_PREFIX, suffix_uppercase.as_str()) } +#[cfg(test)] +mod env_name_tests { + use super::*; + + #[test] + fn get_env_name_simple_key() { + assert_eq!(get_env_name("browser"), "SE_BROWSER"); + } + + #[test] + fn get_env_name_dashes_become_underscores() { + assert_eq!(get_env_name("browser-version"), "SE_BROWSER_VERSION"); + } + + #[test] + fn get_env_name_mixed_case_uppercased() { + assert_eq!(get_env_name("Cache-Path"), "SE_CACHE_PATH"); + } + + #[test] + fn get_env_name_empty_suffix() { + assert_eq!(get_env_name(""), "SE_"); + } +} + fn get_config() -> Result { let cache_path = read_cache_path(); let config_path = Path::new(&cache_path).to_path_buf().join(CONFIG_FILE); diff --git a/rust/src/edge.rs b/rust/src/edge.rs index 042f805244dd0..fb04e94f5f4cd 100644 --- a/rust/src/edge.rs +++ b/rust/src/edge.rs @@ -21,7 +21,8 @@ use crate::config::OS::{LINUX, MACOS, WINDOWS}; use crate::downloads::{parse_json_from_url, read_version_from_link}; use crate::files::{BrowserPath, compose_driver_path_in_cache}; use crate::metadata::{ - create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, + create_driver_metadata, get_driver_version_from_metadata, get_metadata, + should_cache_driver_version, write_metadata, }; use crate::{ BETA, DASH_DASH_VERSION, DEV, ENV_PROGRAM_FILES, ENV_PROGRAM_FILES_X86, Logger, NIGHTLY, @@ -252,7 +253,8 @@ impl SeleniumManager for EdgeManager { read_version_from_link(self.get_http_client(), &driver_url, self.get_logger())?; let driver_ttl = self.get_ttl(); - if driver_ttl > 0 && !major_browser_version.is_empty() { + if should_cache_driver_version(driver_ttl, major_browser_version, &driver_version) + { metadata.drivers.push(create_driver_metadata( major_browser_version.as_str(), self.driver_name, diff --git a/rust/src/firefox.rs b/rust/src/firefox.rs index 19b9f4b3b014d..ea3276b76ddaa 100644 --- a/rust/src/firefox.rs +++ b/rust/src/firefox.rs @@ -21,7 +21,8 @@ use crate::config::OS::{LINUX, MACOS, WINDOWS}; use crate::downloads::{parse_json_from_url, read_content_from_link, read_redirect_from_link}; use crate::files::{BrowserPath, compose_driver_path_in_cache}; use crate::metadata::{ - create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, + create_driver_metadata, get_driver_version_from_metadata, get_metadata, + should_cache_driver_version, write_metadata, }; use crate::{ BETA, DASH_VERSION, DEV, ESR, LATEST_RELEASE, Logger, NIGHTLY, OFFLINE_REQUEST_ERR_MSG, STABLE, @@ -296,7 +297,8 @@ impl SeleniumManager for FirefoxManager { }; let driver_ttl = self.get_ttl(); - if driver_ttl > 0 && !major_browser_version.is_empty() { + if should_cache_driver_version(driver_ttl, major_browser_version, &driver_version) + { metadata.drivers.push(create_driver_metadata( major_browser_version, self.driver_name, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 2186054c409b3..c5f41bef25506 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1972,3 +1972,40 @@ fn get_index_version(full_version: &str, index: usize) -> Result .ok_or(anyhow!(format!("Wrong version: {}", full_version)))? .to_string()) } + +#[cfg(test)] +mod index_version_tests { + use super::*; + + #[test] + fn get_index_version_major() { + assert_eq!(get_index_version("120.0.6099.109", 0).unwrap(), "120"); + } + + #[test] + fn get_index_version_minor() { + assert_eq!(get_index_version("120.0.6099.109", 1).unwrap(), "0"); + } + + #[test] + fn get_index_version_patch() { + assert_eq!(get_index_version("120.0.6099.109", 2).unwrap(), "6099"); + } + + #[test] + fn get_index_version_single_component() { + assert_eq!(get_index_version("115", 0).unwrap(), "115"); + } + + #[test] + fn get_index_version_out_of_bounds_errors() { + let result = get_index_version("115", 1); + assert!(result.is_err()); + } + + #[test] + fn get_index_version_empty_string_errors() { + let result = get_index_version("", 0); + assert!(result.is_err()); + } +} diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 8d9888d77839c..90f234bc044fd 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -181,6 +181,18 @@ pub fn create_browser_metadata( } } +/// Determines whether a discovered driver version should be written to the metadata cache. +/// +/// Returns `false` when the TTL is zero, the major browser version is empty, or the +/// driver version itself is empty (e.g. a version-less HTTP response). +pub fn should_cache_driver_version( + driver_ttl: u64, + major_browser_version: &str, + driver_version: &str, +) -> bool { + driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty() +} + pub fn create_driver_metadata( major_browser_version: &str, driver_name: &str, diff --git a/rust/tests/cache_unit_tests.rs b/rust/tests/cache_unit_tests.rs new file mode 100644 index 0000000000000..53e53fac98d92 --- /dev/null +++ b/rust/tests/cache_unit_tests.rs @@ -0,0 +1,317 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License +// for the specific language governing permissions and limitations +// under the License. + +use selenium_manager::files::{collect_files_from_cache, find_latest_from_cache}; +use selenium_manager::get_manager_by_browser; +use selenium_manager::metadata::{ + create_driver_metadata, get_driver_version_from_metadata, get_metadata, + should_cache_driver_version, write_metadata, Metadata, +}; +use selenium_manager::SeleniumManager; + +use rstest::rstest; +use std::fs; +use std::path::PathBuf; +use tempfile::tempdir; +use walkdir::WalkDir; + +fn create_driver_in_cache( + cache: &PathBuf, + driver_name: &str, + version: &str, +) { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + let platform = manager.get_platform_label().to_string(); + let os = manager.get_os().to_string(); + let ext = if selenium_manager::config::OS::WINDOWS.is(&os) { + ".exe" + } else { + "" + }; + let dir = cache + .join(driver_name) + .join(os) + .join(&platform) + .join(version); + fs::create_dir_all(&dir).unwrap(); + fs::write( + dir.join(format!("{}{}", driver_name, ext)), + b"fake binary", + ) + .unwrap(); +} + +fn create_driver_in_cache_custom( + cache: &PathBuf, + driver_name: &str, + os_label: &str, + arch_label: &str, + version: &str, +) { + let dir = cache + .join(driver_name) + .join(os_label) + .join(arch_label) + .join(version); + fs::create_dir_all(&dir).unwrap(); + let ext = if os_label == "windows" { ".exe" } else { "" }; + fs::write( + dir.join(format!("{}{}", driver_name, ext)), + b"fake binary", + ) + .unwrap(); +} + +#[test] +fn find_latest_from_cache_returns_none_when_empty() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + let result = find_latest_from_cache(&cache, |_| true).unwrap(); + assert!(result.is_none()); +} + +#[test] +fn find_latest_from_cache_returns_matching_file() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "120.0.6099.109"); + + let result = find_latest_from_cache(&cache, |entry| { + entry + .file_name() + .to_str() + .map(|s| s.contains("chromedriver")) + .unwrap_or(false) + }) + .unwrap(); + + assert!(result.is_some()); + let path = result.unwrap(); + assert!(path.exists()); + assert!(path.to_str().unwrap().contains("chromedriver")); +} + +#[test] +fn find_latest_from_cache_returns_last_when_multiple_matches() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "115.0.5790.110"); + create_driver_in_cache(&cache, "chromedriver", "120.0.6099.109"); + + let result = find_latest_from_cache(&cache, |entry| { + entry + .file_name() + .to_str() + .map(|s| s.contains("chromedriver")) + .unwrap_or(false) + }) + .unwrap(); + + assert!(result.is_some()); + let path = result.unwrap(); + assert!( + path.to_str().unwrap().contains("120.0.6099.109"), + "expected last sorted entry to be 120.x, got: {:?}", + path + ); +} + +#[test] +fn find_latest_from_cache_lexical_sort_misorders_across_digit_boundaries() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache_custom(&cache, "chromedriver", "linux", "x86_64", "9.0.0"); + create_driver_in_cache_custom(&cache, "chromedriver", "linux", "x86_64", "10.0.0"); + + let result = find_latest_from_cache(&cache, |entry| { + entry + .file_name() + .to_str() + .map(|s| s.contains("chromedriver")) + .unwrap_or(false) + }) + .unwrap() + .unwrap(); + + assert!( + result.to_str().unwrap().contains("9.0.0"), + "lexical sort puts '9' after '10'; this test documents the latent bug" + ); +} + +#[test] +fn collect_files_from_cache_returns_empty_when_no_match() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "120.0.6099.109"); + + let result = collect_files_from_cache(&cache, |_| false); + assert!(result.is_empty()); +} + +#[test] +fn collect_files_from_cache_returns_all_matches() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "115.0.5790.110"); + create_driver_in_cache(&cache, "chromedriver", "120.0.6099.109"); + + let result = collect_files_from_cache(&cache, |entry| { + entry + .file_name() + .to_str() + .map(|s| s.contains("chromedriver")) + .unwrap_or(false) + }); + + assert_eq!(result.len(), 2); +} + +#[test] +fn is_driver_and_matches_browser_version_matches_major() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "120.0.6099.109"); + + let mut manager = get_manager_by_browser("chrome".to_string()).unwrap(); + manager.set_browser_version("120".to_string()); + let entries: Vec<_> = WalkDir::new(&cache) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .collect(); + + let matching: Vec<_> = entries + .iter() + .filter(|e| manager.is_driver_and_matches_browser_version(e)) + .collect(); + + assert_eq!(matching.len(), 1); +} + +#[test] +fn is_driver_and_matches_browser_version_empty_major_matches_all() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "115.0.5790.110"); + create_driver_in_cache(&cache, "chromedriver", "120.0.6099.109"); + + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + let entries: Vec<_> = WalkDir::new(&cache) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .collect(); + + let matching: Vec<_> = entries + .iter() + .filter(|e| manager.is_driver_and_matches_browser_version(e)) + .collect(); + + assert_eq!( + matching.len(), + 2, + "starts_with(\"\") matches every cached driver — documents the latent bug" + ); +} + +#[test] +fn find_best_driver_from_cache_returns_matching_version() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "115.0.5790.110"); + create_driver_in_cache(&cache, "chromedriver", "120.0.6099.109"); + + let mut manager = get_manager_by_browser("chrome".to_string()).unwrap(); + manager.set_cache_path(cache.to_str().unwrap().to_string()); + manager.set_browser_version("120".to_string()); + + let result = manager.find_best_driver_from_cache().unwrap(); + assert!(result.is_some()); + let path = result.unwrap(); + assert!(path.to_str().unwrap().contains("120.0.6099.109")); +} + +#[test] +fn find_best_driver_from_cache_falls_back_to_latest_when_no_match() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + create_driver_in_cache(&cache, "chromedriver", "115.0.5790.110"); + + let mut manager = get_manager_by_browser("chrome".to_string()).unwrap(); + manager.set_cache_path(cache.to_str().unwrap().to_string()); + manager.set_browser_version("130".to_string()); + + let result = manager.find_best_driver_from_cache().unwrap(); + assert!(result.is_some()); + let path = result.unwrap(); + assert!(path.to_str().unwrap().contains("chromedriver")); +} + +#[test] +fn find_best_driver_from_cache_returns_none_when_cache_empty() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + + let mut manager = get_manager_by_browser("chrome".to_string()).unwrap(); + manager.set_cache_path(cache.to_str().unwrap().to_string()); + manager.set_browser_version("120".to_string()); + + let result = manager.find_best_driver_from_cache().unwrap(); + assert!(result.is_none()); +} + +#[test] +fn empty_driver_version_is_not_cached_in_metadata() { + let tmp = tempdir().unwrap(); + let cache = tmp.path().to_path_buf(); + let log = selenium_manager::logger::Logger::default(); + + let mut metadata = Metadata { + browsers: Vec::new(), + drivers: Vec::new(), + stats: Vec::new(), + cached_assets: Vec::new(), + }; + + let major_browser_version = "120"; + let driver_name = "edgedriver"; + let driver_ttl = 3600; + + let driver_version = ""; + + if should_cache_driver_version(driver_ttl, major_browser_version, driver_version) { + metadata.drivers.push(create_driver_metadata( + major_browser_version, + driver_name, + driver_version, + driver_ttl, + )); + write_metadata(&metadata, &log, Some(cache.clone())); + } + + let read_back = get_metadata(&log, &Some(cache.clone())); + let result = get_driver_version_from_metadata( + &read_back.drivers, + driver_name, + major_browser_version, + ); + + assert!( + result.is_none(), + "empty driver_version must not be cached in metadata — this test fails if the !driver_version.is_empty() guard is removed from should_cache_driver_version()" + ); +} diff --git a/rust/tests/config_unit_tests.rs b/rust/tests/config_unit_tests.rs new file mode 100644 index 0000000000000..7367275aae24b --- /dev/null +++ b/rust/tests/config_unit_tests.rs @@ -0,0 +1,100 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License +// for the specific language governing permissions and limitations +// under the License. + +use selenium_manager::config::{OS, ARCH, str_to_os}; +use selenium_manager::config::OS::{LINUX, MACOS, WINDOWS}; +use selenium_manager::config::ARCH::{ARM64, ARMV7, X32, X64}; + +use rstest::rstest; + +#[rstest] +#[case("windows", WINDOWS)] +#[case("win", WINDOWS)] +#[case("Windows", WINDOWS)] +#[case("WINDOWS", WINDOWS)] +#[case("macos", MACOS)] +#[case("mac", MACOS)] +#[case("MacOS", MACOS)] +#[case("linux", LINUX)] +#[case("gnu/linux", LINUX)] +#[case("Linux", LINUX)] +fn str_to_os_parses_valid_os(#[case] input: &str, #[case] expected: OS) { + let result = str_to_os(input).unwrap(); + assert_eq!(result, expected); +} + +#[rstest] +#[case("solaris")] +#[case("")] +#[case("darwin")] +#[case("unix")] +fn str_to_os_rejects_invalid_os(#[case] input: &str) { + let result = str_to_os(input); + assert!(result.is_err()); +} + +#[rstest] +#[case(WINDOWS, "windows")] +#[case(WINDOWS, "win")] +#[case(WINDOWS, "Windows")] +#[case(MACOS, "macos")] +#[case(MACOS, "mac")] +#[case(LINUX, "linux")] +#[case(LINUX, "gnu/linux")] +fn os_is_matches(#[case] os: OS, #[case] candidate: &str) { + assert!(os.is(candidate)); +} + +#[rstest] +#[case(WINDOWS, "linux")] +#[case(WINDOWS, "macos")] +#[case(MACOS, "windows")] +#[case(MACOS, "linux")] +#[case(LINUX, "windows")] +#[case(LINUX, "macos")] +fn os_is_does_not_match(#[case] os: OS, #[case] candidate: &str) { + assert!(!os.is(candidate)); +} + +#[rstest] +#[case(X32, "x86")] +#[case(X32, "i386")] +#[case(X32, "x32")] +#[case(X32, "i686")] +#[case(X64, "x86_64")] +#[case(X64, "amd64")] +#[case(X64, "x64")] +#[case(X64, "ia64")] +#[case(ARM64, "arm64")] +#[case(ARM64, "aarch64")] +#[case(ARM64, "arm")] +#[case(ARMV7, "arm7l")] +#[case(ARMV7, "armv7l")] +fn arch_is_matches(#[case] arch: ARCH, #[case] candidate: &str) { + assert!(arch.is(candidate)); +} + +#[rstest] +#[case(X32, "x86_64")] +#[case(X32, "arm64")] +#[case(X64, "x86")] +#[case(X64, "i686")] +#[case(X64, "arm7l")] +#[case(ARM64, "x86_64")] +#[case(ARMV7, "aarch64")] +fn arch_is_does_not_match(#[case] arch: ARCH, #[case] candidate: &str) { + assert!(!arch.is(candidate)); +} diff --git a/rust/tests/version_unit_tests.rs b/rust/tests/version_unit_tests.rs new file mode 100644 index 0000000000000..61f3a2919c315 --- /dev/null +++ b/rust/tests/version_unit_tests.rs @@ -0,0 +1,185 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License +// for the specific language governing permissions and limitations +// under the License. + +use selenium_manager::files::parse_version; +use selenium_manager::logger::Logger; +use selenium_manager::SeleniumManager; +use selenium_manager::get_manager_by_browser; + +use rstest::rstest; + +#[rstest] +#[case("Google Chrome 120.0.6099.109", "120.0.6099.109")] +#[case("Chromium 115.0.5790.110", "115.0.5790.110")] +#[case("Microsoft Edge 120.0.2210.91", "120.0.2210.91")] +#[case("Mozilla Firefox 121.0", "121.0")] +#[case("115", "115")] +#[case(" 130.0.6723.91 ", "130.0.6723.91")] +fn parse_version_extracts_version_string(#[case] input: &str, #[case] expected: &str) { + let log = Logger::new(); + let result = parse_version(input.to_string(), &log).unwrap(); + assert_eq!(result, expected); +} + +#[test] +fn parse_version_strips_trailing_dot() { + let log = Logger::new(); + let result = parse_version("120.".to_string(), &log).unwrap(); + assert_eq!(result, "120"); +} + +#[rstest] +#[case("error: command not found")] +#[case("ERROR: something went wrong")] +#[case("Error: unable to locate browser")] +fn parse_version_returns_error_on_error_string(#[case] input: &str) { + let log = Logger::new(); + let result = parse_version(input.to_string(), &log); + assert!(result.is_err()); +} + +#[test] +fn parse_version_returns_empty_on_garbage() { + let log = Logger::new(); + let result = parse_version("no version here".to_string(), &log).unwrap(); + assert_eq!(result, ""); +} + +#[test] +fn parse_version_handles_esr_suffix() { + let log = Logger::new(); + let result = parse_version("Mozilla Firefox 115.12.0esr".to_string(), &log).unwrap(); + assert_eq!(result, "115.12.0"); +} + +#[test] +fn parse_version_handles_snap_prefix() { + let log = Logger::new(); + let result = parse_version("snap 121.0.1".to_string(), &log).unwrap(); + assert_eq!(result, "121.0.1"); +} + +#[rstest] +#[case("stable", true)] +#[case("STABLE", true)] +#[case("Stable", true)] +#[case("beta", false)] +#[case("120.0.6099.109", false)] +fn is_stable_predicate(#[case] version: &str, #[case] expected: bool) { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + assert_eq!(manager.is_stable(version), expected); +} + +#[rstest] +#[case("beta", true)] +#[case("BETA", true)] +#[case("Beta", true)] +#[case("stable", false)] +#[case("120.0", false)] +fn is_beta_predicate(#[case] version: &str, #[case] expected: bool) { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + assert_eq!(manager.is_beta(version), expected); +} + +#[rstest] +#[case("dev", true)] +#[case("DEV", true)] +#[case("Dev", true)] +#[case("stable", false)] +fn is_dev_predicate(#[case] version: &str, #[case] expected: bool) { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + assert_eq!(manager.is_dev(version), expected); +} + +#[rstest] +#[case("nightly", true)] +#[case("NIGHTLY", true)] +#[case("canary", true)] +#[case("CANARY", true)] +#[case("Canary", true)] +#[case("stable", false)] +#[case("120.0", false)] +fn is_nightly_predicate(#[case] version: &str, #[case] expected: bool) { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + assert_eq!(manager.is_nightly(version), expected); +} + +#[rstest] +#[case("esr", true)] +#[case("ESR", true)] +#[case("Esr", true)] +#[case("stable", false)] +#[case("115.12.0", false)] +fn is_esr_predicate(#[case] version: &str, #[case] expected: bool) { + let manager = get_manager_by_browser("firefox".to_string()).unwrap(); + assert_eq!(manager.is_esr(version), expected); +} + +#[rstest] +#[case("120.0.6099.109", true)] +#[case("121.0", true)] +#[case("stable", false)] +#[case("beta", false)] +#[case("", false)] +fn is_version_specific_predicate(#[case] version: &str, #[case] expected: bool) { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + assert_eq!(manager.is_version_specific(version), expected); +} + +#[rstest] +#[case("120.0.6099.109", "120")] +#[case("115", "115")] +#[case("130.0.6723.91", "130")] +fn get_major_version_extracts_major(#[case] version: &str, #[case] expected: &str) { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + let result = manager.get_major_version(version).unwrap(); + assert_eq!(result, expected); +} + +#[test] +fn get_major_version_errors_on_empty() { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + let result = manager.get_major_version(""); + assert!(result.is_err()); +} + +#[test] +fn get_major_version_returns_first_component_for_non_numeric() { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + let result = manager.get_major_version("not-a-version"); + assert_eq!(result.unwrap(), "not-a-version"); +} + +#[rstest] +#[case("stable", "stable")] +#[case("beta", "beta")] +#[case("dev", "dev")] +#[case("nightly", "nightly")] +#[case("canary", "canary")] +#[case("esr", "esr")] +#[case("120.0.6099.109", "120")] +#[case("115", "115")] +fn get_major_browser_version_returns_expected(#[case] version: &str, #[case] expected: &str) { + let mut manager = get_manager_by_browser("chrome".to_string()).unwrap(); + manager.set_browser_version(version.to_string()); + assert_eq!(manager.get_major_browser_version(), expected); +} + +#[test] +fn get_major_browser_version_empty_returns_empty() { + let manager = get_manager_by_browser("chrome".to_string()).unwrap(); + assert_eq!(manager.get_major_browser_version(), ""); +}