Skip to content
Draft
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
22 changes: 17 additions & 5 deletions crates/cairo-coverage-core/src/build/coverage_input.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::args::IncludedComponent;
use crate::build::executed_statement_count::ExecutedStatementCount;
use crate::build::filter::statement_category_filter::StatementCategoryFilter;
use crate::build::filter::ignore_matcher::CairoCoverageIgnoreMatcher;
use crate::build::statement_information::StatementInformationMap;
use crate::build::{executed_statement_count, statement_information};
use crate::build::{executed_statement_count, filter, statement_information};
use crate::loading::enriched_program::EnrichedProgram;
use crate::loading::execution_data::ExecutionData;
use anyhow::Result;
use cairo_lang_sierra::program::Program;
use cairo_lang_sierra_to_casm::compiler::{CairoProgramDebugInfo, SierraToCasmConfig};
use cairo_lang_sierra_to_casm::metadata::{MetadataComputationConfig, calc_metadata};
use cairo_lang_sierra_type_size::ProgramRegistryInfo;
use camino::Utf8Path;

/// All necessary data for the coverage analysis.
#[derive(Clone)]
Expand All @@ -29,15 +31,25 @@ pub fn build(
program,
coverage_annotations,
profiler_annotations,
..
test_executables,
},
}: ExecutionData,
filter: &StatementCategoryFilter,
project_path: &Utf8Path,
included_components: &[IncludedComponent],
ignore_matcher: &CairoCoverageIgnoreMatcher,
) -> CoverageInput {
let casm_debug_info = compile(&program).expect("failed to compile program to casm");

let filter = filter::statement_category_filter::build(
project_path,
included_components,
ignore_matcher,
&test_executables,
&casm_debug_info,
);

let statement_information_map =
statement_information::build_map(coverage_annotations, profiler_annotations, filter);
statement_information::build_map(coverage_annotations, profiler_annotations, &filter);

let executed_statement_count = executed_statement_count::build(
&casm_level_infos,
Expand Down
81 changes: 0 additions & 81 deletions crates/cairo-coverage-core/src/build/filter/libfuncs.rs

This file was deleted.

1 change: 0 additions & 1 deletion crates/cairo-coverage-core/src/build/filter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
pub mod ignore_matcher;
mod libfuncs;
pub mod statement_category_filter;
Original file line number Diff line number Diff line change
@@ -1,47 +1,42 @@
use crate::args::IncludedComponent;
use crate::build::filter::ignore_matcher::CairoCoverageIgnoreMatcher;
use crate::build::filter::libfuncs;
use crate::build::filter::libfuncs::NOT_RELIABLE_LIBFUNCS;
use crate::loading::enriched_program::EnrichedProgram;
use cairo_annotations::annotations::coverage::SourceFileFullPath;
use cairo_annotations::annotations::profiler::FunctionName;
use cairo_lang_sierra::ids::FunctionId;
use cairo_lang_sierra::program::StatementIdx;
use camino::Utf8PathBuf;
use std::collections::{HashMap, HashSet};
use cairo_lang_sierra_to_casm::compiler::CairoProgramDebugInfo;
use camino::Utf8Path;
use std::collections::HashSet;

/// Statement category filter that is used to filter out statements that should not be included in the coverage report.
/// `included_components` and `ignore_matcher` are references to reduce the amount of data that needs to be copied.
pub struct StatementCategoryFilter<'a> {
user_project_path: String,
user_project_path: &'a str,
included_components: &'a [IncludedComponent],
test_functions: HashSet<FunctionName>,
ignore_matcher: &'a CairoCoverageIgnoreMatcher,
libfunc_names_by_idx: HashMap<StatementIdx, String>,
casm_debug_info: &'a CairoProgramDebugInfo,
}

/// Build a new instance of the [`StatementCategoryFilter`] based on the given parameters.
pub fn build<'a>(
user_project_path: &Utf8PathBuf,
user_project_path: &'a Utf8Path,
included_components: &'a [IncludedComponent],
ignore_matcher: &'a CairoCoverageIgnoreMatcher,
enriched_program: &EnrichedProgram,
test_executables: &[FunctionId],
casm_debug_info: &'a CairoProgramDebugInfo,
) -> StatementCategoryFilter<'a> {
let test_functions = enriched_program
.test_executables
let test_functions = test_executables
.iter()
.map(ToString::to_string)
.map(FunctionName)
.collect();

