Skip to content

Commit 0d540ab

Browse files
committed
fix: foundry coverage
1 parent c7d2d77 commit 0d540ab

27 files changed

Lines changed: 486 additions & 238 deletions

l1-contracts/bootstrap.sh

Lines changed: 85 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -336,34 +336,39 @@ function coverage {
336336
forge --version
337337

338338
# Default values
339-
MATCH_PATH=""
339+
TEST_MATCH_PATH=""
340340
LCOV=false
341341
SERVE=false
342342
HELP=false
343343
GOVERNANCE=false
344+
CORE=false
344345

345346
# Help text
346347
show_help() {
347348
echo "Usage: ./bootstrap.sh coverage [options]"
348349
echo "Options:"
349-
echo " -p <path> Run coverage only for files matching this path pattern"
350-
echo " -l Generate LCOV report"
351-
echo " -s Serve coverage report (requires -l)"
350+
echo " -p <path> Run only tests in files matching this path pattern"
351+
echo " -c Run core coverage using only non-governance tests"
352+
echo " -l Generate a fresh LCOV report"
353+
echo " -s Serve the existing coverage report"
352354
echo " -g Run coverage for governance contracts using only gov tests"
353355
echo " -h Show this help message"
354356
echo ""
355357
echo "Examples:"
356358
echo " ./bootstrap.sh coverage # Run coverage for all files"
357-
echo " ./bootstrap.sh coverage -p src/core # Run coverage only for src/core"
358-
echo " ./bootstrap.sh coverage -l -s # Generate and serve LCOV report"
359+
echo " ./bootstrap.sh coverage -p test/staking_asset_handler/**/*.t.sol # Run only matching tests"
360+
echo " ./bootstrap.sh coverage -c # Run core coverage using only non-governance tests"
361+
echo " ./bootstrap.sh coverage -s # Serve the existing coverage report"
362+
echo " ./bootstrap.sh coverage -l -s # Generate and serve a fresh LCOV report"
359363
echo " ./bootstrap.sh coverage -g # Run coverage for governance contracts using only gov tests"
360364
echo " ./bootstrap.sh coverage -g -l -s # Run coverage for governance contracts using only gov tests with LCOV report and serve"
361365
}
362366

363367
# Parse options
364-
while getopts "p:lshg" opt; do
368+
while getopts "p:lcshg" opt; do
365369
case $opt in
366-
p) MATCH_PATH="$OPTARG" ;;
370+
p) TEST_MATCH_PATH="$OPTARG" ;;
371+
c) CORE=true ;;
367372
l) LCOV=true ;;
368373
s) SERVE=true ;;
369374
h) HELP=true ;;
@@ -378,54 +383,96 @@ function coverage {
378383
exit 0
379384
fi
380385

381-
# Validate serve option
382386
if [ "$SERVE" = true ] && [ "$LCOV" = false ]; then
383-
echo "Error: -s option requires -l option to be enabled"
387+
if [ -n "$TEST_MATCH_PATH" ] || [ "$GOVERNANCE" = true ] || [ "$CORE" = true ]; then
388+
echo "Warning: -s serves the existing report only; it cannot be combined with -p, -c, or -g without -l"
389+
exit 1
390+
fi
391+
392+
coverage_serve
393+
exit 0
394+
fi
395+
396+
download_solc
397+
398+
local -a ENV_VARS=("FOUNDRY_PROFILE=coverage" "FORGE_COVERAGE=true")
399+
local -a CMD=("forge" "coverage" "--offline")
400+
401+
if [ "$GOVERNANCE" = true ] && [ "$CORE" = true ]; then
402+
echo "Warning: -c and -g cannot be used together"
384403
exit 1
385404
fi
386405

387-
# Build the command
388406
if [ "$GOVERNANCE" = true ]; then
389-
CMD="FORGE_COVERAGE=true forge coverage --match-path \"test/governance/**/*.t.sol\" --no-match-coverage \"(test|script|mock|generated|core|periphery)\""
407+
CMD+=(
408+
"--match-path" "test/governance/**/*.t.sol"
409+
"--no-match-coverage" "(test|script|mock|generated|core|periphery)"
410+
)
411+
elif [ "$CORE" = true ]; then
412+
CMD+=(
413+
"--no-match-path" "test/governance/**/*.t.sol"
414+
"--no-match-coverage" "(test|script|mock|generated|governance)"
415+
)
390416
else
391-
# Default coverage command
392-
CMD="FORGE_COVERAGE=true forge coverage --no-match-coverage \"(test|script|mock|generated)\""
417+
CMD+=("--no-match-coverage" "(test|script|mock|generated)")
393418
fi
394419

395-
if [ -n "$MATCH_PATH" ] && [ "$GOVERNANCE" = true ]; then
396-
echo "Warning: -p option is not supported in governance mode"
420+
if [ -n "$TEST_MATCH_PATH" ] && { [ "$GOVERNANCE" = true ] || [ "$CORE" = true ]; }; then
421+
echo "Warning: -p option is not supported in governance or core mode"
397422
exit 1
398423
fi
399424

400-
# Add path filter if specified (only if not in governance mode)
401-
if [ -n "$MATCH_PATH" ] && [ "$GOVERNANCE" = false ]; then
402-
if [ ! -e "$MATCH_PATH" ]; then
403-
echo "Warning: Path '$MATCH_PATH' does not exist"
425+
# Add a test-file filter if specified (only if not in governance mode).
426+
# This narrows which tests execute via `--match-path`; it does not scope
427+
# coverage output to those paths.
428+
if [ -n "$TEST_MATCH_PATH" ] && [ "$GOVERNANCE" = false ]; then
429+
local -a MATCHED_PATHS=()
430+
shopt -s globstar nullglob
431+
MATCHED_PATHS=($TEST_MATCH_PATH)
432+
shopt -u globstar nullglob
433+
434+
if [ ${#MATCHED_PATHS[@]} -eq 0 ]; then
435+
echo "Warning: Path pattern '$TEST_MATCH_PATH' did not match any files"
404436
exit 1
405437
fi
406-
CMD="$CMD --match-path \"$MATCH_PATH\""
438+
CMD+=("--match-path" "$TEST_MATCH_PATH")
407439
fi
408440

409441
# Add LCOV report if requested
410442
if [ "$LCOV" = true ]; then
411-
CMD="$CMD --report lcov"
443+
CMD+=("--report" "lcov")
412444
fi
413445

414-
echo "Running coverage with command: $CMD"
415-
eval "$CMD"
446+
local DISPLAY_CMD
447+
printf -v DISPLAY_CMD '%q ' env "${ENV_VARS[@]}" "${CMD[@]}"
448+
echo "Running coverage with command: ${DISPLAY_CMD% }"
449+
env "${ENV_VARS[@]}" "${CMD[@]}"
416450

417451
# Serve report if requested
418452
if [ "$SERVE" = true ]; then
419-
if ! command -v genhtml &> /dev/null; then
420-
echo "Error: genhtml not found. Please install lcov package."
421-
exit 1
422-
fi
453+
coverage_serve
454+
fi
455+
}
456+
457+
function coverage_serve {
458+
echo_header "l1-contracts coverage serve"
459+
460+
if ! command -v genhtml &> /dev/null; then
461+
echo "Error: genhtml not found. Please install lcov package."
462+
exit 1
463+
fi
423464

424-
mkdir -p coverage
425-
genhtml lcov.info --branch-coverage --output-dir coverage
426-
echo "Serving coverage report at http://localhost:8000"
427-
python3 -m http.server --directory "coverage" 8000
465+
if [ ! -f "lcov.info" ]; then
466+
echo "Error: lcov.info not found. Run './bootstrap.sh coverage -l' first."
467+
exit 1
428468
fi
469+
470+
mkdir -p coverage
471+
# Foundry can emit LCOV branch records that genhtml treats as inconsistent
472+
# even when the report is otherwise usable.
473+
genhtml lcov.info --branch-coverage --ignore-errors inconsistent,inconsistent --output-dir coverage
474+
echo "Serving coverage report at http://localhost:8000"
475+
python3 -m http.server --directory "coverage" 8000
429476
}
430477

431478
function release {
@@ -445,6 +492,12 @@ case "$cmd" in
445492
"hash")
446493
echo $hash
447494
;;
495+
"coverage-serve")
496+
coverage_serve
497+
;;
498+
"coverage_serve")
499+
coverage_serve
500+
;;
448501
*)
449502
default_cmd_handler "$@"
450503
;;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

