Skip to content
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
923 changes: 482 additions & 441 deletions Cargo.lock

Large diffs are not rendered by default.

41 changes: 21 additions & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ rust-version = "1.86.0"
[workspace.dependencies]
ahash = "0.8"
anyhow = "1.0"
aptos = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
aptos-framework = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
aptos-gas-schedule = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
aptos-types = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
aptos-vm = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
aptos = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
aptos-framework = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
aptos-gas-schedule = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
aptos-types = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
aptos-vm = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
clap = { version = "4.5", features = ["derive"] }
codespan = "0.11"
codespan-reporting = "0.11"
Expand All @@ -36,21 +36,21 @@ either = "1.9"
fs_extra = "1.3"
itertools = "0.13"
log = "0.4"
move-binary-format = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-bytecode-source-map = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-cli = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-command-line-common = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
legacy-move-compiler = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-compiler-v2 = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-coverage = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-ir-types = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-model = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-binary-format = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-bytecode-source-map = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-cli = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-command-line-common = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
legacy-move-compiler = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-compiler-v2 = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-coverage = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-ir-types = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-model = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-mutator = { path = "move-mutator" }
move-package = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-prover = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-symbol-pool = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-unit-test = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-vm-runtime = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.35" }
move-package = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-prover = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-symbol-pool = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-unit-test = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
move-vm-runtime = { git = "https://github.com/aptos-labs/aptos-core.git", branch = "aptos-release-v1.38" }
mutator-common = { path = "mutator-common" }
num = "0.4"
num-traits = "0.2"
Expand All @@ -63,11 +63,12 @@ serde_json = "1"
stacker = "0.1"
tabled = "0.16"
tempfile = "3.12"
termcolor = "1.1" # aptos deps require 1.1 here
termcolor = "1.1" # aptos deps require 1.1 here

# These below are necessary for some aptos deps
[patch.crates-io]
merlin = { git = "https://github.com/aptos-labs/merlin" }
jemalloc-sys = { git = "https://github.com/aptos-labs/jemalloc-sys-shim", rev = "e0920246dd74303fab9a14b990768c6ac990a59b" }

[patch."https://github.com/aptos-labs/aptos-core.git"]
poem = "=3.1.3"
Expand Down
7 changes: 5 additions & 2 deletions move-mutation-test/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ pub fn create_mutator_options(
mutate_modules: options.mutate_modules.clone(),
downsampling_ratio_percentage: options.downsampling_ratio_percentage,
apply_coverage,
// To run tests, compilation must succeed
verify_mutants: true,
mode: options.mode,
operators: options.operators.clone(),
..Default::default()
Expand Down Expand Up @@ -119,6 +117,10 @@ pub struct TestBuildConfig {
/// The default value is large enough for all normal tests in most projects.
#[clap(long, default_value_t = 1_000_000)]
pub gas_limit: u64,

/// Whether to stop running test for the current mutant upon the first test failure for the mutant.
#[clap(long, default_value_t = true, action = clap::ArgAction::Set)]
pub fail_fast: bool,
}

impl TestBuildConfig {
Expand All @@ -141,6 +143,7 @@ impl TestBuildConfig {
.language_version
.or_else(|| Some(LanguageVersion::latest_stable())),
experiments: self.move_options.compute_experiments(),
print_errors: false,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions move-mutation-test/src/mutation_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ fn run_tests<W: WriteColor + Send>(
)
})
.collect(),
fail_fast: cfg.fail_fast,
..UnitTestingConfig::default()
},
natives,
Expand All @@ -170,6 +171,7 @@ fn run_tests<W: WriteColor + Send>(
// once_cell value above and then we can't change it back anymore.
false,
&mut error_writer,
true,
)
.map_err(|err| Error::msg(format!("failed to run unit tests: {err:#}")))?;

Expand Down
1 change: 1 addition & 0 deletions move-mutation-test/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn test_run_mutation_test(path: &Path, expected_report: String) -> datatest_stab
ignore_compile_warnings: false,
compute_coverage: false,
gas_limit: 2000,
fail_fast: true,
};

