Newer
Older
{
let mut file = std::fs::File::create(&path).unwrap();
file.write_all(content.as_bytes())
.expect("writing of executable failed");
}
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o777))
.expect("setting permissions failed");
}
/// Creates an executable `wasm-opt-mocked` file which outputs
/// "wasm-opt version `version`".
///
/// Returns the path to this file.
///
/// Currently works only on `unix`.
#[cfg(unix)]
fn mock_wasm_opt_version(tmp_dir: &Path, version: &str) -> PathBuf {
let path = tmp_dir.join("wasm-opt-mocked");
let content = format!("#!/bin/sh\necho \"wasm-opt version {}\"", version);
create_executable(&path, &content);
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
build_mode: BuildMode::Release,
build_artifact: BuildArtifacts::CodeOnly,
..Default::default()
};
let res = super::execute(args).expect("build failed");
// our ci has set `CARGO_TARGET_DIR` to cache artifacts.
// this dir does not include `/target/` as a path, hence
// we can't match for e.g. `foo_project/target/ink`.
//
// we also can't match for `/ink` here, since this would match
// for `/ink` being the root path.
assert!(res.target_directory.ends_with("ink"));
assert!(
res.metadata_result.is_none(),
"CodeOnly should not generate the metadata"
);
let optimized_size = res.optimization_result.unwrap().optimized_size;
assert!(optimized_size > 0.0);
// our optimized contract template should always be below 3k.
// we specified that debug symbols should be removed
// original code should have some but the optimized version should have them removed
assert!(!has_debug_symbols(&res.dest_wasm.unwrap()));
#[test]
fn check_must_not_output_contract_artifacts_in_project_dir() {
// given
let project_dir = manifest_path.directory().expect("directory must exist");
let args = crate::cmd::build::ExecuteArgs {
manifest_path: manifest_path.clone(),
build_artifact: BuildArtifacts::CheckOnly,
..Default::default()
};
// when
super::execute(args).expect("build failed");
// then
assert!(
!project_dir.join("target/ink/new_project.contract").exists(),
"found contract artifact in project directory!"
);
assert!(
!project_dir.join("target/ink/new_project.wasm").exists(),
"found wasm artifact in project directory!"
);
Ok(())
})
}
#[test]
fn optimization_passes_from_cli_must_take_precedence_over_profile() {
let cmd = BuildCommand {
build_artifact: BuildArtifacts::All,
verbosity: VerbosityFlags::default(),
unstable_options: UnstableOptions::default(),
// we choose zero optimization passes as the "cli" parameter
optimization_passes: Some(OptimizationPasses::Zero),
};
// when
let res = cmd.exec().expect("build failed");
let optimization = res
.optimization_result
.expect("no optimization result available");
// then
// The size does not exactly match the original size even without optimization
// passed because there is still some post processing happening.
let size_diff = optimization.original_size - optimization.optimized_size;
0.0 < size_diff && size_diff < 10.0,
"The optimized size savings are larger than allowed or negative: {}",
size_diff,
);
Ok(())
})
}
#[test]
fn optimization_passes_from_profile_must_be_used() {
verbosity: VerbosityFlags::default(),
unstable_options: UnstableOptions::default(),
// we choose no optimization passes as the "cli" parameter
optimization_passes: None,
};
// when
let res = cmd.exec().expect("build failed");
let optimization = res
.optimization_result
.expect("no optimization result available");
// then
// The size does not exactly match the original size even without optimization
// passed because there is still some post processing happening.
let size_diff = optimization.original_size - optimization.optimized_size;
size_diff > (optimization.original_size / 2.0),
"The optimized size savings are too small: {}",
size_diff,
#[test]
fn project_template_dependencies_must_be_ink_compatible() {
let res =
assert_compatible_ink_dependencies(&manifest_path, Verbosity::Default);
// then
assert!(res.is_ok());
Ok(())
})
}
#[test]
fn detect_mismatching_parity_scale_codec_dependencies() {
// at the time of writing this test ink! already uses `parity-scale-codec`
// in a version > 2, hence 1 is an incompatible version.
let mut manifest = Manifest::new(manifest_path.clone())?;
manifest
.set_dependency_version("scale", "1.0.0")
.expect("setting `scale` version failed");
manifest
.write(&manifest_path)
.expect("writing manifest failed");
// when
let res =
assert_compatible_ink_dependencies(&manifest_path, Verbosity::Default);
// then
assert!(res.is_err());
Ok(())
})
}
#[test]
fn incompatible_wasm_opt_version_must_be_detected_if_built_from_repo() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "98 (version_13-79-gc12cc3f50)");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_err());
assert!(
format!("{:?}", res).starts_with(
"Err(Your wasm-opt version is 98, but we require a version >= 99."
),
"Expected a different output, found {:?}",
res
);
Ok(())
})
}
#[test]
fn compatible_wasm_opt_version_must_be_detected_if_built_from_repo() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "99 (version_99-79-gc12cc3f50");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_ok());
Ok(())
})
}
#[test]
fn incompatible_wasm_opt_version_must_be_detected_if_installed_as_package() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "98");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_err());
// this println is here to debug a spuriously failing CI at the following assert.
eprintln!("error: {:?}", res);
assert!(format!("{:?}", res).starts_with(
"Err(Your wasm-opt version is 98, but we require a version >= 99."
));
Ok(())
})
}
#[test]
fn compatible_wasm_opt_version_must_be_detected_if_installed_as_package() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "99");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_ok());
Ok(())
})
}
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
#[test]
fn contract_lib_name_different_from_package_name_must_build() {
with_new_contract_project(|manifest_path| {
// given
let mut manifest =
Manifest::new(manifest_path.clone()).expect("manifest creation failed");
let _ = manifest
.set_lib_name("some_lib_name")
.expect("setting lib name failed");
let _ = manifest
.set_package_name("some_package_name")
.expect("setting pacakge name failed");
manifest
.write(&manifest_path)
.expect("writing manifest failed");
// when
let cmd = BuildCommand {
manifest_path: Some(manifest_path.into()),
build_artifact: BuildArtifacts::All,
verbosity: VerbosityFlags::default(),
unstable_options: UnstableOptions::default(),
optimization_passes: None,
};
let res = cmd.exec().expect("build failed");
// then
assert_eq!(
res.dest_wasm
.expect("`dest_wasm` does not exist")
.file_name(),
Some(OsStr::new("some_lib_name.wasm"))
);
Ok(())
})
}
let _ = assert_debug_mode_supported(
&Version::parse("3.0.0-rc4").expect("parsing must work"),
)
.expect("debug mode must be compatible");
let _ = assert_debug_mode_supported(
&Version::parse("4.0.0-rc1").expect("parsing must work"),
)
.expect("debug mode must be compatible");
let _ = assert_debug_mode_supported(
&Version::parse("5.0.0").expect("parsing must work"),
)
.expect("debug mode must be compatible");
}
#[test]
pub fn debug_mode_must_be_incompatible() {
let res = assert_debug_mode_supported(
&Version::parse("3.0.0-rc3").expect("parsing must work"),
)
.expect_err("assertion must fail");
assert_eq!(
res.to_string(),
"Building the contract in debug mode requires an ink! version newer than `3.0.0-rc3`!"
);
}
#[test]
fn building_template_in_debug_mode_must_work() {
with_new_contract_project(|manifest_path| {
// given
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
build_mode: BuildMode::Debug,
..Default::default()
};
let res = super::execute(args);
// then
assert!(res.is_ok(), "building template in debug mode failed!");
Ok(())
})
}
#[test]
fn building_template_in_release_mode_must_work() {
with_new_contract_project(|manifest_path| {
// given
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
build_mode: BuildMode::Release,
..Default::default()
};
let res = super::execute(args);
// then
assert!(res.is_ok(), "building template in release mode failed!");
Ok(())
})
}
#[test]
fn building_contract_with_source_file_in_subfolder_must_work() {
with_new_contract_project(|manifest_path| {
// given
let path = manifest_path.directory().expect("dir must exist");
let old_lib_path = path.join(Path::new("lib.rs"));
let new_lib_path = path.join(Path::new("srcfoo")).join(Path::new("lib.rs"));
let new_dir_path = path.join(Path::new("srcfoo"));
std::fs::create_dir_all(new_dir_path).expect("creating dir must work");
std::fs::rename(old_lib_path, new_lib_path).expect("moving file must work");
let mut manifest = Manifest::new(manifest_path.clone())
.expect("creating manifest must work");
manifest
.set_lib_path("srcfoo/lib.rs")
.expect("setting lib path must work");
manifest.write(&manifest_path).expect("writing must work");
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
build_artifact: BuildArtifacts::CheckOnly,
..Default::default()
};
let res = super::execute(args);
// then
assert!(res.is_ok(), "building contract failed!");
Ok(())
})
}
#[test]
fn keep_debug_symbols_in_debug_mode() {
with_new_contract_project(|manifest_path| {
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
build_mode: BuildMode::Debug,
build_artifact: BuildArtifacts::CodeOnly,
keep_debug_symbols: true,
..Default::default()
};
let res = super::execute(args).expect("build failed");
// we specified that debug symbols should be kept
assert!(has_debug_symbols(&res.dest_wasm.unwrap()));
Ok(())
})
}
#[test]
fn keep_debug_symbols_in_release_mode() {
with_new_contract_project(|manifest_path| {
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
build_mode: BuildMode::Release,
build_artifact: BuildArtifacts::CodeOnly,
keep_debug_symbols: true,
..Default::default()
};
let res = super::execute(args).expect("build failed");
// we specified that debug symbols should be kept
assert!(has_debug_symbols(&res.dest_wasm.unwrap()));
Ok(())
})
}
#[test]
fn build_with_json_output_works() {
with_new_contract_project(|manifest_path| {
// given
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
output_type: OutputType::Json,
..Default::default()
};
// when
let res = super::execute(args).expect("build failed");
// then
assert!(res.serialize_json().is_ok());
Ok(())
})
}
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
// This test has to be ignored until the next ink! rc.
// Before that we don't have the `__ink_dylint_…` markers available
// to actually run `dylint`.
#[test]
#[ignore]
fn dylint_must_find_issue() {
with_new_contract_project(|manifest_path| {
// given
let contract = r#"
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod fail_mapping_01 {
use ink_storage::{traits::SpreadAllocate, Mapping};
#[ink(storage)]
#[derive(SpreadAllocate)]
pub struct MyContract {
balances: Mapping<AccountId, Balance>,
}
impl MyContract {
#[ink(constructor)]
pub fn new() -> Self {
Self {
balances: Default::default(),
}
}
/// Returns the total token supply.
#[ink(message)]
pub fn get(&self) {
// ...
}
}
}"#;
let project_dir = manifest_path.directory().expect("directory must exist");
let lib = project_dir.join("lib.rs");
std::fs::write(&lib, contract)?;
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
build_artifact: BuildArtifacts::CheckOnly,
..Default::default()
};
// when
let res = super::execute(args);
// then
match res {
Err(err) => {
eprintln!("err: {:?}", err);
assert!(err.to_string().contains(
"help: add an `initialize_contract` function in this constructor"
));
}
_ => panic!("build succeeded, but must fail!"),
};
Ok(())
})
}
#[cfg(unix)]
#[test]
fn missing_cargo_dylint_installation_must_be_detected() {
with_new_contract_project(|manifest_path| {
// given
let manifest_dir = manifest_path.directory().unwrap();
// mock existing `dylint-link` binary
create_executable(&manifest_dir.join("dylint-link"), "#!/bin/sh\nexit 0");
// mock a non-existing `cargo dylint` installation.
create_executable(&manifest_dir.join("cargo"), "#!/bin/sh\nexit 1");
// when
let args = crate::cmd::build::ExecuteArgs {
manifest_path,
..Default::default()
};
let res = super::execute(args).map(|_| ()).unwrap_err();
// then
assert!(format!("{:?}", res).contains("cargo-dylint was not found!"));
Ok(())
})
}