l1-contracts/foundry.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ remappings = [
3838
"@oz/=lib/openzeppelin-contracts/contracts/",
3939
"@aztec/=src",
4040
"@test/=test",
41+
"@generated/=generated/",
4142
"@zkpassport/=lib/circuits/src/solidity/src/",
4243
"@zkpassport-test/=lib/circuits/src/solidity/test",
4344
"@aztec-blob-lib/=src/mock/libraries",
@@ -95,7 +96,30 @@ remappings = [
9596
"@oz/=lib/openzeppelin-contracts/contracts/",
9697
"@aztec/=src",
9798
"@test/=test",
99+
"@generated/=generated/",
98100
"@zkpassport/=lib/circuits/src/solidity/src/",
99101
"@zkpassport-test/=lib/circuits/src/solidity/test",
100102
"@aztec-blob-lib/=src/core/libraries/rollup/",
101103
]
104+
105+
[profile.coverage]
106+
script = "coverage-script"
107+
sparse_mode = true
108+
skip = [
109+
"shouting.t.sol",
110+
"test/benchmark/happy.t.sol",
111+
"test/script",
112+
"script/deploy/DeployAztecL1Contracts.s.sol",
113+
"test/staking/move.t.sol",
114+
"test/validator-selection/setupEpoch.t.sol",
115+
"test/validator-selection/setupSampleSeed.t.sol",
116+
]
117+
remappings = [
118+
"@oz/=lib/openzeppelin-contracts/contracts/",
119+
"@aztec/=src",
120+
"@test/=test",
121+
"@generated/=src/mock/coverage/generated/",
122+
"@zkpassport/=src/mock/coverage/zkpassport/",
123+
"@zkpassport-test/=lib/circuits/src/solidity/test",
124+
"@aztec-blob-lib/=src/mock/libraries",
125+
]

l1-contracts/script/deploy/DeployRollupLib.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {TestERC20} from "@aztec/mock/TestERC20.sol";
2424

2525
import {SlashFactory} from "@aztec/periphery/SlashFactory.sol";
2626

27-
import {HonkVerifier} from "../../generated/HonkVerifier.sol";
27+
import {HonkVerifier} from "@generated/HonkVerifier.sol";
2828

2929
import {IRollupConfiguration} from "./RollupConfiguration.sol";
3030

l1-contracts/script/deploy/RollupConfiguration.sol

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -102,50 +102,46 @@ contract RollupConfiguration is IRollupConfiguration, Test {
102102
view
103103
returns (RollupConfigInput memory)
104104
{
105-
uint256 aztecSlotDuration = vm.envUint("AZTEC_SLOT_DURATION");
106-
uint256 aztecEpochDuration = vm.envUint("AZTEC_EPOCH_DURATION");
107-
uint256 roundSizeInEpochs = vm.envUint("AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS");
108-
uint256 slashingRoundSize = roundSizeInEpochs * aztecEpochDuration;
109-
// The slashing quorum, i.e. how many slots must signal for the same payload in a round for it to be submittable to
110-
// the Slasher (defaults to slashRoundSize / 2 + 1)
111-
uint256 slashingQuorum = vm.envOr("AZTEC_SLASHING_QUORUM", slashingRoundSize / 2 + 1);
112-
113-
// Build config without version first
114-
RollupConfigInput memory config = RollupConfigInput({
115-
aztecSlotDuration: aztecSlotDuration,
116-
aztecEpochDuration: aztecEpochDuration,
117-
targetCommitteeSize: vm.envUint("AZTEC_TARGET_COMMITTEE_SIZE"),
118-
lagInEpochsForValidatorSet: vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET"),
119-
lagInEpochsForRandao: vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_RANDAO"),
120-
inboxLag: vm.envUint("AZTEC_INBOX_LAG"),
121-
aztecProofSubmissionEpochs: vm.envUint("AZTEC_PROOF_SUBMISSION_EPOCHS"),
122-
localEjectionThreshold: vm.envUint("AZTEC_LOCAL_EJECTION_THRESHOLD"),
123-
slashingQuorum: slashingQuorum,
124-
slashingRoundSize: slashingRoundSize,
125-
slashingLifetimeInRounds: vm.envUint("AZTEC_SLASHING_LIFETIME_IN_ROUNDS"),
126-
slashingExecutionDelayInRounds: vm.envUint("AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS"),
127-
slashAmounts: _getSlashAmounts(),
128-
slashingOffsetInRounds: _getSlashingOffset(),
129-
slasherFlavor: _getSlasherFlavor(),
130-
slashingVetoer: vm.envAddress("AZTEC_SLASHING_VETOER"),
131-
slashingDisableDuration: vm.envUint("AZTEC_SLASHING_DISABLE_DURATION"),
132-
manaTarget: vm.envUint("AZTEC_MANA_TARGET"),
133-
exitDelaySeconds: vm.envUint("AZTEC_EXIT_DELAY_SECONDS"),
134-
version: 0, // Computed below
135-
provingCostPerMana: EthValue.wrap(vm.envUint("AZTEC_PROVING_COST_PER_MANA")),
136-
initialEthPerFeeAsset: EthPerFeeAssetE12.wrap(vm.envUint("AZTEC_INITIAL_ETH_PER_FEE_ASSET")),
137-
rewardConfig: this.getRewardConfiguration(_rewardDistributor),
138-
rewardBoostConfig: this.getRewardBoostConfiguration(),
139-
stakingQueueConfig: this.getStakingQueueConfiguration(),
140-
earliestRewardsClaimableTimestamp: getEarliestRewardsClaimableTimestamp()
141-
});
105+
RollupConfigInput memory config = _getBaseRollupConfiguration();
106+
config.rewardConfig = this.getRewardConfiguration(_rewardDistributor);
107+
config.rewardBoostConfig = this.getRewardBoostConfiguration();
108+
config.stakingQueueConfig = this.getStakingQueueConfiguration();
109+
config.earliestRewardsClaimableTimestamp = getEarliestRewardsClaimableTimestamp();
142110

143111
// Compute version as first 4 bytes of hash(abi.encode(config, genesisState))
144112
config.version = _computeConfigVersion(config, this.getGenesisState());
145113

146114
return config;
147115
}
148116

117+
function _getBaseRollupConfiguration() private view returns (RollupConfigInput memory config) {
118+
uint256 aztecEpochDuration = vm.envUint("AZTEC_EPOCH_DURATION");
119+
uint256 slashingRoundSize = vm.envUint("AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS") * aztecEpochDuration;
120+
121+
config.aztecSlotDuration = vm.envUint("AZTEC_SLOT_DURATION");
122+
config.aztecEpochDuration = aztecEpochDuration;
123+
config.targetCommitteeSize = vm.envUint("AZTEC_TARGET_COMMITTEE_SIZE");
124+
config.lagInEpochsForValidatorSet = vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET");
125+
config.lagInEpochsForRandao = vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_RANDAO");
126+
config.inboxLag = vm.envUint("AZTEC_INBOX_LAG");
127+
config.aztecProofSubmissionEpochs = vm.envUint("AZTEC_PROOF_SUBMISSION_EPOCHS");
128+
config.localEjectionThreshold = vm.envUint("AZTEC_LOCAL_EJECTION_THRESHOLD");
129+
config.slashingQuorum = vm.envOr("AZTEC_SLASHING_QUORUM", slashingRoundSize / 2 + 1);
130+
config.slashingRoundSize = slashingRoundSize;
131+
config.slashingLifetimeInRounds = vm.envUint("AZTEC_SLASHING_LIFETIME_IN_ROUNDS");
132+
config.slashingExecutionDelayInRounds = vm.envUint("AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS");
133+
config.slashAmounts = _getSlashAmounts();
134+
config.slashingOffsetInRounds = _getSlashingOffset();
135+
config.slasherFlavor = _getSlasherFlavor();
136+
config.slashingVetoer = vm.envAddress("AZTEC_SLASHING_VETOER");
137+
config.slashingDisableDuration = vm.envUint("AZTEC_SLASHING_DISABLE_DURATION");
138+
config.manaTarget = vm.envUint("AZTEC_MANA_TARGET");
139+
config.exitDelaySeconds = vm.envUint("AZTEC_EXIT_DELAY_SECONDS");
140+
config.version = 0;
141+
config.provingCostPerMana = EthValue.wrap(vm.envUint("AZTEC_PROVING_COST_PER_MANA"));
142+
config.initialEthPerFeeAsset = EthPerFeeAssetE12.wrap(vm.envUint("AZTEC_INITIAL_ETH_PER_FEE_ASSET"));
143+
}
144+
149145
/// @notice Compute rollup config version by hashing config + genesis state
150146
/// @dev Version is the first 4 bytes (uint32) of keccak256(abi.encode(rollupConfig, genesisState))
151147
/// This DOES NOT match the TS implementation: keccak256(jsonStringify({rollupConfigArgs, genesisStateArgs}))

0 commit comments

Comments
 (0)