let report_file = PathBuf::from("report.txt");
Expand Down
5 changes: 0 additions & 5 deletions move-mutator/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ pub struct CLIOptions {
#[clap(long, value_parser)]
pub out_mutant_dir: Option<PathBuf>,

/// Indicates if mutants should be verified and made sure mutants can compile.
#[clap(long, default_value = "false", conflicts_with = "move_sources")]
pub verify_mutants: bool,

/// Indicates if the output files should be overwritten.
#[clap(long, default_value = "false")]
pub no_overwrite: bool,
Expand Down Expand Up @@ -104,7 +100,6 @@ impl Default for CLIOptions {
mutate_modules: ModuleFilter::All,
mutate_functions: FunctionFilter::All,
out_mutant_dir: Some(PathBuf::from(DEFAULT_OUTPUT_DIR)),
verify_mutants: false,
no_overwrite: false,
apply_coverage: false,
downsampling_ratio_percentage: None,
Expand Down
66 changes: 0 additions & 66 deletions move-mutator/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use crate::configuration::Configuration;
use codespan_reporting::diagnostic::Severity;
use either::Either;
use fs_extra::dir::CopyOptions;
use itertools::Itertools;
use legacy_move_compiler::shared::{
known_attributes::{AttributeKind, KnownAttribute},
Expand All @@ -17,7 +16,6 @@ use move_model::model::GlobalEnv;
use move_package::{
compilation::compiled_package::{make_source_and_deps_for_compiler, CompiledPackage},
resolution::resolution_graph::ResolvedTable,
source_package::layout::SourcePackageLayout,
BuildConfig,
};
use move_symbol_pool::Symbol;
Expand Down Expand Up @@ -275,70 +273,6 @@ fn prepare_compiler_for_files(
}
}

/// Verify the mutant.
/// This function compiles the mutated source and checks if the compilation is successful.
/// If the compilation is successful, the mutant is valid.
///
/// This function uses the Move compiler to compile the mutated source. To do so, it copies the whole package
/// to a temporary directory and replaces the original file with the mutated source. It may introduce problems
/// with dependencies that are specified as relative paths to the package root.
///
/// # Arguments
///
/// * `config` - the build configuration.
/// * `mutated_source` - the mutated source code as a string.
/// * `original_file` - the path to the original file.
///
/// # Errors
///
/// * If any error occurs during the verification, the string with the cause is returned.
///
/// # Returns
///
/// * `Result<(), anyhow::Error>` - Ok if the mutant is valid, or an error if any error occurs.
pub fn verify_mutant(
config: &BuildConfig,
mutated_source: &str,
original_file: &Path,
) -> Result<(), anyhow::Error> {
// Find the root for the package.
let root = SourcePackageLayout::try_find_root(&original_file.canonicalize()?)?;

debug!("Package path found: {root:?}");

// Get the relative path to the original file.
let relative_path = original_file.canonicalize()?;
let relative_path = relative_path.strip_prefix(&root)?;

debug!("Relative path: {relative_path:?}");

let tempdir = tempfile::tempdir()?;

debug!("Temporary directory: {:?}", tempdir.path());

// Copy the whole package to the tempdir.
// We need to copy the whole package because the Move compiler needs to find the Move.toml file and all the dependencies
// as we don't know which files are needed for the compilation.
let options = CopyOptions::new().content_only(true);
fs_extra::dir::copy(&root, &tempdir, &options)?;

// Write the mutated source to the tempdir in place of the original file.
std::fs::write(tempdir.path().join(relative_path), mutated_source)?;

debug!(
"Mutated source written to {:?}",
tempdir.path().join(relative_path)
);

// Create a working config, making sure that the test mode is disabled.
// We want just check if the compilation is successful.
let mut working_config = config.clone();
working_config.test_mode = false;
let _ = compile_package(working_config, tempdir.path())?;

Ok(())
}

pub(crate) fn compile_package(
build_config: BuildConfig,
package_path: &Path,
Expand Down
12 changes: 1 addition & 11 deletions move-mutator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod output;
pub mod report;

use crate::{
compiler::{generate_ast, verify_mutant},
compiler::generate_ast,
configuration::Configuration,
report::{MutationReport, Report},
};
Expand Down Expand Up @@ -169,16 +169,6 @@ pub fn run_move_mutator(
let rayon_tid = rayon::current_thread_index().unwrap_or(0);
info!("job_{rayon_tid}: Checking mutant {mutant}");

if mutator_configuration.project.verify_mutants {
let res = verify_mutant(&config, &mutated_info.mutated_source, &path);

// In case the mutant is not a valid Move file, skip the mutant (do not save it).
if let Err(e) = res {
info!("job_{rayon_tid}: Mutant {mutant} is invalid and will not be generated: {e:?}");
return None;
}
}

let mutant_id = mutated_info.unique_id();
let Ok(mutant_path) = output::setup_mutant_path(&output_dir, &path, mutant_id) else {
// If we cannot set up the mutant path, we skip the mutant.
Expand Down
28 changes: 0 additions & 28 deletions move-mutator/tests/basic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ fn check_mutator_verify_mutants_correctly() {

let options = CLIOptions {
out_mutant_dir: Some(outdir.clone()),
verify_mutants: true,
..Default::default()
};

Expand Down Expand Up @@ -177,33 +176,6 @@ fn check_mutator_properly_fails_with_single_files_that_require_dep_or_addr_resol
fs::remove_dir_all(package_path).unwrap();
}

// Check if the mutator produce zero mutants if verification is enabled for
// files without any package (we're unable to verify such files successfully).
#[test]
fn check_mutator_fails_verify_file_without_package() {
let package_path = clone_project("tests/move-assets/file_without_package");
let outdir = package_path.join("outdir");

let options = CLIOptions {
move_sources: vec![package_path.join("Sub.move")],
out_mutant_dir: Some(outdir.clone()),
verify_mutants: true,
..Default::default()
};

let config = quick_build_config();

let result = move_mutator::run_move_mutator(options.clone(), &config, &package_path);
assert!(result.is_ok());

let report_path = outdir.join("report.json");
assert!(report_path.exists());

let report = move_mutator::report::Report::load_from_json_file(&report_path).unwrap();
assert!(report.get_mutants().is_empty());
fs::remove_dir_all(package_path).unwrap();
}

// Check that the mutator will apply function-filters correctly and generate mutants only for
// specified functions.
#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@
},
{
"module_func": "Continue::sum_intermediate_in_for",
"tested": 50,
"killed": 49,
"tested": 55,
"killed": 54,
"mutants_alive_diffs": [
"--- original\n+++ modified\n@@ -18,7 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n+ for (i in 1..(n + 1)) {\n if (i % 10 == 0) continue;\n sum = sum + i\n };\n"
],
Expand All @@ -183,16 +183,21 @@
"--- original\n+++ modified\n@@ -18,7 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n+ for (i in 0..(n + 0)) {\n if (i % 10 == 0) continue;\n sum = sum + i\n };\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ true;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ false;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,10 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n+ !(for (i in 0..(n + 1)) {\n if (i % 10 == 0) continue;\n sum = sum + i\n- };\n+ });\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ true;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ false;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ true;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ false;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,10 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n+ !(for (i in 0..(n + 1)) {\n if (i % 10 == 0) continue;\n sum = sum + i\n- };\n+ });\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ 0;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ 18446744073709551615;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ 2;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ 0;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ true;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,10 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n- sum = sum + i\n- };\n+ false;\n\n sum\n }\n",
"--- original\n+++ modified\n@@ -18,7 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n+ for (true in 0..(n + 1)) {\n if (i % 10 == 0) continue;\n sum = sum + i\n };\n",
"--- original\n+++ modified\n@@ -18,7 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n+ for (false in 0..(n + 1)) {\n if (i % 10 == 0) continue;\n sum = sum + i\n };\n",
"--- original\n+++ modified\n@@ -18,7 +18,7 @@\n fun sum_intermediate_in_for(n: u64): u64 {\n let sum = 0;\n\n- for (i in 0..(n + 1)) {\n+ for (!(i) in 0..(n + 1)) {\n if (i % 10 == 0) continue;\n sum = sum + i\n };\n",
"--- original\n+++ modified\n@@ -19,7 +19,7 @@\n let sum = 0;\n\n for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n+ if (true) continue;\n sum = sum + i\n };\n\n",
"--- original\n+++ modified\n@@ -19,7 +19,7 @@\n let sum = 0;\n\n for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n+ if (false) continue;\n sum = sum + i\n };\n\n",
"--- original\n+++ modified\n@@ -19,7 +19,7 @@\n let sum = 0;\n\n for (i in 0..(n + 1)) {\n- if (i % 10 == 0) continue;\n+ if (!(i % 10 == 0)) continue;\n sum = sum + i\n };\n\n",
Expand Down
Loading
Loading