Skip to content

Commit

Permalink
Fix flaky CI test (use-ink#263)
Browse files Browse the repository at this point in the history
* Add fix

* Revert "Add fix"

This reverts commit 5c81fee.

* Generate unique contract names for tests

* Fix test

* Implement comments

* Implement comments
  • Loading branch information
Michael Müller authored Apr 20, 2021
1 parent f839c1d commit 307de23
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 43 deletions.
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)
})
}
}

0 comments on commit 307de23

Please sign in to comment.