diff --git a/tests/common/develop.rs b/tests/common/develop.rs index dee0c46e1..4933f3165 100644 --- a/tests/common/develop.rs +++ b/tests/common/develop.rs @@ -1,10 +1,8 @@ -use crate::common::{adjust_canonicalization, check_installed, maybe_mock_cargo}; +use crate::common::{check_installed, create_virtualenv, maybe_mock_cargo}; use anyhow::Result; -use fs_err as fs; -use maturin::{develop, Target}; -use std::path::{Path, PathBuf}; +use maturin::develop; +use std::path::Path; use std::process::Command; -use std::process::Stdio; use std::str; /// Creates a virtualenv and activates it, checks that the package isn't installed, uses @@ -12,39 +10,7 @@ use std::str; pub fn test_develop(package: impl AsRef, bindings: Option) -> Result<()> { maybe_mock_cargo(); - let test_name = package - .as_ref() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(); - - let venv_dir = PathBuf::from("test-crates") - .canonicalize()? - .join("venvs") - .join(format!("{}-develop", test_name)); - let target = Target::from_target_triple(None)?; - - if venv_dir.is_dir() { - fs::remove_dir_all(&venv_dir)?; - } - - let output = Command::new("virtualenv") - .arg(adjust_canonicalization(&venv_dir)) - .stderr(Stdio::inherit()) - .output() - .expect("Failed to create a virtualenv"); - if !output.status.success() { - panic!( - "Failed to run virtualenv: {}\n---stdout:\n{}---stderr:\n{}", - output.status, - str::from_utf8(&output.stdout)?, - str::from_utf8(&output.stderr)? - ); - } - - let python = target.get_venv_python(&venv_dir); + let (venv_dir, python) = create_virtualenv(&package, "develop")?; // Ensure the test doesn't wrongly pass check_installed(&package.as_ref(), &python).unwrap_err(); diff --git a/tests/common/editable.rs b/tests/common/editable.rs new file mode 100644 index 000000000..23bf5934a --- /dev/null +++ b/tests/common/editable.rs @@ -0,0 +1,77 @@ +use crate::common::{ + adjust_canonicalization, check_installed, create_virtualenv, maybe_mock_cargo, +}; +use anyhow::{bail, Context, Result}; +use maturin::BuildOptions; +use std::path::Path; +use std::process::Command; +use std::str; +use structopt::StructOpt; + +/// test PEP 660 editable installs +pub fn test_editable(package: impl AsRef, bindings: Option) -> Result<()> { + maybe_mock_cargo(); + + let package_string = package.as_ref().join("Cargo.toml").display().to_string(); + + let (venv_dir, python) = create_virtualenv(&package, "editable")?; + let interpreter = python.to_str().expect("invalid interpreter path"); + // The first argument is ignored by clap + let mut cli = vec![ + "build", + "--interpreter", + &interpreter, + "--manifest-path", + &package_string, + "--cargo-extra-args='--quiet'", + ]; + + if let Some(ref bindings) = bindings { + cli.push("--bindings"); + cli.push(bindings); + } + + let options: BuildOptions = BuildOptions::from_iter_safe(cli)?; + + let build_context = options.into_build_context(false, cfg!(feature = "faster-tests"), true)?; + let wheels = build_context.build_wheels()?; + + for (filename, _supported_version) in wheels.iter() { + // TODO: should add an assertion for .pth file in wheel root for mixed project layout + let command = [ + "-m", + "pip", + "--disable-pip-version-check", + "install", + "--force-reinstall", + &adjust_canonicalization(filename), + ]; + let output = Command::new(&python) + .args(&command) + .output() + .context(format!("pip install failed with {:?}", python))?; + if !output.status.success() { + bail!( + "pip install in {} failed running {:?}: {}\n--- Stdout:\n{}\n--- Stderr:\n{}\n---\n", + venv_dir.display(), + &command, + output.status, + str::from_utf8(&output.stdout)?.trim(), + str::from_utf8(&output.stderr)?.trim(), + ); + } + if !output.stderr.is_empty() { + bail!( + "pip raised a warning running {:?}: {}\n--- Stdout:\n{}\n--- Stderr:\n{}\n---\n", + &command, + output.status, + str::from_utf8(&output.stdout)?.trim(), + str::from_utf8(&output.stderr)?.trim(), + ); + } + + check_installed(&package.as_ref(), &python)?; + } + + Ok(()) +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 8b7441bb0..6a7967cdf 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,10 +1,13 @@ use anyhow::{bail, Result}; +use fs_err as fs; +use maturin::Target; use std::path::Path; use std::path::PathBuf; -use std::process::Command; +use std::process::{Command, Stdio}; use std::{env, io, str}; pub mod develop; +pub mod editable; pub mod errors; pub mod integration; pub mod other; @@ -92,3 +95,44 @@ pub fn handle_result(result: Result) -> T { Ok(result) => result, } } + +/// Create virtualenv +pub fn create_virtualenv( + package: impl AsRef, + venv_suffix: &str, +) -> Result<(PathBuf, PathBuf)> { + let test_name = package + .as_ref() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(); + + let venv_dir = PathBuf::from("test-crates") + .canonicalize()? + .join("venvs") + .join(format!("{}-{}", test_name, venv_suffix)); + let target = Target::from_target_triple(None)?; + + if venv_dir.is_dir() { + fs::remove_dir_all(&venv_dir)?; + } + + let output = Command::new("virtualenv") + .arg(adjust_canonicalization(&venv_dir)) + .stderr(Stdio::inherit()) + .output() + .expect("Failed to create a virtualenv"); + if !output.status.success() { + panic!( + "Failed to run virtualenv: {}\n---stdout:\n{}---stderr:\n{}", + output.status, + str::from_utf8(&output.stdout)?, + str::from_utf8(&output.stderr)? + ); + } + + let python = target.get_venv_python(&venv_dir); + Ok((venv_dir, python)) +} diff --git a/tests/run.rs b/tests/run.rs index 66ffb38ee..1c41f71a9 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -1,6 +1,6 @@ //! To speed up the tests, they are tests all collected in a single module -use common::{develop, errors, handle_result, integration, other}; +use common::{develop, editable, errors, handle_result, integration, other}; mod common; @@ -45,6 +45,24 @@ fn develop_hello_world() { handle_result(develop::test_develop("test-crates/hello-world", None)); } +#[test] +fn editable_pyo3_pure() { + handle_result(editable::test_editable("test-crates/pyo3-pure", None)); +} + +#[test] +fn editable_pyo3_mixed() { + handle_result(editable::test_editable("test-crates/pyo3-mixed", None)); +} + +#[test] +fn editable_pyo3_mixed_py_subdir() { + handle_result(editable::test_editable( + "test-crates/pyo3-mixed-py-subdir", + None, + )); +} + #[test] fn integration_pyo3_pure() { handle_result(integration::test_integration("test-crates/pyo3-pure", None));