diff --git a/src/cli/add.rs b/src/cli/add.rs index 3a1033040..840ad3e02 100644 --- a/src/cli/add.rs +++ b/src/cli/add.rs @@ -5,6 +5,7 @@ use crate::{ }; use anyhow::Context; use clap::Parser; +use indexmap::IndexMap; use itertools::Itertools; use rattler_conda_types::{ version_spec::VersionOperator, MatchSpec, NamelessMatchSpec, Platform, Version, VersionSpec, @@ -39,19 +40,45 @@ pub struct Args { /// The path to 'pixi.toml' #[arg(long)] pub manifest_path: Option, + + /// This is a host dependency + #[arg(long, conflicts_with = "build")] + pub host: bool, + + /// This is a build dependency + #[arg(long, conflicts_with = "host")] + pub build: bool, +} + +#[derive(Debug, Copy, Clone)] +pub enum SpecType { + Host, + Build, + Run, +} + +impl SpecType { + pub fn from_args(args: &Args) -> Self { + if args.host { + Self::Host + } else if args.build { + Self::Build + } else { + Self::Run + } + } } pub async fn execute(args: Args) -> anyhow::Result<()> { - let mut project = match args.manifest_path { - Some(path) => Project::load(path.as_path())?, - None => Project::discover()?, - }; - add_specs_to_project(&mut project, args.specs).await + let mut project = Project::load_or_else_discover(args.manifest_path.as_deref())?; + let spec_type = SpecType::from_args(&args); + add_specs_to_project(&mut project, args.specs, spec_type).await } pub async fn add_specs_to_project( project: &mut Project, specs: Vec, + spec_type: SpecType, ) -> anyhow::Result<()> { // Split the specs into package name and version specifier let new_specs = specs @@ -70,7 +97,11 @@ pub async fn add_specs_to_project( // Determine the best version per platform let mut best_versions = HashMap::new(); for platform in project.platforms() { - let current_specs = project.dependencies(*platform)?; + let current_specs = match spec_type { + SpecType::Host => project.host_dependencies(*platform)?, + SpecType::Build => project.build_dependencies(*platform)?, + SpecType::Run => project.dependencies(*platform)?, + }; // Solve the environment with the new specs added let solved_versions = match determine_best_version( &new_specs, @@ -120,14 +151,14 @@ pub async fn add_specs_to_project( spec }; let spec = MatchSpec::from_nameless(updated_spec, Some(name)); - project.add_dependency(&spec)?; + match spec_type { + SpecType::Host => project.add_host_dependency(&spec)?, + SpecType::Build => project.add_build_dependency(&spec)?, + SpecType::Run => project.add_dependency(&spec)?, + } added_specs.push(spec); } - // TODO why is this only needed in the test? - // project.save()?; - // project.reload()?; - // Update the lock file and write to disk update_lock_file( project, @@ -150,7 +181,7 @@ pub async fn add_specs_to_project( /// Given several specs determines the highest installable version for them. pub fn determine_best_version( new_specs: &HashMap, - current_specs: &HashMap, + current_specs: &IndexMap, sparse_repo_data: &[SparseRepoData], platform: Platform, ) -> anyhow::Result> { diff --git a/src/cli/install.rs b/src/cli/install.rs index 9d49f952a..565af036a 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; pub struct Args { /// The path to 'pixi.toml' #[arg(long)] - manifest_path: Option, + pub manifest_path: Option, } pub async fn execute(args: Args) -> anyhow::Result<()> { diff --git a/src/environment.rs b/src/environment.rs index 696847bcb..f9c2b4925 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -153,7 +153,7 @@ pub fn lock_file_up_to_date(project: &Project, lock_file: &CondaLock) -> anyhow: for platform in platforms.iter().cloned() { // Check if all dependencies exist in the lock-file. let dependencies = project - .dependencies(platform)? + .all_dependencies(platform)? .into_iter() .collect::>(); @@ -301,7 +301,7 @@ pub async fn update_lock_file( // Empty match-specs because these differ per platform let mut builder = LockFileBuilder::new(channels, platforms.iter().cloned(), vec![]); for platform in platforms.iter().cloned() { - let dependencies = project.dependencies(platform)?; + let dependencies = project.all_dependencies(platform)?; let match_specs = dependencies .iter() .map(|(name, constraint)| { diff --git a/src/project/manifest.rs b/src/project/manifest.rs index c211f42f6..c59a0b987 100644 --- a/src/project/manifest.rs +++ b/src/project/manifest.rs @@ -36,6 +36,22 @@ pub struct ProjectManifest { #[serde_as(as = "IndexMap<_, DisplayFromStr>")] pub dependencies: IndexMap, + /// The host-dependencies of the project. + /// + /// We use an [`IndexMap`] to preserve the order in which the items where defined in the + /// manifest. + #[serde(default, rename = "host-dependencies")] + #[serde_as(as = "Option>")] + pub host_dependencies: Option>, + + /// The build-dependencies of the project. + /// + /// We use an [`IndexMap`] to preserve the order in which the items where defined in the + /// manifest. + #[serde(default, rename = "build-dependencies")] + #[serde_as(as = "Option>")] + pub build_dependencies: Option>, + /// Target specific configuration. /// /// We use an [`IndexMap`] to preserve the order in which the items where defined in the @@ -108,6 +124,16 @@ pub struct TargetMetadata { #[serde(default)] #[serde_as(as = "IndexMap<_, DisplayFromStr>")] pub dependencies: IndexMap, + + /// The host-dependencies of the project. + #[serde(default, rename = "host-dependencies")] + #[serde_as(as = "Option>")] + pub host_dependencies: Option>, + + /// The build-dependencies of the project. + #[serde(default, rename = "build-dependencies")] + #[serde_as(as = "Option>")] + pub build_dependencies: Option>, } /// Describes the contents of the `[package]` section of the project manifest. @@ -292,6 +318,27 @@ mod test { ); } + #[test] + fn test_dependency_types() { + let contents = format!( + r#" + {PROJECT_BOILERPLATE} + [dependencies] + my-game = "1.0.0" + + [build-dependencies] + cmake = "*" + + [host-dependencies] + sdl2 = "*" + "# + ); + + assert_debug_snapshot!( + toml_edit::de::from_str::(&contents).expect("parsing should succeed!") + ); + } + #[test] fn test_invalid_target_specific() { let examples = [r#"[target.foobar.dependencies] diff --git a/src/project/mod.rs b/src/project/mod.rs index 533073ef5..36e27fa75 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -8,12 +8,12 @@ use crate::project::manifest::{ProjectManifest, TargetMetadata, TargetSelector}; use crate::report_error::ReportError; use anyhow::Context; use ariadne::{Label, Report, ReportKind, Source}; +use indexmap::IndexMap; use rattler_conda_types::{ Channel, ChannelConfig, MatchSpec, NamelessMatchSpec, Platform, Version, }; use rattler_virtual_packages::VirtualPackage; use std::{ - collections::HashMap, env, fs, path::{Path, PathBuf}, }; @@ -111,7 +111,7 @@ impl Project { pub fn dependencies( &self, platform: Platform, - ) -> anyhow::Result> { + ) -> anyhow::Result> { // Get the base dependencies (defined in the `[dependencies]` section) let base_dependencies = self.manifest.dependencies.iter(); @@ -129,6 +129,63 @@ impl Project { .collect()) } + /// Returns the build dependencies of the project. + pub fn build_dependencies( + &self, + platform: Platform, + ) -> anyhow::Result> { + // Get the base dependencies (defined in the `[build-dependencies]` section) + let base_dependencies = self.manifest.build_dependencies.iter(); + + // Get the platform specific dependencies in the order they were defined. + let platform_specific = self + .target_specific_metadata(platform) + .flat_map(|target| target.build_dependencies.iter()); + + // Combine the specs. + // + // Note that if a dependency was specified twice the platform specific one "wins". + Ok(base_dependencies + .chain(platform_specific) + .flatten() + .map(|(name, spec)| (name.clone(), spec.clone())) + .collect()) + } + + /// Returns the host dependencies of the project. + pub fn host_dependencies( + &self, + platform: Platform, + ) -> anyhow::Result> { + // Get the base dependencies (defined in the `[host-dependencies]` section) + let base_dependencies = self.manifest.host_dependencies.iter(); + + // Get the platform specific dependencies in the order they were defined. + let platform_specific = self + .target_specific_metadata(platform) + .flat_map(|target| target.host_dependencies.iter()); + + // Combine the specs. + // + // Note that if a dependency was specified twice the platform specific one "wins". + Ok(base_dependencies + .chain(platform_specific) + .flatten() + .map(|(name, spec)| (name.clone(), spec.clone())) + .collect()) + } + + /// Returns all dependencies of the project. These are the run, host, build dependency sets combined. + pub fn all_dependencies( + &self, + platform: Platform, + ) -> anyhow::Result> { + let mut dependencies = self.dependencies(platform)?; + dependencies.extend(self.host_dependencies(platform)?); + dependencies.extend(self.build_dependencies(platform)?); + Ok(dependencies) + } + /// Returns all the targets specific metadata that apply with the given context. /// TODO: Add more context here? /// TODO: Should we return the selector too to provide better diagnostics later? @@ -155,17 +212,17 @@ impl Project { &self.manifest.project.version } - pub fn add_dependency(&mut self, spec: &MatchSpec) -> anyhow::Result<()> { - // Find the dependencies table - let deps = &mut self.doc["dependencies"]; - + fn add_to_deps_table( + deps_table: &mut Item, + spec: &MatchSpec, + ) -> anyhow::Result<(String, NamelessMatchSpec)> { // If it doesnt exist create a proper table - if deps.is_none() { - *deps = Item::Table(Table::new()); + if deps_table.is_none() { + *deps_table = Item::Table(Table::new()); } // Cast the item into a table - let deps_table = deps.as_table_like_mut().ok_or_else(|| { + let deps_table = deps_table.as_table_like_mut().ok_or_else(|| { anyhow::anyhow!("dependencies in {} are malformed", consts::PROJECT_MANIFEST) })?; @@ -184,9 +241,48 @@ impl Project { // Store (or replace) in the document deps_table.insert(name, Item::Value(nameless.to_string().into())); - self.manifest - .dependencies - .insert(name.to_string(), nameless); + Ok((name.to_string(), nameless)) + } + + pub fn add_dependency(&mut self, spec: &MatchSpec) -> anyhow::Result<()> { + // Find the dependencies table + let deps = &mut self.doc["dependencies"]; + let (name, nameless) = Project::add_to_deps_table(deps, spec)?; + + self.manifest.dependencies.insert(name, nameless); + + Ok(()) + } + + pub fn add_host_dependency(&mut self, spec: &MatchSpec) -> anyhow::Result<()> { + // Find the dependencies table + let deps = &mut self.doc["host-dependencies"]; + let (name, nameless) = Project::add_to_deps_table(deps, spec)?; + + let host_deps = if let Some(ref mut host_dependencies) = self.manifest.host_dependencies { + host_dependencies + } else { + self.manifest.host_dependencies.insert(IndexMap::new()) + }; + + host_deps.insert(name, nameless); + + Ok(()) + } + + pub fn add_build_dependency(&mut self, spec: &MatchSpec) -> anyhow::Result<()> { + // Find the dependencies table + let deps = &mut self.doc["build-dependencies"]; + let (name, nameless) = Project::add_to_deps_table(deps, spec)?; + + let build_deps = if let Some(ref mut build_dependencies) = self.manifest.build_dependencies + { + build_dependencies + } else { + self.manifest.build_dependencies.insert(IndexMap::new()) + }; + + build_deps.insert(name, nameless); Ok(()) } @@ -315,6 +411,7 @@ pub fn find_project_root() -> Option { mod tests { use super::*; use crate::project::manifest::SystemRequirements; + use insta::assert_debug_snapshot; use rattler_conda_types::ChannelConfig; use rattler_virtual_packages::{Archspec, Cuda, LibC, Linux, Osx, VirtualPackage}; use std::str::FromStr; @@ -464,4 +561,67 @@ mod tests { assert!(toml_edit::de::from_str::(&file_content).is_err()); } } + + #[test] + fn test_dependency_sets() { + let file_contents = r#" + [dependencies] + foo = "1.0" + + [host-dependencies] + libc = "2.12" + + [build-dependencies] + bar = "1.0" + "#; + + let manifest = toml_edit::de::from_str::(&format!( + "{PROJECT_BOILERPLATE}\n{file_contents}" + )) + .unwrap(); + let project = Project { + root: Default::default(), + source: "".to_string(), + doc: Default::default(), + manifest, + }; + + assert_debug_snapshot!(project.all_dependencies(Platform::Linux64).unwrap()); + } + + #[test] + fn test_dependency_target_sets() { + let file_contents = r#" + [dependencies] + foo = "1.0" + + [host-dependencies] + libc = "2.12" + + [build-dependencies] + bar = "1.0" + + [target.linux-64.build-dependencies] + baz = "1.0" + + [target.linux-64.host-dependencies] + banksy = "1.0" + + [target.linux-64.dependencies] + wolflib = "1.0" + "#; + + let manifest = toml_edit::de::from_str::(&format!( + "{PROJECT_BOILERPLATE}\n{file_contents}" + )) + .unwrap(); + let project = Project { + root: Default::default(), + source: "".to_string(), + doc: Default::default(), + manifest, + }; + + assert_debug_snapshot!(project.all_dependencies(Platform::Linux64).unwrap()); + } } diff --git a/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap b/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap new file mode 100644 index 000000000..104a3a254 --- /dev/null +++ b/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap @@ -0,0 +1,86 @@ +--- +source: src/project/manifest.rs +expression: "toml_edit::de::from_str::(&contents).expect(\"parsing should succeed!\")" +--- +ProjectManifest { + project: ProjectMetadata { + name: "foo", + version: Version { + norm: Some( + "0.1.0", + ), + version: "[[0], [0], [1], [0]]", + local: "[]", + }, + description: None, + authors: [], + channels: [], + platforms: Spanned { + span: 121..123, + value: [], + }, + }, + commands: {}, + system_requirements: SystemRequirements { + windows: None, + unix: None, + macos: None, + linux: None, + cuda: None, + libc: None, + archspec: None, + }, + dependencies: { + "my-game": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0.0", + ), + version: "[[0], [1], [0], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + }, + host_dependencies: Some( + { + "sdl2": NamelessMatchSpec { + version: Some( + Any, + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + }, + ), + build_dependencies: Some( + { + "cmake": NamelessMatchSpec { + version: Some( + Any, + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + }, + ), + target: {}, +} diff --git a/src/project/snapshots/pixi__project__manifest__test__target_specific.snap b/src/project/snapshots/pixi__project__manifest__test__target_specific.snap index 3aee4616a..f74163217 100644 --- a/src/project/snapshots/pixi__project__manifest__test__target_specific.snap +++ b/src/project/snapshots/pixi__project__manifest__test__target_specific.snap @@ -31,6 +31,8 @@ ProjectManifest { archspec: None, }, dependencies: {}, + host_dependencies: None, + build_dependencies: None, target: { Spanned { span: 146..152, @@ -60,6 +62,8 @@ ProjectManifest { namespace: None, }, }, + host_dependencies: None, + build_dependencies: None, }, Spanned { span: 206..212, @@ -89,6 +93,8 @@ ProjectManifest { namespace: None, }, }, + host_dependencies: None, + build_dependencies: None, }, }, } diff --git a/src/project/snapshots/pixi__project__tests__dependency_sets.snap b/src/project/snapshots/pixi__project__tests__dependency_sets.snap new file mode 100644 index 000000000..ffe4bad44 --- /dev/null +++ b/src/project/snapshots/pixi__project__tests__dependency_sets.snap @@ -0,0 +1,66 @@ +--- +source: src/project/mod.rs +expression: "project.all_dependencies(Platform::Linux64).unwrap()" +--- +{ + "foo": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0", + ), + version: "[[0], [1], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + "libc": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "2.12", + ), + version: "[[0], [2], [12]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + "bar": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0", + ), + version: "[[0], [1], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, +} diff --git a/src/project/snapshots/pixi__project__tests__dependency_target_sets.snap b/src/project/snapshots/pixi__project__tests__dependency_target_sets.snap new file mode 100644 index 000000000..c00ad99d3 --- /dev/null +++ b/src/project/snapshots/pixi__project__tests__dependency_target_sets.snap @@ -0,0 +1,126 @@ +--- +source: src/project/mod.rs +expression: "project.all_dependencies(Platform::Linux64).unwrap()" +--- +{ + "foo": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0", + ), + version: "[[0], [1], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + "wolflib": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0", + ), + version: "[[0], [1], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + "libc": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "2.12", + ), + version: "[[0], [2], [12]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + "banksy": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0", + ), + version: "[[0], [1], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + "bar": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0", + ), + version: "[[0], [1], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, + "baz": NamelessMatchSpec { + version: Some( + Operator( + Equals, + Version { + norm: Some( + "1.0", + ), + version: "[[0], [1], [0]]", + local: "[]", + }, + ), + ), + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + }, +} diff --git a/tests/add_tests.rs b/tests/add_tests.rs new file mode 100644 index 000000000..c83708149 --- /dev/null +++ b/tests/add_tests.rs @@ -0,0 +1,87 @@ +mod common; +use crate::common::package_database::{Package, PackageDatabase}; +use crate::common::LockFileExt; +use crate::common::PixiControl; +use pixi::cli::add::SpecType; +use tempfile::TempDir; + +/// Test add functionality for different types of packages. +/// Run, dev, build +#[tokio::test] +async fn add_functionality() { + let mut package_database = PackageDatabase::default(); + + // Add a package `foo` that depends on `bar` both set to version 1. + package_database.add_package(Package::build("rattler", "1").finish()); + package_database.add_package(Package::build("rattler", "2").finish()); + package_database.add_package(Package::build("rattler", "3").finish()); + + // Write the repodata to disk + let channel_dir = TempDir::new().unwrap(); + package_database + .write_repodata(channel_dir.path()) + .await + .unwrap(); + + let pixi = PixiControl::new().unwrap(); + + pixi.init() + .with_local_channel(channel_dir.path()) + .await + .unwrap(); + + // Add a package + pixi.add("rattler==1").await.unwrap(); + pixi.add("rattler==2") + .set_type(SpecType::Host) + .await + .unwrap(); + pixi.add("rattler==3") + .set_type(SpecType::Build) + .await + .unwrap(); + + let lock = pixi.lock_file().await.unwrap(); + assert!(lock.contains_matchspec("rattler==3")); + assert!(!lock.contains_matchspec("rattler==2")); + assert!(!lock.contains_matchspec("rattler==1")); +} + +/// Test that we get the union of all packages in the lockfile for the run, build and host +#[tokio::test] +async fn add_functionality_union() { + let mut package_database = PackageDatabase::default(); + + // Add a package `foo` that depends on `bar` both set to version 1. + package_database.add_package(Package::build("rattler", "1").finish()); + package_database.add_package(Package::build("libcomputer", "1.2").finish()); + package_database.add_package(Package::build("libidk", "3.1").finish()); + + // Write the repodata to disk + let channel_dir = TempDir::new().unwrap(); + package_database + .write_repodata(channel_dir.path()) + .await + .unwrap(); + + let pixi = PixiControl::new().unwrap(); + + pixi.init() + .with_local_channel(channel_dir.path()) + .await + .unwrap(); + + // Add a package + pixi.add("rattler").await.unwrap(); + pixi.add("libcomputer") + .set_type(SpecType::Host) + .await + .unwrap(); + pixi.add("libidk").set_type(SpecType::Build).await.unwrap(); + + // Lock file should contain all packages + let lock = pixi.lock_file().await.unwrap(); + assert!(lock.contains_matchspec("rattler==1")); + assert!(lock.contains_matchspec("libcomputer==1.2")); + assert!(lock.contains_matchspec("libidk==3.1")); +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index bf4d74f21..f532244c2 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -2,6 +2,8 @@ pub mod package_database; +use pixi::cli::add::SpecType; +use pixi::cli::install::Args; use pixi::cli::run::create_command; use pixi::cli::{add, init, run}; use pixi::{consts, Project}; @@ -111,7 +113,9 @@ impl PixiControl { AddBuilder { args: add::Args { manifest_path: Some(self.manifest_path()), + host: false, specs: vec![spec.into()], + build: false, }, } } @@ -128,6 +132,14 @@ impl PixiControl { Ok(RunResult { output }) } + /// Create an installed environment. I.e a resolved and installed prefix + pub async fn install(&self) -> anyhow::Result<()> { + pixi::cli::install::execute(Args { + manifest_path: Some(self.manifest_path()), + }) + .await + } + /// Get the associated lock file pub async fn lock_file(&self) -> anyhow::Result { pixi::environment::load_lock_for_manifest_path(&self.manifest_path()).await @@ -176,6 +188,25 @@ impl AddBuilder { self.args.specs.push(spec.into()); self } + + /// Set as a host + pub fn set_type(mut self, t: SpecType) -> Self { + match t { + SpecType::Host => { + self.args.host = true; + self.args.build = false; + } + SpecType::Build => { + self.args.host = false; + self.args.build = true; + } + SpecType::Run => { + self.args.host = false; + self.args.build = false; + } + } + self + } } // When `.await` is called on an object that is not a `Future` the compiler will first check if the diff --git a/tests/init_tests.rs b/tests/init_tests.rs new file mode 100644 index 000000000..2d8603870 --- /dev/null +++ b/tests/init_tests.rs @@ -0,0 +1,73 @@ +mod common; + +use crate::common::PixiControl; +use rattler_conda_types::{Channel, ChannelConfig, Version}; +use std::str::FromStr; + +#[tokio::test] +async fn init_creates_project_manifest() { + // Run the init command + let pixi = PixiControl::new().unwrap(); + pixi.init().await.unwrap(); + + // There should be a loadable project manifest in the directory + let project = pixi.project().unwrap(); + + // Default configuration should be present in the file + assert!(!project.name().is_empty()); + assert_eq!( + project.name(), + pixi.project_path() + .file_stem() + .unwrap() + .to_string_lossy() + .as_ref(), + "project name should match the directory name" + ); + assert_eq!(project.version(), &Version::from_str("0.1.0").unwrap()); +} + +/// Tests that when initializing an empty project with a custom channel it is actually used. +#[tokio::test] +async fn specific_channel() { + let pixi = PixiControl::new().unwrap(); + + // Init with a custom channel + pixi.init() + .with_channel("random") + .with_channel("foobar") + .await + .unwrap(); + + // Load the project + let project = pixi.project().unwrap(); + + // The only channel should be the "random" channel + let channels = project.channels(); + assert_eq!( + channels, + &[ + Channel::from_str("random", &ChannelConfig::default()).unwrap(), + Channel::from_str("foobar", &ChannelConfig::default()).unwrap() + ] + ) +} + +/// Tests that when initializing an empty project the default channel `conda-forge` is used. +#[tokio::test] +async fn default_channel() { + let pixi = PixiControl::new().unwrap(); + + // Init a new project + pixi.init().await.unwrap(); + + // Load the project + let project = pixi.project().unwrap(); + + // The only channel should be the "conda-forge" channel + let channels = project.channels(); + assert_eq!( + channels, + &[Channel::from_str("conda-forge", &ChannelConfig::default()).unwrap()] + ) +} diff --git a/tests/install_tests.rs b/tests/install_tests.rs index 15741b40d..c0977b958 100644 --- a/tests/install_tests.rs +++ b/tests/install_tests.rs @@ -4,8 +4,6 @@ use crate::common::package_database::{Package, PackageDatabase}; use crate::common::string_from_iter; use common::{LockFileExt, PixiControl}; use pixi::cli::run; -use rattler_conda_types::{Channel, ChannelConfig, Version}; -use std::str::FromStr; use tempfile::TempDir; /// Should add a python version to the environment and lock file that matches the specified version @@ -33,74 +31,6 @@ async fn install_run_python() { assert_eq!(result.stdout().trim(), "Python 3.11.0"); } -#[tokio::test] -async fn init_creates_project_manifest() { - // Run the init command - let pixi = PixiControl::new().unwrap(); - pixi.init().await.unwrap(); - - // There should be a loadable project manifest in the directory - let project = pixi.project().unwrap(); - - // Default configuration should be present in the file - assert!(!project.name().is_empty()); - assert_eq!( - project.name(), - pixi.project_path() - .file_stem() - .unwrap() - .to_string_lossy() - .as_ref(), - "project name should match the directory name" - ); - assert_eq!(project.version(), &Version::from_str("0.1.0").unwrap()); -} - -/// Tests that when initializing an empty project with a custom channel it is actually used. -#[tokio::test] -async fn specific_channel() { - let pixi = PixiControl::new().unwrap(); - - // Init with a custom channel - pixi.init() - .with_channel("random") - .with_channel("foobar") - .await - .unwrap(); - - // Load the project - let project = pixi.project().unwrap(); - - // The only channel should be the "random" channel - let channels = project.channels(); - assert_eq!( - channels, - &[ - Channel::from_str("random", &ChannelConfig::default()).unwrap(), - Channel::from_str("foobar", &ChannelConfig::default()).unwrap() - ] - ) -} - -/// Tests that when initializing an empty project the default channel `conda-forge` is used. -#[tokio::test] -async fn default_channel() { - let pixi = PixiControl::new().unwrap(); - - // Init a new project - pixi.init().await.unwrap(); - - // Load the project - let project = pixi.project().unwrap(); - - // The only channel should be the "conda-forge" channel - let channels = project.channels(); - assert_eq!( - channels, - &[Channel::from_str("conda-forge", &ChannelConfig::default()).unwrap()] - ) -} - /// This is a test to check that creating incremental lock files works. /// /// It works by using a fake channel that contains two packages: `foo` and `bar`. `foo` depends on