From ff464751aeee63b8e6d6d05b7a8998a5a4c8f324 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 7 May 2024 09:05:23 +1000 Subject: [PATCH 1/7] Add Native tests for the native locator --- .github/workflows/pr-check.yml | 27 ++++++ native_locator/Cargo.toml | 8 +- native_locator/src/common_python.rs | 31 ++++--- native_locator/src/conda.rs | 83 ++++++++++------- native_locator/src/known.rs | 67 ++++++++++---- native_locator/src/lib.rs | 9 ++ native_locator/src/logging.rs | 21 ----- native_locator/src/main.rs | 21 +++-- native_locator/src/messaging.rs | 44 +++++++-- native_locator/src/utils.rs | 20 +++- native_locator/src/windows_python.rs | 21 +++-- native_locator/tests/common/mod.rs | 92 +++++++++++++++++++ native_locator/tests/common_python_test.rs | 30 ++++++ native_locator/tests/conda_test.rs | 81 ++++++++++++++++ .../tests/unix/conda/.conda/environments.txt | 2 + native_locator/tests/unix/conda/conda | 0 .../python-10.0.1-hdf0ec26_0_cpython.json | 1 + .../tests/unix/conda/envs/one/python | 0 .../tests/unix/conda/envs/two/python | 0 .../.conda/environments.txt | 0 .../tests/unix/conda_without_envs/conda | 0 native_locator/tests/unix/known/python | 0 .../tests/unix/known/python.version | 1 + 23 files changed, 448 insertions(+), 111 deletions(-) create mode 100644 native_locator/src/lib.rs create mode 100644 native_locator/tests/common/mod.rs create mode 100644 native_locator/tests/common_python_test.rs create mode 100644 native_locator/tests/conda_test.rs create mode 100644 native_locator/tests/unix/conda/.conda/environments.txt create mode 100644 native_locator/tests/unix/conda/conda create mode 100644 native_locator/tests/unix/conda/envs/one/conda-meta/python-10.0.1-hdf0ec26_0_cpython.json create mode 100644 native_locator/tests/unix/conda/envs/one/python create mode 100644 native_locator/tests/unix/conda/envs/two/python create mode 100644 native_locator/tests/unix/conda_without_envs/.conda/environments.txt create mode 100644 native_locator/tests/unix/conda_without_envs/conda create mode 100644 native_locator/tests/unix/known/python create mode 100644 native_locator/tests/unix/known/python.version diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index f7102dd5342a..fd2a47645e50 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -188,6 +188,9 @@ jobs: - name: Install test requirements run: python -m pip install --upgrade -r build/test-requirements.txt + - name: Native Locator (Rust) tests + run: nox --session native_test + - name: Install functional test requirements run: python -m pip install --upgrade -r ./build/functional-test-requirements.txt if: matrix.test-suite == 'functional' @@ -311,6 +314,30 @@ jobs: run: npm run test:functional if: matrix.test-suite == 'functional' + native-tests: + name: Native Tests + # The value of runs-on is the OS of the current job (specified in the strategy matrix below) instead of being hardcoded. + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ${{ env.special-working-directory }} + strategy: + fail-fast: false + matrix: + # We're not running CI on macOS for now because it's one less matrix entry to lower the number of runners used, + # macOS runners are expensive, and we assume that Ubuntu is enough to cover the Unix case. + os: [ubuntu-latest, windows-latest] + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + path: ${{ env.special-working-directory-relative }} + + - name: Native Locator tests + run: cargo test + working-directory: ${{ env.special-working-directory }}/native_locator + smoke-tests: name: Smoke tests # The value of runs-on is the OS of the current job (specified in the strategy matrix below) instead of being hardcoded. diff --git a/native_locator/Cargo.toml b/native_locator/Cargo.toml index f20396b09a9d..29fdba160ce9 100644 --- a/native_locator/Cargo.toml +++ b/native_locator/Cargo.toml @@ -7,7 +7,13 @@ edition = "2021" winreg = "0.52.0" [dependencies] -serde = {version ="1.0.152", features = ["derive"]} +serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.93" serde_repr = "0.1.10" regex = "1.10.4" + +[features] +test = [] + +[lib] +doctest = false diff --git a/native_locator/src/common_python.rs b/native_locator/src/common_python.rs index c5482bbfc16a..7ce8702b7929 100644 --- a/native_locator/src/common_python.rs +++ b/native_locator/src/common_python.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +use crate::known; use crate::messaging; use crate::utils; use std::env; @@ -20,10 +21,10 @@ fn get_env_path(path: &str) -> Option { } } -fn report_path_python(path: &str) { +fn report_path_python(dispatcher: &mut impl messaging::MessageDispatcher, path: &str) { let version = utils::get_version(path); let env_path = get_env_path(path); - messaging::send_message(messaging::PythonEnvironment::new( + dispatcher.send_message(messaging::PythonEnvironment::new( "Python".to_string(), vec![path.to_string()], "System".to_string(), @@ -33,20 +34,26 @@ fn report_path_python(path: &str) { )); } -fn report_python_on_path() { - let bin = if cfg!(windows) { - "python.exe" - } else { - "python" - }; - if let Ok(paths) = env::var("PATH") { +fn report_python_on_path( + dispatcher: &mut impl messaging::MessageDispatcher, + known_paths: &impl known::KnownPaths, +) { + if let Some(paths) = known_paths.get_env_var("PATH".to_string()) { + let bin = if cfg!(windows) { + "python.exe" + } else { + "python" + }; env::split_paths(&paths) .map(|p| p.join(bin)) .filter(|p| p.exists()) - .for_each(|full_path| report_path_python(full_path.to_str().unwrap())); + .for_each(|full_path| report_path_python(dispatcher, full_path.to_str().unwrap())); } } -pub fn find_and_report() { - report_python_on_path(); +pub fn find_and_report( + dispatcher: &mut impl messaging::MessageDispatcher, + known_paths: &impl known::KnownPaths, +) { + report_python_on_path(dispatcher, known_paths); } diff --git a/native_locator/src/conda.rs b/native_locator/src/conda.rs index db0bc3bc10f1..2d984685eb0d 100644 --- a/native_locator/src/conda.rs +++ b/native_locator/src/conda.rs @@ -84,7 +84,6 @@ fn get_version_from_meta_json(json_file: &Path) -> Option { fn get_conda_package_json_path(any_path: &Path, package: &str) -> Option { let package_name = format!("{}-", package); let conda_meta_path = get_conda_meta_path(any_path)?; - std::fs::read_dir(conda_meta_path).ok()?.find_map(|entry| { let path = entry.ok()?.path(); let file_name = path.file_name()?.to_string_lossy(); @@ -97,6 +96,7 @@ fn get_conda_package_json_path(any_path: &Path, package: &str) -> Option bool { let conda_python_json_path = get_conda_package_json_path(any_path, "python"); match conda_python_json_path { @@ -127,11 +127,9 @@ fn get_conda_bin_names() -> Vec<&'static str> { } /// Find the conda binary on the PATH environment variable -fn find_conda_binary_on_path() -> Option { - let paths = env::var("PATH").ok()?; - let paths = env::split_paths(&paths); - for path in paths { - let path = Path::new(&path); +fn find_conda_binary_on_path(known_paths: &impl known::KnownPaths) -> Option { + let paths = known_paths.get_env_var("PATH".to_string())?; + for path in env::split_paths(&paths) { for bin in get_conda_bin_names() { let conda_path = path.join(bin); match std::fs::metadata(&conda_path) { @@ -161,11 +159,11 @@ fn find_python_binary_path(env_path: &Path) -> Option { } #[cfg(windows)] -fn get_known_conda_locations() -> Vec { - let user_profile = env::var("USERPROFILE").unwrap(); - let program_data = env::var("PROGRAMDATA").unwrap(); - let all_user_profile = env::var("ALLUSERSPROFILE").unwrap(); - let home_drive = env::var("HOMEDRIVE").unwrap(); +fn get_known_conda_locations(known_paths_provider: &impl known::KnownPaths) -> Vec { + let user_profile = known_paths_provider.get_env_var("USERPROFILE".to_string()).unwrap(); + let program_data = known_paths_provider.get_env_var("PROGRAMDATA".to_string()).unwrap(); + let all_user_profile = known_paths_provider.get_env_var("ALLUSERSPROFILE".to_string()).unwrap(); + let home_drive = known_paths_provider.get_env_var("HOMEDRIVE".to_string()).unwrap(); let mut known_paths = vec![ Path::new(&user_profile).join("Anaconda3\\Scripts"), Path::new(&program_data).join("Anaconda3\\Scripts"), @@ -176,12 +174,12 @@ fn get_known_conda_locations() -> Vec { Path::new(&all_user_profile).join("Miniconda3\\Scripts"), Path::new(&home_drive).join("Miniconda3\\Scripts"), ]; - known_paths.append(&mut known::get_know_global_search_locations()); + known_paths.append(&mut known_paths_provider.get_know_global_search_locations()); known_paths } #[cfg(unix)] -fn get_known_conda_locations() -> Vec { +fn get_known_conda_locations(known_paths_provider: &impl known::KnownPaths) -> Vec { let mut known_paths = vec![ PathBuf::from("/opt/anaconda3/bin"), PathBuf::from("/opt/miniconda3/bin"), @@ -202,14 +200,16 @@ fn get_known_conda_locations() -> Vec { PathBuf::from("/anaconda3/bin"), PathBuf::from("/miniconda3/bin"), ]; - known_paths.append(&mut known::get_know_global_search_locations()); + known_paths.append(&mut known_paths_provider.get_know_global_search_locations()); known_paths } /// Find conda binary in known locations -fn find_conda_binary_in_known_locations() -> Option { +fn find_conda_binary_in_known_locations( + known_paths_provider: &impl known::KnownPaths, +) -> Option { let conda_bin_names = get_conda_bin_names(); - let known_locations = get_known_conda_locations(); + let known_locations = get_known_conda_locations(known_paths_provider); for location in known_locations { for bin in &conda_bin_names { let conda_path = location.join(bin); @@ -223,17 +223,19 @@ fn find_conda_binary_in_known_locations() -> Option { } /// Find the conda binary on the system -pub fn find_conda_binary() -> Option { - let conda_binary_on_path = find_conda_binary_on_path(); +pub fn find_conda_binary(known_paths_provider: &impl known::KnownPaths) -> Option { + let conda_binary_on_path = find_conda_binary_on_path(known_paths_provider); match conda_binary_on_path { Some(conda_binary_on_path) => Some(conda_binary_on_path), - None => find_conda_binary_in_known_locations(), + None => find_conda_binary_in_known_locations(known_paths_provider), } } -fn get_conda_envs_from_environment_txt() -> Vec { +fn get_conda_envs_from_environment_txt( + known_paths_provider: &impl known::KnownPaths, +) -> Vec { let mut envs = vec![]; - let home = known::get_user_home(); + let home = known_paths_provider.get_user_home(); match home { Some(home) => { let home = Path::new(&home); @@ -252,9 +254,12 @@ fn get_conda_envs_from_environment_txt() -> Vec { envs } -fn get_known_env_locations(conda_bin: PathBuf) -> Vec { +fn get_known_env_locations( + conda_bin: PathBuf, + known_paths_provider: &impl known::KnownPaths, +) -> Vec { let mut paths = vec![]; - let home = known::get_user_home(); + let home = known_paths_provider.get_user_home(); match home { Some(home) => { let home = Path::new(&home); @@ -284,9 +289,12 @@ fn get_known_env_locations(conda_bin: PathBuf) -> Vec { paths } -fn get_conda_envs_from_known_env_locations(conda_bin: PathBuf) -> Vec { +fn get_conda_envs_from_known_env_locations( + conda_bin: PathBuf, + known_paths_provider: &impl known::KnownPaths, +) -> Vec { let mut envs = vec![]; - for location in get_known_env_locations(conda_bin) { + for location in get_known_env_locations(conda_bin, known_paths_provider) { if is_conda_environment(&Path::new(&location)) { envs.push(location.to_string()); } @@ -324,14 +332,18 @@ struct CondaEnv { path: PathBuf, } -fn get_distinct_conda_envs(conda_bin: PathBuf) -> Vec { - let mut envs = get_conda_envs_from_environment_txt(); - let mut known_envs = get_conda_envs_from_known_env_locations(conda_bin.to_path_buf()); +fn get_distinct_conda_envs( + conda_bin: PathBuf, + known_paths_provider: &impl known::KnownPaths, +) -> Vec { + let mut envs = get_conda_envs_from_environment_txt(known_paths_provider); + let mut known_envs = + get_conda_envs_from_known_env_locations(conda_bin.to_path_buf(), known_paths_provider); envs.append(&mut known_envs); envs.sort(); envs.dedup(); - let locations = get_known_env_locations(conda_bin); + let locations = get_known_env_locations(conda_bin, known_paths_provider); let mut conda_envs = vec![]; for env in envs { let env = Path::new(&env); @@ -367,16 +379,19 @@ fn get_distinct_conda_envs(conda_bin: PathBuf) -> Vec { conda_envs } -pub fn find_and_report() { - let conda_binary = find_conda_binary(); +pub fn find_and_report( + dispatcher: &mut impl messaging::MessageDispatcher, + known_paths_provider: &impl known::KnownPaths, +) { + let conda_binary = find_conda_binary(known_paths_provider); match conda_binary { Some(conda_binary) => { let params = messaging::EnvManager::new(vec![conda_binary.to_string_lossy().to_string()], None); let message = messaging::EnvManagerMessage::new(params); - messaging::send_message(message); + dispatcher.send_message(message); - let envs = get_distinct_conda_envs(conda_binary.to_path_buf()); + let envs = get_distinct_conda_envs(conda_binary.to_path_buf(), known_paths_provider); for env in envs { let executable = find_python_binary_path(Path::new(&env.path)); let params = messaging::PythonEnvironment::new( @@ -407,7 +422,7 @@ pub fn find_and_report() { Some(env.path.to_string_lossy().to_string()), ); let message = messaging::PythonEnvironmentMessage::new(params); - messaging::send_message(message); + dispatcher.send_message(message); } } None => (), diff --git a/native_locator/src/known.rs b/native_locator/src/known.rs index d1d09e8aeda6..e622ac3cb30e 100644 --- a/native_locator/src/known.rs +++ b/native_locator/src/known.rs @@ -2,33 +2,64 @@ // Licensed under the MIT License. use std::{env, path::PathBuf}; +pub trait KnownPaths { + fn get_user_home(&self) -> Option; + fn get_env_var(&self, key: String) -> Option; + fn get_know_global_search_locations(&self) -> Vec; +} + +pub struct KnownPathsImpl {} + #[cfg(windows)] -pub fn get_know_global_search_locations() -> Vec { - vec![] +impl KnownPaths for KnownPathsImpl { + fn get_user_home(&self) -> Option { + get_user_home() + } + fn get_env_var(&self, key: string) -> Option { + get_env_var(key) + } + fn get_know_global_search_locations(&self) -> Vec { + vec![] + } } #[cfg(unix)] -pub fn get_know_global_search_locations() -> Vec { - vec![ - PathBuf::from("/usr/bin"), - PathBuf::from("/usr/local/bin"), - PathBuf::from("/bin"), - PathBuf::from("/home/bin"), - PathBuf::from("/sbin"), - PathBuf::from("/usr/sbin"), - PathBuf::from("/usr/local/sbin"), - PathBuf::from("/home/sbin"), - PathBuf::from("/opt"), - PathBuf::from("/opt/bin"), - PathBuf::from("/opt/sbin"), - PathBuf::from("/opt/homebrew/bin"), - ] +impl KnownPaths for KnownPathsImpl { + fn get_user_home(&self) -> Option { + get_user_home() + } + fn get_env_var(&self, key: String) -> Option { + get_env_var(key) + } + fn get_know_global_search_locations(&self) -> Vec { + vec![ + PathBuf::from("/usr/bin"), + PathBuf::from("/usr/local/bin"), + PathBuf::from("/bin"), + PathBuf::from("/home/bin"), + PathBuf::from("/sbin"), + PathBuf::from("/usr/sbin"), + PathBuf::from("/usr/local/sbin"), + PathBuf::from("/home/sbin"), + PathBuf::from("/opt"), + PathBuf::from("/opt/bin"), + PathBuf::from("/opt/sbin"), + PathBuf::from("/opt/homebrew/bin"), + ] + } } -pub fn get_user_home() -> Option { +fn get_user_home() -> Option { let home = env::var("HOME").or_else(|_| env::var("USERPROFILE")); match home { Ok(home) => Some(home), Err(_) => None, } } + +fn get_env_var(key: String) -> Option { + match env::var(key) { + Ok(path) => Some(path), + Err(_) => None, + } +} diff --git a/native_locator/src/lib.rs b/native_locator/src/lib.rs new file mode 100644 index 000000000000..d95a4300d253 --- /dev/null +++ b/native_locator/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub mod messaging; +pub mod utils; +pub mod common_python; +pub mod logging; +pub mod conda; +pub mod known; diff --git a/native_locator/src/logging.rs b/native_locator/src/logging.rs index 7dc2b495442a..66532ff67eff 100644 --- a/native_locator/src/logging.rs +++ b/native_locator/src/logging.rs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -use crate::messaging; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Clone)] @@ -40,23 +39,3 @@ impl LogMessage { } } } - -pub fn log_debug(message: &str) { - messaging::send_message(LogMessage::new(message.to_string(), LogLevel::Debug)); -} - -pub fn log_info(message: &str) { - messaging::send_message(LogMessage::new(message.to_string(), LogLevel::Info)); -} - -pub fn log_warning(message: &str) { - messaging::send_message(LogMessage::new(message.to_string(), LogLevel::Warning)); -} - -pub fn log_error(message: &str) { - messaging::send_message(LogMessage::new(message.to_string(), LogLevel::Error)); -} - -pub fn log_msg(message: &str, level: LogLevel) { - messaging::send_message(LogMessage::new(message.to_string(), level)); -} diff --git a/native_locator/src/main.rs b/native_locator/src/main.rs index aa339e4e2b6d..5e6b8a072241 100644 --- a/native_locator/src/main.rs +++ b/native_locator/src/main.rs @@ -3,6 +3,9 @@ use std::time::SystemTime; +use known::KnownPathsImpl; +use messaging::{create_dispatcher, MessageDispatcher}; + mod common_python; mod conda; mod known; @@ -12,30 +15,34 @@ mod utils; mod windows_python; fn main() { + let mut dispatcher = create_dispatcher(); + // let dispatcher = JsonRpcDispatcher {}; + let known_paths = KnownPathsImpl {}; + + dispatcher.log_info("Starting Native Locator"); let now = SystemTime::now(); - logging::log_info("Starting Native Locator"); // Finds python on PATH - common_python::find_and_report(); + common_python::find_and_report(&mut dispatcher, &known_paths); // Finds conda binary and conda environments - conda::find_and_report(); + conda::find_and_report(&mut dispatcher, &known_paths); // Finds Windows Store, Known Path, and Registry pythons #[cfg(windows)] - windows_python::find_and_report(); + windows_python::find_and_report(&dispatcher, &known_paths); match now.elapsed() { Ok(elapsed) => { - logging::log_info(&format!( + dispatcher.log_info(&format!( "Native Locator took {} milliseconds.", elapsed.as_millis() )); } Err(e) => { - logging::log_error(&format!("Error getting elapsed time: {:?}", e)); + dispatcher.log_error(&format!("Error getting elapsed time: {:?}", e)); } } - messaging::send_message(messaging::ExitMessage::new()); + dispatcher.send_message(messaging::ExitMessage::new()); } diff --git a/native_locator/src/messaging.rs b/native_locator/src/messaging.rs index 540ba0595988..a433604f059a 100644 --- a/native_locator/src/messaging.rs +++ b/native_locator/src/messaging.rs @@ -1,8 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +use crate::logging::{LogLevel, LogMessage}; use serde::{Deserialize, Serialize}; +pub trait MessageDispatcher { + fn send_message(&mut self, message: T) -> (); + fn log_debug(&mut self, message: &str) -> (); + fn log_info(&mut self, message: &str) -> (); + fn log_warning(&mut self, message: &str) -> (); + fn log_error(&mut self, message: &str) -> (); +} + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EnvManager { @@ -59,7 +68,7 @@ impl PythonEnvironment { ) -> Self { Self { name, - python_executable_path: python_executable_path, + python_executable_path, category, version, activated_run, @@ -104,15 +113,30 @@ impl ExitMessage { } } -fn send_rpc_message(message: String) -> () { - print!( - "Content-Length: {}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n{}", - message.len(), - message - ); +pub struct JsonRpcDispatcher {} +impl MessageDispatcher for JsonRpcDispatcher { + fn send_message(&mut self, message: T) -> () { + let message = serde_json::to_string(&message).unwrap(); + print!( + "Content-Length: {}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n{}", + message.len(), + message + ); + } + fn log_debug(&mut self, message: &str) -> () { + self.send_message(LogMessage::new(message.to_string(), LogLevel::Debug)); + } + fn log_error(&mut self, message: &str) -> () { + self.send_message(LogMessage::new(message.to_string(), LogLevel::Error)); + } + fn log_info(&mut self, message: &str) -> () { + self.send_message(LogMessage::new(message.to_string(), LogLevel::Info)); + } + fn log_warning(&mut self, message: &str) -> () { + self.send_message(LogMessage::new(message.to_string(), LogLevel::Warning)); + } } -pub fn send_message(message: T) -> () { - let message = serde_json::to_string(&message).unwrap(); - send_rpc_message(message); +pub fn create_dispatcher() -> JsonRpcDispatcher { + JsonRpcDispatcher {} } diff --git a/native_locator/src/utils.rs b/native_locator/src/utils.rs index 001f56c815ca..d3573e3190a1 100644 --- a/native_locator/src/utils.rs +++ b/native_locator/src/utils.rs @@ -3,7 +3,7 @@ use std::process::Command; -pub fn get_version(path: &str) -> Option { +fn get_version_impl(path: &str) -> Option { let output = Command::new(path) .arg("-c") .arg("import sys; print(sys.version)") @@ -14,3 +14,21 @@ pub fn get_version(path: &str) -> Option { let output = output.split_whitespace().next().unwrap_or(output); Some(output.to_string()) } + +#[cfg(not(feature = "test"))] +pub fn get_version(path: &str) -> Option { + get_version_impl(path) +} + +// Tests + +#[cfg(feature = "test")] +pub fn get_version(path: &str) -> Option { + use std::path::PathBuf; + let version_file = PathBuf::from(path.to_owned() + ".version"); + if version_file.exists() { + let version = std::fs::read_to_string(version_file).ok()?; + return Some(version.trim().to_string()); + } + get_version_impl(path) +} diff --git a/native_locator/src/windows_python.rs b/native_locator/src/windows_python.rs index 0cb49d975685..7776a3481e6d 100644 --- a/native_locator/src/windows_python.rs +++ b/native_locator/src/windows_python.rs @@ -6,9 +6,9 @@ use crate::messaging; use crate::utils; use std::path::Path; -fn report_path_python(path: &str) { +fn report_path_python(path: &str, dispatcher: &mut impl messaging::MessageDispatcher) { let version = utils::get_version(path); - messaging::send_message(messaging::PythonEnvironment::new( + dispatcher.send_message(messaging::PythonEnvironment::new( "Python".to_string(), vec![path.to_string()], "WindowsStore".to_string(), @@ -18,8 +18,11 @@ fn report_path_python(path: &str) { )); } -fn report_windows_store_python() { - let home = known::get_user_home(); +fn report_windows_store_python( + dispatcher: &mut impl messaging::MessageDispatcher, + known_paths_provider: &impl known::KnownPaths, +) { + let home = known_paths_provider.get_user_home(); match home { Some(home) => { let apps_path = Path::new(&home) @@ -38,7 +41,7 @@ fn report_windows_store_python() { Some(name) => { let name = name.to_string_lossy().to_lowercase(); if name.starts_with("python3.") && name.ends_with(".exe") { - report_path_python(&path.to_string_lossy()); + report_path_python(&path.to_string_lossy(), dispatcher); } } None => {} @@ -57,7 +60,11 @@ fn report_windows_store_python() { fn report_registry_pythons() {} -pub fn find_and_report() { - report_windows_store_python(); +#[allow(dead_code)] +pub fn find_and_report( + dispatcher: &mut impl messaging::MessageDispatcher, + known_paths_provider: &impl known::KnownPaths, +) { + report_windows_store_python(dispatcher, known_paths_provider); report_registry_pythons(); } diff --git a/native_locator/tests/common/mod.rs b/native_locator/tests/common/mod.rs new file mode 100644 index 000000000000..ef8cee57e738 --- /dev/null +++ b/native_locator/tests/common/mod.rs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::{collections::HashMap, path::PathBuf}; + +use python_finder::{known::KnownPaths, messaging::MessageDispatcher}; +use serde_json::Value; + +#[allow(dead_code)] +pub fn test_file_path(paths: &[&str]) -> String { + // let parts: Vec = paths.iter().map(|p| p.to_string()).collect(); + let mut root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + paths.iter().for_each(|p| root.push(p)); + + root.to_string_lossy().to_string() +} + +#[allow(dead_code)] +pub fn join_test_paths(paths: &[&str]) -> String { + let path: PathBuf = paths.iter().map(|p| p.to_string()).collect(); + path.to_string_lossy().to_string() +} + +pub struct TestDispatcher { + pub messages: Vec, +} +pub trait TestMessages { + fn get_messages(&self) -> Vec; +} + +pub fn create_test_dispatcher() -> TestDispatcher { + impl MessageDispatcher for TestDispatcher { + fn send_message(&mut self, message: T) -> () { + self.messages.push(serde_json::to_string(&message).unwrap()); + } + fn log_debug(&mut self, _message: &str) -> () {} + fn log_error(&mut self, _message: &str) -> () {} + fn log_info(&mut self, _message: &str) -> () {} + fn log_warning(&mut self, _message: &str) -> () {} + } + impl TestMessages for TestDispatcher { + fn get_messages(&self) -> Vec { + self.messages.clone() + } + } + TestDispatcher { + messages: Vec::new(), + } +} +pub struct TestKnown { + vars: HashMap, + home: Option, + globals_locations: Vec, +} +pub fn create_test_known( + vars: HashMap, + home: Option, + globals_locations: Vec, +) -> TestKnown { + impl KnownPaths for TestKnown { + fn get_env_var(&self, key: String) -> Option { + self.vars.get(&key).cloned() + } + fn get_user_home(&self) -> Option { + self.home.clone() + } + fn get_know_global_search_locations(&self) -> Vec { + self.globals_locations.clone() + } + } + TestKnown { + vars, + home, + globals_locations, + } +} + +pub fn assert_messages(expected_json: &[Value], dispatcher: &TestDispatcher) { + assert_eq!( + expected_json.len(), + dispatcher.messages.len(), + "Incorrect number of messages" + ); + + if expected_json.len() == 0 { + return; + } + + let actual: serde_json::Value = serde_json::from_str(dispatcher.messages[0].as_str()).unwrap(); + assert_eq!(expected_json[0], actual); +} diff --git a/native_locator/tests/common_python_test.rs b/native_locator/tests/common_python_test.rs new file mode 100644 index 000000000000..daf2290717b6 --- /dev/null +++ b/native_locator/tests/common_python_test.rs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use python_finder::common_python; +use serde_json::json; +use std::collections::HashMap; +mod common; +use crate::common::{ + assert_messages, join_test_paths, create_test_dispatcher, create_test_known, test_file_path, +}; + +#[test] +#[cfg(unix)] +fn find_python_in_path_this() { + let unix_python = test_file_path(&["tests/unix/known"]); + let unix_python_exe = join_test_paths(&[unix_python.as_str(), "python"]); + + let mut dispatcher = create_test_dispatcher(); + let known = create_test_known( + HashMap::from([("PATH".to_string(), unix_python.clone())]), + Some(unix_python.clone()), + Vec::new(), + ); + + common_python::find_and_report(&mut dispatcher, &known); + + assert_eq!(dispatcher.messages.len(), 1); + let expected_json = json!({"name":"Python","pythonExecutablePath":[unix_python_exe.clone()],"category":"System","version":null,"activatedRun":null,"envPath":unix_python.clone()}); + assert_messages(&[expected_json], &dispatcher); +} diff --git a/native_locator/tests/conda_test.rs b/native_locator/tests/conda_test.rs new file mode 100644 index 000000000000..54fb0194b783 --- /dev/null +++ b/native_locator/tests/conda_test.rs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use serde_json::json; +use std::collections::HashMap; +mod common; +use crate::common::{ + assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, +}; + +use python_finder::conda; +// use serde::{Deserialize, Serialize}; +// use std::{env, path::PathBuf}; + +#[test] +#[cfg(unix)] +fn does_not_find_any_conda_envs() { + let mut dispatcher = create_test_dispatcher(); + let known = create_test_known( + HashMap::from([("PATH".to_string(), "".to_string())]), + Some("SOME_BOGUS_HOME_DIR".to_string()), + Vec::new(), + ); + + conda::find_and_report(&mut dispatcher, &known); + + assert_eq!(dispatcher.messages.len(), 0); +} + +#[test] +#[cfg(unix)] +fn find_conda_exe_and_empty_envs() { + let conda_dir = test_file_path(&["tests/unix/conda_without_envs"]); + + let mut dispatcher = create_test_dispatcher(); + let known = create_test_known( + HashMap::from([("PATH".to_string(), conda_dir.clone())]), + Some("SOME_BOGUS_HOME_DIR".to_string()), + Vec::new(), + ); + + conda::find_and_report(&mut dispatcher, &known); + + let conda_exe = join_test_paths(&[conda_dir.clone().as_str(), "conda"]); + let expected_json = json!({"jsonrpc":"2.0","method":"envManager","params":{"executablePath":[conda_exe.clone()],"version":null}}); + assert_messages(&[expected_json], &dispatcher) +} +#[test] +#[cfg(unix)] +fn finds_two_conda_envs_from_txt() { + use std::fs; + + let conda_dir = test_file_path(&["tests/unix/conda"]); + let conda_1 = join_test_paths(&[conda_dir.clone().as_str(), "envs/one"]); + let conda_2 = join_test_paths(&[conda_dir.clone().as_str(), "envs/two"]); + let _ = fs::write( + "tests/unix/conda/.conda/environments.txt", + format!("{}\n{}", conda_1.clone(), conda_2.clone()), + ); + + let mut dispatcher = create_test_dispatcher(); + let known = create_test_known( + HashMap::from([("PATH".to_string(), conda_dir.clone())]), + Some(conda_dir.clone()), + Vec::new(), + ); + + conda::find_and_report(&mut dispatcher, &known); + + let conda_exe = join_test_paths(&[conda_dir.clone().as_str(), "conda"]); + let conda_1_exe = join_test_paths(&[conda_1.clone().as_str(), "python"]); + let conda_2_exe = join_test_paths(&[conda_2.clone().as_str(), "python"]); + + let expected_conda_env = json!({"jsonrpc":"2.0","method":"envManager","params":{"executablePath":[conda_exe.clone()],"version":null}}); + let expected_conda_1 = json!({"jsonrpc":"2.0","method":"pythonEnvironment","params":{"name":"envs/one","pythonExecutablePath":[conda_1_exe.clone()],"category":"conda","version":"10.0.1","activatedRun":[conda_exe.clone(),"run","-n","envs/one","python"],"envPath":conda_1.clone()}}); + let expected_conda_2 = json!({"jsonrpc":"2.0","method":"pythonEnvironment","params":{"name":"envs/two","pythonExecutablePath":[conda_2_exe.clone()],"category":"conda","version":null,"activatedRun":[conda_exe.clone(),"run","-n","envs/two","python"],"envPath":conda_2.clone()}}); + assert_messages( + &[expected_conda_env, expected_conda_1, expected_conda_2], + &dispatcher, + ) +} diff --git a/native_locator/tests/unix/conda/.conda/environments.txt b/native_locator/tests/unix/conda/.conda/environments.txt new file mode 100644 index 000000000000..908019719b55 --- /dev/null +++ b/native_locator/tests/unix/conda/.conda/environments.txt @@ -0,0 +1,2 @@ +/Users/donjayamanne/Development/vsc/vscode-python/native_locator/tests/unix/conda/envs/one +/Users/donjayamanne/Development/vsc/vscode-python/native_locator/tests/unix/conda/envs/two \ No newline at end of file diff --git a/native_locator/tests/unix/conda/conda b/native_locator/tests/unix/conda/conda new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/native_locator/tests/unix/conda/envs/one/conda-meta/python-10.0.1-hdf0ec26_0_cpython.json b/native_locator/tests/unix/conda/envs/one/conda-meta/python-10.0.1-hdf0ec26_0_cpython.json new file mode 100644 index 000000000000..23127993ac05 --- /dev/null +++ b/native_locator/tests/unix/conda/envs/one/conda-meta/python-10.0.1-hdf0ec26_0_cpython.json @@ -0,0 +1 @@ +10.1.1 diff --git a/native_locator/tests/unix/conda/envs/one/python b/native_locator/tests/unix/conda/envs/one/python new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/native_locator/tests/unix/conda/envs/two/python b/native_locator/tests/unix/conda/envs/two/python new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/native_locator/tests/unix/conda_without_envs/.conda/environments.txt b/native_locator/tests/unix/conda_without_envs/.conda/environments.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/native_locator/tests/unix/conda_without_envs/conda b/native_locator/tests/unix/conda_without_envs/conda new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/native_locator/tests/unix/known/python b/native_locator/tests/unix/known/python new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/native_locator/tests/unix/known/python.version b/native_locator/tests/unix/known/python.version new file mode 100644 index 000000000000..4044f90867df --- /dev/null +++ b/native_locator/tests/unix/known/python.version @@ -0,0 +1 @@ +12.0.0 From 8401dcddc99a88a3dc8bb32cd200a7005ec6f4a6 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 7 May 2024 09:08:57 +1000 Subject: [PATCH 2/7] fix typo --- native_locator/src/known.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native_locator/src/known.rs b/native_locator/src/known.rs index e622ac3cb30e..3bb3f671e3a4 100644 --- a/native_locator/src/known.rs +++ b/native_locator/src/known.rs @@ -15,7 +15,7 @@ impl KnownPaths for KnownPathsImpl { fn get_user_home(&self) -> Option { get_user_home() } - fn get_env_var(&self, key: string) -> Option { + fn get_env_var(&self, key: String) -> Option { get_env_var(key) } fn get_know_global_search_locations(&self) -> Vec { From 51cfdda07467d925d5038cff80842944cd7e5aa7 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 7 May 2024 09:12:20 +1000 Subject: [PATCH 3/7] revert --- .github/workflows/pr-check.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index fd2a47645e50..19556a38e30c 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -188,9 +188,6 @@ jobs: - name: Install test requirements run: python -m pip install --upgrade -r build/test-requirements.txt - - name: Native Locator (Rust) tests - run: nox --session native_test - - name: Install functional test requirements run: python -m pip install --upgrade -r ./build/functional-test-requirements.txt if: matrix.test-suite == 'functional' From 2bf19f461c0871b4edde83c2f7b37d567ec60099 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 7 May 2024 09:18:31 +1000 Subject: [PATCH 4/7] Fixes --- native_locator/tests/common_python_test.rs | 13 +++++------ native_locator/tests/conda_test.rs | 25 ++++++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/native_locator/tests/common_python_test.rs b/native_locator/tests/common_python_test.rs index daf2290717b6..d33f109d04b5 100644 --- a/native_locator/tests/common_python_test.rs +++ b/native_locator/tests/common_python_test.rs @@ -1,17 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use python_finder::common_python; -use serde_json::json; -use std::collections::HashMap; mod common; -use crate::common::{ - assert_messages, join_test_paths, create_test_dispatcher, create_test_known, test_file_path, -}; #[test] #[cfg(unix)] fn find_python_in_path_this() { + use crate::common::{ + assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, + }; + use python_finder::common_python; + use serde_json::json; + use std::collections::HashMap; + let unix_python = test_file_path(&["tests/unix/known"]); let unix_python_exe = join_test_paths(&[unix_python.as_str(), "python"]); diff --git a/native_locator/tests/conda_test.rs b/native_locator/tests/conda_test.rs index 54fb0194b783..295b2e95aa8f 100644 --- a/native_locator/tests/conda_test.rs +++ b/native_locator/tests/conda_test.rs @@ -1,20 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use serde_json::json; -use std::collections::HashMap; mod common; -use crate::common::{ - assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, -}; - -use python_finder::conda; -// use serde::{Deserialize, Serialize}; -// use std::{env, path::PathBuf}; #[test] #[cfg(unix)] fn does_not_find_any_conda_envs() { + use crate::common::{create_test_dispatcher, create_test_known}; + use python_finder::conda; + use std::collections::HashMap; + let mut dispatcher = create_test_dispatcher(); let known = create_test_known( HashMap::from([("PATH".to_string(), "".to_string())]), @@ -30,6 +25,12 @@ fn does_not_find_any_conda_envs() { #[test] #[cfg(unix)] fn find_conda_exe_and_empty_envs() { + use crate::common::{ + assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, + }; + use python_finder::conda; + use serde_json::json; + use std::collections::HashMap; let conda_dir = test_file_path(&["tests/unix/conda_without_envs"]); let mut dispatcher = create_test_dispatcher(); @@ -48,6 +49,12 @@ fn find_conda_exe_and_empty_envs() { #[test] #[cfg(unix)] fn finds_two_conda_envs_from_txt() { + use crate::common::{ + assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, + }; + use python_finder::conda; + use serde_json::json; + use std::collections::HashMap; use std::fs; let conda_dir = test_file_path(&["tests/unix/conda"]); From 3d78e5786db4d89661783ef75f1f6a133e55ca79 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 7 May 2024 09:22:16 +1000 Subject: [PATCH 5/7] Fix windows tests --- native_locator/src/main.rs | 2 +- native_locator/tests/common/mod.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/native_locator/src/main.rs b/native_locator/src/main.rs index 5e6b8a072241..2f105f3700e6 100644 --- a/native_locator/src/main.rs +++ b/native_locator/src/main.rs @@ -30,7 +30,7 @@ fn main() { // Finds Windows Store, Known Path, and Registry pythons #[cfg(windows)] - windows_python::find_and_report(&dispatcher, &known_paths); + windows_python::find_and_report(&mut dispatcher, &known_paths); match now.elapsed() { Ok(elapsed) => { diff --git a/native_locator/tests/common/mod.rs b/native_locator/tests/common/mod.rs index ef8cee57e738..695c578a943c 100644 --- a/native_locator/tests/common/mod.rs +++ b/native_locator/tests/common/mod.rs @@ -29,6 +29,7 @@ pub trait TestMessages { fn get_messages(&self) -> Vec; } +#[allow(dead_code)] pub fn create_test_dispatcher() -> TestDispatcher { impl MessageDispatcher for TestDispatcher { fn send_message(&mut self, message: T) -> () { @@ -53,6 +54,7 @@ pub struct TestKnown { home: Option, globals_locations: Vec, } +#[allow(dead_code)] pub fn create_test_known( vars: HashMap, home: Option, @@ -76,6 +78,7 @@ pub fn create_test_known( } } +#[allow(dead_code)] pub fn assert_messages(expected_json: &[Value], dispatcher: &TestDispatcher) { assert_eq!( expected_json.len(), From d32fda6cb62df8ae3a4ec381d3ae1e5b8009d5d6 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 7 May 2024 09:32:10 +1000 Subject: [PATCH 6/7] Rename known to Environment --- native_locator/src/common_python.rs | 8 +-- native_locator/src/conda.rs | 62 +++++++++++----------- native_locator/src/known.rs | 8 +-- native_locator/src/main.rs | 9 ++-- native_locator/tests/common/mod.rs | 12 ++--- native_locator/tests/common_python_test.rs | 5 +- native_locator/tests/conda_test.rs | 14 ++--- 7 files changed, 59 insertions(+), 59 deletions(-) diff --git a/native_locator/src/common_python.rs b/native_locator/src/common_python.rs index 7ce8702b7929..f3fd8d682009 100644 --- a/native_locator/src/common_python.rs +++ b/native_locator/src/common_python.rs @@ -36,9 +36,9 @@ fn report_path_python(dispatcher: &mut impl messaging::MessageDispatcher, path: fn report_python_on_path( dispatcher: &mut impl messaging::MessageDispatcher, - known_paths: &impl known::KnownPaths, + environment: &impl known::Environment, ) { - if let Some(paths) = known_paths.get_env_var("PATH".to_string()) { + if let Some(paths) = environment.get_env_var("PATH".to_string()) { let bin = if cfg!(windows) { "python.exe" } else { @@ -53,7 +53,7 @@ fn report_python_on_path( pub fn find_and_report( dispatcher: &mut impl messaging::MessageDispatcher, - known_paths: &impl known::KnownPaths, + environment: &impl known::Environment, ) { - report_python_on_path(dispatcher, known_paths); + report_python_on_path(dispatcher, environment); } diff --git a/native_locator/src/conda.rs b/native_locator/src/conda.rs index 2d984685eb0d..1bb579e5d218 100644 --- a/native_locator/src/conda.rs +++ b/native_locator/src/conda.rs @@ -127,8 +127,8 @@ fn get_conda_bin_names() -> Vec<&'static str> { } /// Find the conda binary on the PATH environment variable -fn find_conda_binary_on_path(known_paths: &impl known::KnownPaths) -> Option { - let paths = known_paths.get_env_var("PATH".to_string())?; +fn find_conda_binary_on_path(environment: &impl known::Environment) -> Option { + let paths = environment.get_env_var("PATH".to_string())?; for path in env::split_paths(&paths) { for bin in get_conda_bin_names() { let conda_path = path.join(bin); @@ -159,11 +159,13 @@ fn find_python_binary_path(env_path: &Path) -> Option { } #[cfg(windows)] -fn get_known_conda_locations(known_paths_provider: &impl known::KnownPaths) -> Vec { - let user_profile = known_paths_provider.get_env_var("USERPROFILE".to_string()).unwrap(); - let program_data = known_paths_provider.get_env_var("PROGRAMDATA".to_string()).unwrap(); - let all_user_profile = known_paths_provider.get_env_var("ALLUSERSPROFILE".to_string()).unwrap(); - let home_drive = known_paths_provider.get_env_var("HOMEDRIVE".to_string()).unwrap(); +fn get_known_conda_locations(environment: &impl known::Environment) -> Vec { + let user_profile = environment.get_env_var("USERPROFILE".to_string()).unwrap(); + let program_data = environment.get_env_var("PROGRAMDATA".to_string()).unwrap(); + let all_user_profile = environment + .get_env_var("ALLUSERSPROFILE".to_string()) + .unwrap(); + let home_drive = environment.get_env_var("HOMEDRIVE".to_string()).unwrap(); let mut known_paths = vec![ Path::new(&user_profile).join("Anaconda3\\Scripts"), Path::new(&program_data).join("Anaconda3\\Scripts"), @@ -174,12 +176,12 @@ fn get_known_conda_locations(known_paths_provider: &impl known::KnownPaths) -> V Path::new(&all_user_profile).join("Miniconda3\\Scripts"), Path::new(&home_drive).join("Miniconda3\\Scripts"), ]; - known_paths.append(&mut known_paths_provider.get_know_global_search_locations()); + known_paths.append(&mut environment.get_know_global_search_locations()); known_paths } #[cfg(unix)] -fn get_known_conda_locations(known_paths_provider: &impl known::KnownPaths) -> Vec { +fn get_known_conda_locations(environment: &impl known::Environment) -> Vec { let mut known_paths = vec![ PathBuf::from("/opt/anaconda3/bin"), PathBuf::from("/opt/miniconda3/bin"), @@ -200,16 +202,14 @@ fn get_known_conda_locations(known_paths_provider: &impl known::KnownPaths) -> V PathBuf::from("/anaconda3/bin"), PathBuf::from("/miniconda3/bin"), ]; - known_paths.append(&mut known_paths_provider.get_know_global_search_locations()); + known_paths.append(&mut environment.get_know_global_search_locations()); known_paths } /// Find conda binary in known locations -fn find_conda_binary_in_known_locations( - known_paths_provider: &impl known::KnownPaths, -) -> Option { +fn find_conda_binary_in_known_locations(environment: &impl known::Environment) -> Option { let conda_bin_names = get_conda_bin_names(); - let known_locations = get_known_conda_locations(known_paths_provider); + let known_locations = get_known_conda_locations(environment); for location in known_locations { for bin in &conda_bin_names { let conda_path = location.join(bin); @@ -223,19 +223,17 @@ fn find_conda_binary_in_known_locations( } /// Find the conda binary on the system -pub fn find_conda_binary(known_paths_provider: &impl known::KnownPaths) -> Option { - let conda_binary_on_path = find_conda_binary_on_path(known_paths_provider); +pub fn find_conda_binary(environment: &impl known::Environment) -> Option { + let conda_binary_on_path = find_conda_binary_on_path(environment); match conda_binary_on_path { Some(conda_binary_on_path) => Some(conda_binary_on_path), - None => find_conda_binary_in_known_locations(known_paths_provider), + None => find_conda_binary_in_known_locations(environment), } } -fn get_conda_envs_from_environment_txt( - known_paths_provider: &impl known::KnownPaths, -) -> Vec { +fn get_conda_envs_from_environment_txt(environment: &impl known::Environment) -> Vec { let mut envs = vec![]; - let home = known_paths_provider.get_user_home(); + let home = environment.get_user_home(); match home { Some(home) => { let home = Path::new(&home); @@ -256,10 +254,10 @@ fn get_conda_envs_from_environment_txt( fn get_known_env_locations( conda_bin: PathBuf, - known_paths_provider: &impl known::KnownPaths, + environment: &impl known::Environment, ) -> Vec { let mut paths = vec![]; - let home = known_paths_provider.get_user_home(); + let home = environment.get_user_home(); match home { Some(home) => { let home = Path::new(&home); @@ -291,10 +289,10 @@ fn get_known_env_locations( fn get_conda_envs_from_known_env_locations( conda_bin: PathBuf, - known_paths_provider: &impl known::KnownPaths, + environment: &impl known::Environment, ) -> Vec { let mut envs = vec![]; - for location in get_known_env_locations(conda_bin, known_paths_provider) { + for location in get_known_env_locations(conda_bin, environment) { if is_conda_environment(&Path::new(&location)) { envs.push(location.to_string()); } @@ -334,16 +332,16 @@ struct CondaEnv { fn get_distinct_conda_envs( conda_bin: PathBuf, - known_paths_provider: &impl known::KnownPaths, + environment: &impl known::Environment, ) -> Vec { - let mut envs = get_conda_envs_from_environment_txt(known_paths_provider); + let mut envs = get_conda_envs_from_environment_txt(environment); let mut known_envs = - get_conda_envs_from_known_env_locations(conda_bin.to_path_buf(), known_paths_provider); + get_conda_envs_from_known_env_locations(conda_bin.to_path_buf(), environment); envs.append(&mut known_envs); envs.sort(); envs.dedup(); - let locations = get_known_env_locations(conda_bin, known_paths_provider); + let locations = get_known_env_locations(conda_bin, environment); let mut conda_envs = vec![]; for env in envs { let env = Path::new(&env); @@ -381,9 +379,9 @@ fn get_distinct_conda_envs( pub fn find_and_report( dispatcher: &mut impl messaging::MessageDispatcher, - known_paths_provider: &impl known::KnownPaths, + environment: &impl known::Environment, ) { - let conda_binary = find_conda_binary(known_paths_provider); + let conda_binary = find_conda_binary(environment); match conda_binary { Some(conda_binary) => { let params = @@ -391,7 +389,7 @@ pub fn find_and_report( let message = messaging::EnvManagerMessage::new(params); dispatcher.send_message(message); - let envs = get_distinct_conda_envs(conda_binary.to_path_buf(), known_paths_provider); + let envs = get_distinct_conda_envs(conda_binary.to_path_buf(), environment); for env in envs { let executable = find_python_binary_path(Path::new(&env.path)); let params = messaging::PythonEnvironment::new( diff --git a/native_locator/src/known.rs b/native_locator/src/known.rs index 3bb3f671e3a4..8c2fdb4386e1 100644 --- a/native_locator/src/known.rs +++ b/native_locator/src/known.rs @@ -2,16 +2,16 @@ // Licensed under the MIT License. use std::{env, path::PathBuf}; -pub trait KnownPaths { +pub trait Environment { fn get_user_home(&self) -> Option; fn get_env_var(&self, key: String) -> Option; fn get_know_global_search_locations(&self) -> Vec; } -pub struct KnownPathsImpl {} +pub struct EnvironmentApi {} #[cfg(windows)] -impl KnownPaths for KnownPathsImpl { +impl Environment for EnvironmentApi { fn get_user_home(&self) -> Option { get_user_home() } @@ -24,7 +24,7 @@ impl KnownPaths for KnownPathsImpl { } #[cfg(unix)] -impl KnownPaths for KnownPathsImpl { +impl Environment for EnvironmentApi { fn get_user_home(&self) -> Option { get_user_home() } diff --git a/native_locator/src/main.rs b/native_locator/src/main.rs index 2f105f3700e6..8a11101404dd 100644 --- a/native_locator/src/main.rs +++ b/native_locator/src/main.rs @@ -3,7 +3,7 @@ use std::time::SystemTime; -use known::KnownPathsImpl; +use known::EnvironmentApi; use messaging::{create_dispatcher, MessageDispatcher}; mod common_python; @@ -16,17 +16,16 @@ mod windows_python; fn main() { let mut dispatcher = create_dispatcher(); - // let dispatcher = JsonRpcDispatcher {}; - let known_paths = KnownPathsImpl {}; + let environment = EnvironmentApi {}; dispatcher.log_info("Starting Native Locator"); let now = SystemTime::now(); // Finds python on PATH - common_python::find_and_report(&mut dispatcher, &known_paths); + common_python::find_and_report(&mut dispatcher, &environment); // Finds conda binary and conda environments - conda::find_and_report(&mut dispatcher, &known_paths); + conda::find_and_report(&mut dispatcher, &environment); // Finds Windows Store, Known Path, and Registry pythons #[cfg(windows)] diff --git a/native_locator/tests/common/mod.rs b/native_locator/tests/common/mod.rs index 695c578a943c..5ff67224d258 100644 --- a/native_locator/tests/common/mod.rs +++ b/native_locator/tests/common/mod.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::PathBuf}; -use python_finder::{known::KnownPaths, messaging::MessageDispatcher}; +use python_finder::{known::Environment, messaging::MessageDispatcher}; use serde_json::Value; #[allow(dead_code)] @@ -49,18 +49,18 @@ pub fn create_test_dispatcher() -> TestDispatcher { messages: Vec::new(), } } -pub struct TestKnown { +pub struct TestEnvironment { vars: HashMap, home: Option, globals_locations: Vec, } #[allow(dead_code)] -pub fn create_test_known( +pub fn create_test_environment( vars: HashMap, home: Option, globals_locations: Vec, -) -> TestKnown { - impl KnownPaths for TestKnown { +) -> TestEnvironment { + impl Environment for TestEnvironment { fn get_env_var(&self, key: String) -> Option { self.vars.get(&key).cloned() } @@ -71,7 +71,7 @@ pub fn create_test_known( self.globals_locations.clone() } } - TestKnown { + TestEnvironment { vars, home, globals_locations, diff --git a/native_locator/tests/common_python_test.rs b/native_locator/tests/common_python_test.rs index d33f109d04b5..a05be51e7218 100644 --- a/native_locator/tests/common_python_test.rs +++ b/native_locator/tests/common_python_test.rs @@ -7,7 +7,8 @@ mod common; #[cfg(unix)] fn find_python_in_path_this() { use crate::common::{ - assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, + assert_messages, create_test_dispatcher, create_test_environment, join_test_paths, + test_file_path, }; use python_finder::common_python; use serde_json::json; @@ -17,7 +18,7 @@ fn find_python_in_path_this() { let unix_python_exe = join_test_paths(&[unix_python.as_str(), "python"]); let mut dispatcher = create_test_dispatcher(); - let known = create_test_known( + let known = create_test_environment( HashMap::from([("PATH".to_string(), unix_python.clone())]), Some(unix_python.clone()), Vec::new(), diff --git a/native_locator/tests/conda_test.rs b/native_locator/tests/conda_test.rs index 295b2e95aa8f..ef4d72c6c552 100644 --- a/native_locator/tests/conda_test.rs +++ b/native_locator/tests/conda_test.rs @@ -6,12 +6,12 @@ mod common; #[test] #[cfg(unix)] fn does_not_find_any_conda_envs() { - use crate::common::{create_test_dispatcher, create_test_known}; + use crate::common::{create_test_dispatcher, create_test_environment}; use python_finder::conda; use std::collections::HashMap; let mut dispatcher = create_test_dispatcher(); - let known = create_test_known( + let known = create_test_environment( HashMap::from([("PATH".to_string(), "".to_string())]), Some("SOME_BOGUS_HOME_DIR".to_string()), Vec::new(), @@ -26,7 +26,8 @@ fn does_not_find_any_conda_envs() { #[cfg(unix)] fn find_conda_exe_and_empty_envs() { use crate::common::{ - assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, + assert_messages, create_test_dispatcher, create_test_environment, join_test_paths, + test_file_path, }; use python_finder::conda; use serde_json::json; @@ -34,7 +35,7 @@ fn find_conda_exe_and_empty_envs() { let conda_dir = test_file_path(&["tests/unix/conda_without_envs"]); let mut dispatcher = create_test_dispatcher(); - let known = create_test_known( + let known = create_test_environment( HashMap::from([("PATH".to_string(), conda_dir.clone())]), Some("SOME_BOGUS_HOME_DIR".to_string()), Vec::new(), @@ -50,7 +51,8 @@ fn find_conda_exe_and_empty_envs() { #[cfg(unix)] fn finds_two_conda_envs_from_txt() { use crate::common::{ - assert_messages, create_test_dispatcher, create_test_known, join_test_paths, test_file_path, + assert_messages, create_test_dispatcher, create_test_environment, join_test_paths, + test_file_path, }; use python_finder::conda; use serde_json::json; @@ -66,7 +68,7 @@ fn finds_two_conda_envs_from_txt() { ); let mut dispatcher = create_test_dispatcher(); - let known = create_test_known( + let known = create_test_environment( HashMap::from([("PATH".to_string(), conda_dir.clone())]), Some(conda_dir.clone()), Vec::new(), From da10bd922ab8832e460bbc7f55ca205c63606873 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 7 May 2024 09:35:14 +1000 Subject: [PATCH 7/7] Address review comments --- native_locator/src/windows_python.rs | 8 ++++---- native_locator/tests/{common/mod.rs => common.rs} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename native_locator/tests/{common/mod.rs => common.rs} (100%) diff --git a/native_locator/src/windows_python.rs b/native_locator/src/windows_python.rs index 7776a3481e6d..5f5d53fafa2e 100644 --- a/native_locator/src/windows_python.rs +++ b/native_locator/src/windows_python.rs @@ -20,9 +20,9 @@ fn report_path_python(path: &str, dispatcher: &mut impl messaging::MessageDispat fn report_windows_store_python( dispatcher: &mut impl messaging::MessageDispatcher, - known_paths_provider: &impl known::KnownPaths, + environment: &impl known::Environment, ) { - let home = known_paths_provider.get_user_home(); + let home = environment.get_user_home(); match home { Some(home) => { let apps_path = Path::new(&home) @@ -63,8 +63,8 @@ fn report_registry_pythons() {} #[allow(dead_code)] pub fn find_and_report( dispatcher: &mut impl messaging::MessageDispatcher, - known_paths_provider: &impl known::KnownPaths, + environment: &impl known::Environment, ) { - report_windows_store_python(dispatcher, known_paths_provider); + report_windows_store_python(dispatcher, environment); report_registry_pythons(); } diff --git a/native_locator/tests/common/mod.rs b/native_locator/tests/common.rs similarity index 100% rename from native_locator/tests/common/mod.rs rename to native_locator/tests/common.rs