Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix flaky CI test #263

Merged
merged 6 commits into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 21 additions & 30 deletions src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,8 @@ pub(crate) fn execute(
mod tests_ci_only {
use super::{assert_compatible_ink_dependencies, check_wasm_opt_version_compatibility};
use crate::{
cmd::{self, BuildCommand},
util::tests::with_tmp_dir,
cmd::BuildCommand,
util::tests::{with_new_contract_project, with_tmp_dir},
workspace::Manifest,
BuildArtifacts, ManifestPath, OptimizationPasses, UnstableFlags, UnstableOptions,
Verbosity, VerbosityFlags,
Expand Down Expand Up @@ -638,10 +638,7 @@ mod tests_ci_only {

#[test]
fn build_code_only() {
with_tmp_dir(|path| {
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let manifest_path =
ManifestPath::new(&path.join("new_project").join("Cargo.toml")).unwrap();
with_new_contract_project(|manifest_path| {
let res = super::execute(
&manifest_path,
Verbosity::Default,
Expand Down Expand Up @@ -676,11 +673,9 @@ mod tests_ci_only {

#[test]
fn check_must_not_output_contract_artifacts_in_project_dir() {
with_tmp_dir(|path| {
with_new_contract_project(|manifest_path| {
// given
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let project_dir = path.join("new_project");
let manifest_path = ManifestPath::new(&project_dir.join("Cargo.toml")).unwrap();
let project_dir = manifest_path.directory().expect("directory must exist");

// when
super::execute(
Expand All @@ -707,13 +702,14 @@ mod tests_ci_only {

#[test]
fn optimization_passes_from_cli_must_take_precedence_over_profile() {
with_tmp_dir(|path| {
with_new_contract_project(|manifest_path| {
// given
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let cargo_toml_path = path.join("new_project").join("Cargo.toml");
write_optimization_passes_into_manifest(&cargo_toml_path, OptimizationPasses::Three);
write_optimization_passes_into_manifest(
&manifest_path.clone().into(),
OptimizationPasses::Three,
);
let cmd = BuildCommand {
manifest_path: Some(cargo_toml_path),
manifest_path: Some(manifest_path.into()),
build_artifact: BuildArtifacts::All,
verbosity: VerbosityFlags::default(),
unstable_options: UnstableOptions::default(),
Expand Down Expand Up @@ -746,13 +742,14 @@ mod tests_ci_only {

#[test]
fn optimization_passes_from_profile_must_be_used() {
with_tmp_dir(|path| {
with_new_contract_project(|manifest_path| {
// given
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let cargo_toml_path = path.join("new_project").join("Cargo.toml");
write_optimization_passes_into_manifest(&cargo_toml_path, OptimizationPasses::Three);
write_optimization_passes_into_manifest(
&manifest_path.clone().into(),
OptimizationPasses::Three,
);
let cmd = BuildCommand {
manifest_path: Some(cargo_toml_path),
manifest_path: Some(manifest_path.into()),
build_artifact: BuildArtifacts::All,
verbosity: VerbosityFlags::default(),
unstable_options: UnstableOptions::default(),
Expand Down Expand Up @@ -785,12 +782,9 @@ mod tests_ci_only {

#[test]
fn project_template_dependencies_must_be_ink_compatible() {
with_tmp_dir(|path| {
with_new_contract_project(|manifest_path| {
// given
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let cargo_toml_path = path.join("new_project").join("Cargo.toml");
let manifest_path =
ManifestPath::new(&cargo_toml_path).expect("manifest path creation failed");
// the manifest path

// when
let res = assert_compatible_ink_dependencies(&manifest_path, Verbosity::Default);
Expand All @@ -803,12 +797,9 @@ mod tests_ci_only {

#[test]
fn detect_mismatching_parity_scale_codec_dependencies() {
with_tmp_dir(|path| {
with_new_contract_project(|manifest_path| {
// given
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let cargo_toml_path = path.join("new_project").join("Cargo.toml");
let manifest_path =
ManifestPath::new(&cargo_toml_path).expect("manifest path creation failed");
// the manifest path

// at the time of writing this test ink! already uses `parity-scale-codec`
// in a version > 2, hence 1 is an incompatible version.
Expand Down
8 changes: 2 additions & 6 deletions src/cmd/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ fn blake2_hash(code: &[u8]) -> CodeHash {
mod tests {
use crate::cmd::metadata::blake2_hash;
use crate::{
cmd, crate_metadata::CrateMetadata, util::tests::with_tmp_dir, BuildArtifacts,
cmd, crate_metadata::CrateMetadata, util::tests::with_new_contract_project, BuildArtifacts,
ManifestPath, OptimizationPasses, UnstableFlags, Verbosity,
};
use contract_metadata::*;
Expand Down Expand Up @@ -299,11 +299,7 @@ mod tests {
#[test]
fn generate_metadata() {
env_logger::try_init().ok();
with_tmp_dir(|path| {
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let working_dir = path.join("new_project");
let manifest_path = ManifestPath::new(working_dir.join("Cargo.toml"))?;

with_new_contract_project(|manifest_path| {
// add optional metadata fields
let mut test_manifest = TestContractManifest::new(manifest_path)?;
test_manifest.add_package_value("description", "contract description".into())?;
Expand Down
14 changes: 7 additions & 7 deletions src/cmd/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::util::tests::with_tmp_dir;
use crate::util::tests::{with_new_contract_project, with_tmp_dir};

#[test]
fn rejects_hyphenated_name() {
with_tmp_dir(|path| {
let result = execute("rejects-hyphenated-name", Some(path));
with_new_contract_project(|manifest_path| {
let result = execute("rejects-hyphenated-name", Some(manifest_path));
assert!(result.is_err(), "Should fail");
assert_eq!(
result.err().unwrap().to_string(),
Expand All @@ -125,8 +125,8 @@ mod tests {

#[test]
fn rejects_name_with_period() {
with_tmp_dir(|path| {
let result = execute("../xxx", Some(path));
with_new_contract_project(|manifest_path| {
let result = execute("../xxx", Some(manifest_path));
assert!(result.is_err(), "Should fail");
assert_eq!(
result.err().unwrap().to_string(),
Expand All @@ -138,8 +138,8 @@ mod tests {

#[test]
fn rejects_name_beginning_with_number() {
with_tmp_dir(|path| {
let result = execute("1xxx", Some(path));
with_new_contract_project(|manifest_path| {
let result = execute("1xxx", Some(manifest_path));
assert!(result.is_err(), "Should fail");
assert_eq!(
result.err().unwrap().to_string(),
Expand Down
42 changes: 42 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,12 @@ macro_rules! maybe_println {

#[cfg(test)]
pub mod tests {
use crate::ManifestPath;
use std::path::Path;
use std::sync::atomic::{AtomicU32, Ordering};

/// Creates a temporary directory and passes the `tmp_dir` path to `f`.
/// Panics if `f` returns an `Err`.
pub fn with_tmp_dir<F>(f: F)
where
F: FnOnce(&Path) -> anyhow::Result<()>,
Expand All @@ -118,4 +122,42 @@ pub mod tests {
// catch test panics in order to clean up temp dir which will be very large
f(tmp_dir.path()).expect("Error executing test with tmp dir")
}

/// Global counter to generate unique contract names in `with_new_contract_project`.
///
/// We typically use `with_tmp_dir` to generate temporary folders to build contracts
/// in. But for caching purposes our CI uses `CARGO_TARGET_DIR` to overwrite the
/// target directory of any contract build -- it is set to a fixed cache directory
/// instead.
/// This poses a problem since we still want to ensure that each test builds to its
/// own, unique target directory -- without interfering with the target directory of
/// other tests. In the past this has been a problem when a test tried to create a
/// contract with the same contract name as another test -- both were then build
/// into the same target directory, sometimes causing test failures for strange reasons.
///
/// The fix we decided on is to append a unique number to each contract name which
/// is created. This `COUNTER` provides a global counter which is accessed by each test
/// (in each thread) to get the current `COUNTER` number and increase it afterwards.
///
/// We decided to go for this counter instead of hashing (with e.g. the temp dir) to
/// prevent an infinite number of contract artifacts being created in the cache directory.
static COUNTER: AtomicU32 = AtomicU32::new(0);

/// Creates a new contract into a temporary directory. The contract's
/// `ManifestPath` is passed into `f`.
pub fn with_new_contract_project<F>(f: F)
where
F: FnOnce(ManifestPath) -> anyhow::Result<()>,
{
with_tmp_dir(|tmp_dir| {
let unique_name = format!("new_project_{}", COUNTER.fetch_add(1, Ordering::SeqCst));

crate::cmd::new::execute(&unique_name, Some(tmp_dir))
.expect("new project creation failed");
let working_dir = tmp_dir.join(unique_name);
let manifest_path = ManifestPath::new(working_dir.join("Cargo.toml"))?;

f(manifest_path)
})
}
}