// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![cfg(feature = "runtime-benchmarks")] use super::*; use frame_benchmarking::{v2::*, BenchmarkError}; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::One; fn assert_last_event(generic_event: ::RuntimeEvent) { frame_system::Pallet::::assert_last_event(generic_event.into()); } #[benchmarks] mod benches { use super::*; use frame_support::traits::Hooks; #[benchmark] fn onboard_new_mbms() { T::Migrations::set_fail_after(0); // Should not be called anyway. assert!(!Cursor::::exists()); #[block] { Pallet::::onboard_new_mbms(); } assert_last_event::(Event::UpgradeStarted { migrations: 1 }.into()); } #[benchmark] fn progress_mbms_none() { T::Migrations::set_fail_after(0); // Should not be called anyway. assert!(!Cursor::::exists()); #[block] { Pallet::::progress_mbms(One::one()); } } /// All migrations completed. #[benchmark] fn exec_migration_completed() -> Result<(), BenchmarkError> { T::Migrations::set_fail_after(0); // Should not be called anyway. assert_eq!(T::Migrations::len(), 1, "Setup failed"); let c = ActiveCursor { index: 1, inner_cursor: None, started_at: 0u32.into() }; let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); System::::set_block_number(1u32.into()); #[block] { Pallet::::exec_migration(c, false, &mut meter); } assert_last_event::(Event::UpgradeCompleted {}.into()); Ok(()) } /// No migration runs since it is skipped as historic. #[benchmark] fn exec_migration_skipped_historic() -> Result<(), BenchmarkError> { T::Migrations::set_fail_after(0); // Should not be called anyway. assert_eq!(T::Migrations::len(), 1, "Setup failed"); let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; let id: IdentifierOf = T::Migrations::nth_id(0).unwrap().try_into().unwrap(); Historic::::insert(id, ()); let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); System::::set_block_number(1u32.into()); #[block] { Pallet::::exec_migration(c, false, &mut meter); } assert_last_event::(Event::MigrationSkipped { index: 0 }.into()); Ok(()) } /// Advance a migration by one step. #[benchmark] fn exec_migration_advance() -> Result<(), BenchmarkError> { T::Migrations::set_success_after(1); assert_eq!(T::Migrations::len(), 1, "Setup failed"); let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); System::::set_block_number(1u32.into()); #[block] { Pallet::::exec_migration(c, false, &mut meter); } assert_last_event::(Event::MigrationAdvanced { index: 0, took: One::one() }.into()); Ok(()) } /// Successfully complete a migration. #[benchmark] fn exec_migration_complete() -> Result<(), BenchmarkError> { T::Migrations::set_success_after(0); assert_eq!(T::Migrations::len(), 1, "Setup failed"); let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); System::::set_block_number(1u32.into()); #[block] { Pallet::::exec_migration(c, false, &mut meter); } assert_last_event::(Event::MigrationCompleted { index: 0, took: One::one() }.into()); Ok(()) } #[benchmark] fn exec_migration_fail() -> Result<(), BenchmarkError> { T::Migrations::set_fail_after(0); assert_eq!(T::Migrations::len(), 1, "Setup failed"); let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); System::::set_block_number(1u32.into()); #[block] { Pallet::::exec_migration(c, false, &mut meter); } assert_last_event::(Event::UpgradeFailed {}.into()); Ok(()) } #[benchmark] fn on_init_loop() { T::Migrations::set_fail_after(0); // Should not be called anyway. System::::set_block_number(1u32.into()); Pallet::::on_runtime_upgrade(); #[block] { Pallet::::on_initialize(1u32.into()); } } #[benchmark] fn force_set_cursor() { #[extrinsic_call] _(RawOrigin::Root, Some(cursor::())); } #[benchmark] fn force_set_active_cursor() { #[extrinsic_call] _(RawOrigin::Root, 0, None, None); } #[benchmark] fn force_onboard_mbms() { #[extrinsic_call] _(RawOrigin::Root); } #[benchmark] fn clear_historic(n: Linear<0, { DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 }>) { let id_max_len = ::IdentifierMaxLen::get(); assert!(id_max_len >= 4, "Precondition violated"); for i in 0..DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 { let id = IdentifierOf::::truncate_from( i.encode().into_iter().cycle().take(id_max_len as usize).collect::>(), ); Historic::::insert(&id, ()); } #[extrinsic_call] _( RawOrigin::Root, HistoricCleanupSelector::Wildcard { limit: n.into(), previous_cursor: None }, ); } fn cursor() -> CursorOf { // Note: The weight of a function can depend on the weight of reading the `inner_cursor`. // `Cursor` is a user provided type. Now instead of requiring something like `Cursor: // From`, we instead rely on the fact that it is MEL and the PoV benchmarking will // therefore already take the MEL bound, even when the cursor in storage is `None`. MigrationCursor::Active(ActiveCursor { index: u32::MAX, inner_cursor: None, started_at: 0u32.into(), }) } // Implements a test for each benchmark. Execute with: // `cargo test -p pallet-migrations --features runtime-benchmarks`. impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); }