let user_project_path = user_project_path.to_string();
let libfunc_names_by_idx = libfuncs::build_names_map(&enriched_program.program);

StatementCategoryFilter {
user_project_path,
user_project_path: user_project_path.as_str(),
included_components,
test_functions,
ignore_matcher,
libfunc_names_by_idx,
casm_debug_info,
}
}

Expand All @@ -56,7 +51,7 @@ impl StatementCategoryFilter<'_> {
) -> bool {
self.is_allowed_macro(function_name, is_macro)
&& self.is_user_function(source_file_full_path)
&& self.is_reliable_libfunc(idx)
&& self.does_compile_to_any_casm(idx)
&& self.is_not_ignored(source_file_full_path)
}

Expand All @@ -73,14 +68,12 @@ impl StatementCategoryFilter<'_> {
}

fn is_user_function(&self, source_file_full_path: &SourceFileFullPath) -> bool {
source_file_full_path.0.contains(&self.user_project_path)
source_file_full_path.0.contains(self.user_project_path)
}

fn is_reliable_libfunc(&self, idx: StatementIdx) -> bool {
!self
.libfunc_names_by_idx
.get(&idx)
.is_some_and(|libfunc_name| NOT_RELIABLE_LIBFUNCS.contains(libfunc_name))
fn does_compile_to_any_casm(&self, idx: StatementIdx) -> bool {
let debug_info = &self.casm_debug_info.sierra_statement_info[idx.0];
debug_info.start_offset != debug_info.end_offset
Comment on lines +74 to +76

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the logical change

}

fn is_not_ignored(&self, source_file_full_path: &SourceFileFullPath) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub type StatementInformationMap = HashMap<StatementIdx, StatementInformation>;
/// Additional information about a statement that is needed for coverage analysis.
#[derive(Clone, Eq, PartialEq)]
pub struct StatementInformation {
pub idx: StatementIdx,
pub function_name: FunctionName,
pub source_file_full_path: SourceFileFullPath,
pub line_range: LineRange,
Expand Down Expand Up @@ -90,7 +89,6 @@ fn get_statement_information(
function_name,
source_file_full_path,
line_range: line_range.into(),
idx,
})
},
)
Expand Down
10 changes: 1 addition & 9 deletions crates/cairo-coverage-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ mod output;
use crate::args::RunOptions;
use crate::build::coverage_input;
use crate::build::filter::ignore_matcher;
use crate::build::filter::statement_category_filter;
use crate::hashmap_utils::merge::merge;
use crate::loading::execution_data;
use crate::output::lcov;
Expand All @@ -34,14 +33,7 @@ pub fn run(
let mut project_coverage = execution_data::load(&trace_files)?
.into_par_iter()
.map(|execution_data| {
let filter = statement_category_filter::build(
&project_path,
&include,
&ignore_matcher,
&execution_data.enriched_program,
);

coverage_input::build(execution_data, &filter)
coverage_input::build(execution_data, &project_path, &include, &ignore_matcher)
})
.map(coverage::project::create)
.collect::<Vec<_>>()
Expand Down
16 changes: 14 additions & 2 deletions crates/cairo-coverage/tests/e2e/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,27 @@ fn macros_not_included() {
TestProject::new("macros")
.coverage_args(&["--unstable", "--include"])
.run_without_genhtml()
.assert_empty_output();
.output_same_as_in_file("macros_not_included.lcov");
}

#[test]
fn snforge_template() {
let file = if scarb_version() >= Version::new(2, 15, 0) {
let version = scarb_version();
let file = if version >= Version::new(2, 18, 0) {
// In cairo 2.18.0 `ContractStateDerefMut::deref_mut` and `unsafe_new_contract_state`
// started being called again during test execution.
"snforge_template-scarb-2.18.lcov"
} else if version >= Version::new(2, 15, 0) {
// In cairo 2.15.0 `#[starknet::contract]` attribute generates different code.
// Hence, we have different expected output for scarb 2.15.0 and above.
"snforge_template-scarb-2.15.lcov"
} else if version >= Version::new(2, 11, 0) {
// In cairo 2.11.0 `SnapshotDeref::snapshot_deref` was replaced by impl `SnapshotTDeref::deref`.
"snforge_template-scarb-2.11.lcov"
} else if version >= Version::new(2, 10, 0) {
// In cairo 2.10.0 `ContractStateDerefMut::deref_mut` and `unsafe_new_contract_state`
// stopped being called during test execution.
"snforge_template-scarb-2.10.lcov"
} else {
"snforge_template.lcov"
};
Expand Down
11 changes: 7 additions & 4 deletions crates/cairo-coverage/tests/expected_output/macros.lcov
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ TN:
SF:{dir}/src/lib.cairo
FN:2,macros::assert_macro
FNDA:1,macros::assert_macro
FN:1,macros::function_with_macro
FNDA:1,macros::function_with_macro
FN:2,macros::write_macro
FNDA:0,macros::write_macro
FNF:2
FNH:1
FNF:3
FNH:2
DA:1,1
DA:2,1
LF:1
LH:1
LF:2
LH:2
end_of_record
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
TN:
SF:{dir}/src/lib.cairo
FN:1,macros::function_with_macro
FNDA:1,macros::function_with_macro
FNF:1
FNH:1
DA:1,1
LF:1
LH:1
end_of_record
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
TN:
SF:{dir}/src/lib.cairo
FN:8,scarb_template::fib
FN:5,scarb_template::fib
FNDA:1,scarb_template::fib
FNF:1
FNH:1
DA:5,1
DA:8,1
DA:9,1
DA:11,1
LF:3
LH:3
LF:4
LH:4
end_of_record
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
TN:
SF:{dir}/src/lib.cairo
FN:9,snforge_template::HelloStarknet::ContractStateDeref::snapshot_deref
FNDA:1,snforge_template::HelloStarknet::ContractStateDeref::snapshot_deref
FN:9,snforge_template::HelloStarknet::ContractStateDerefMut::deref_mut
FNDA:0,snforge_template::HelloStarknet::ContractStateDerefMut::deref_mut
FN:22,snforge_template::HelloStarknet::HelloStarknetImpl::get_balance
FNDA:1,snforge_template::HelloStarknet::HelloStarknetImpl::get_balance
FN:16,snforge_template::HelloStarknet::HelloStarknetImpl::increase_balance
FNDA:1,snforge_template::HelloStarknet::HelloStarknetImpl::increase_balance
FN:9,snforge_template::HelloStarknet::StorageStorageImpl::storage
FNDA:1,snforge_template::HelloStarknet::StorageStorageImpl::storage
FN:9,snforge_template::HelloStarknet::StorageStorageMutImpl::storage_mut
FNDA:1,snforge_template::HelloStarknet::StorageStorageMutImpl::storage_mut
FN:21,snforge_template::HelloStarknet::__wrapper__HelloStarknetImpl__get_balance
FNDA:1,snforge_template::HelloStarknet::__wrapper__HelloStarknetImpl__get_balance
FN:16,snforge_template::HelloStarknet::__wrapper__HelloStarknetImpl__increase_balance
FNDA:1,snforge_template::HelloStarknet::__wrapper__HelloStarknetImpl__increase_balance
FN:9,snforge_template::HelloStarknet::unsafe_new_contract_state
FNDA:0,snforge_template::HelloStarknet::unsafe_new_contract_state
FN:1,snforge_template::IHelloStarknetDispatcherImpl::get_balance
FNDA:1,snforge_template::IHelloStarknetDispatcherImpl::get_balance
FN:1,snforge_template::IHelloStarknetDispatcherImpl::increase_balance
FNDA:1,snforge_template::IHelloStarknetDispatcherImpl::increase_balance
FN:1,snforge_template::IHelloStarknetSafeDispatcherImpl::get_balance
FNDA:1,snforge_template::IHelloStarknetSafeDispatcherImpl::get_balance
FN:1,snforge_template::IHelloStarknetSafeDispatcherImpl::increase_balance
FNDA:1,snforge_template::IHelloStarknetSafeDispatcherImpl::increase_balance
FNF:13
FNH:11
DA:1,4
DA:9,3
DA:16,2
DA:17,1
DA:18,1
DA:21,1
DA:22,1
LF:7
LH:7
end_of_record
TN:
SF:{dir}/tests/test_contract.cairo
FN:10,snforge_template_integrationtest::test_contract::deploy_contract
FNDA:1,snforge_template_integrationtest::test_contract::deploy_contract
FNF:1
FNH:1
DA:10,1
DA:11,1
DA:12,1
LF:3
LH:3
end_of_record
Loading
Loading