// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . //! Test the lazy migration. #![cfg(test)] use super::{migration::*, mock::*}; use crate::*; use frame_support::{ pallet_prelude::*, traits::{OnFinalize, OnIdle, OnInitialize}, StorageNoopGuard, }; #[test] fn migration_works() { let mut ext = new_test_ext(); ext.execute_with(|| { sp_tracing::try_init_simple(); // Insert some storage: PageIndex::::set(PageIndexData { begin_used: 10, end_used: 20, overweight_count: 5, }); for p in 10..20 { let msgs = (0..16).map(|i| (p, vec![i as u8; 1])).collect::>(); Pages::::insert(p, msgs); } for i in 0..5 { Overweight::::insert(i, (0, vec![i as u8; 1])); } testing_only::Configuration::::put(123); }); // We need to commit, otherwise the keys are removed from the overlay; not the backend. ext.commit_all().unwrap(); ext.execute_with(|| { // Run one step of the migration: pre_upgrade_checks::(); run_to_block(1); // First we expect a StartedExport event: assert_only_event(Event::StartedExport); // Then we expect 10 Exported events: for page in 0..10 { run_to_block(2 + page); assert_only_event(Event::Exported { page: page as u32 + 10 }); assert!(!Pages::::contains_key(page as u32), "Page is gone"); assert_eq!( MigrationStatus::::get(), MigrationState::StartedExport { next_begin_used: page as u32 + 11 } ); } // Then we expect a CompletedExport event: run_to_block(12); assert_only_event(Event::CompletedExport); assert_eq!(MigrationStatus::::get(), MigrationState::CompletedExport); // Then we expect a StartedOverweightExport event: run_to_block(13); assert_only_event(Event::StartedOverweightExport); assert_eq!( MigrationStatus::::get(), MigrationState::StartedOverweightExport { next_overweight_index: 0 } ); // Then we expect 5 ExportedOverweight events: for index in 0..5 { run_to_block(14 + index); assert_only_event(Event::ExportedOverweight { index }); assert!(!Overweight::::contains_key(index), "Overweight msg is gone"); assert_eq!( MigrationStatus::::get(), MigrationState::StartedOverweightExport { next_overweight_index: index + 1 } ); } // Then we expect a CompletedOverweightExport event: run_to_block(19); assert_only_event(Event::CompletedOverweightExport); assert_eq!(MigrationStatus::::get(), MigrationState::CompletedOverweightExport); // Then we expect a StartedCleanup event: run_to_block(20); assert_only_event(Event::StartedCleanup); assert_eq!( MigrationStatus::::get(), MigrationState::StartedCleanup { cursor: None } ); }); ext.commit_all().unwrap(); // Then it cleans up the remaining storage items: ext.execute_with(|| { run_to_block(21); assert_only_event(Event::CleanedSome { keys_removed: 2 }); }); ext.commit_all().unwrap(); ext.execute_with(|| { run_to_block(22); assert_only_event(Event::CleanedSome { keys_removed: 2 }); }); ext.commit_all().unwrap(); ext.execute_with(|| { run_to_block(24); assert_eq!( System::events().into_iter().map(|e| e.event).collect::>(), vec![ Event::CleanedSome { keys_removed: 2 }.into(), Event::Completed { error: false }.into() ] ); System::reset_events(); assert_eq!(MigrationStatus::::get(), MigrationState::Completed); post_upgrade_checks::(); assert_eq!(RecordedMessages::take().len(), 10 * 16 + 5); // Test the storage removal: assert!(!PageIndex::::exists()); assert!(!testing_only::Configuration::::exists()); assert_eq!(Pages::::iter_keys().count(), 0); assert_eq!(Overweight::::iter_keys().count(), 0); // The `MigrationStatus` never disappears and there are no more storage changes: { let _g = StorageNoopGuard::default(); run_to_block(100); assert_eq!(MigrationStatus::::get(), MigrationState::Completed); assert!(System::events().is_empty()); // ... besides the block number System::set_block_number(24); } }); } /// Too long messages are dropped by the migration. #[test] fn migration_too_long_ignored() { new_test_ext().execute_with(|| { // Setup the storage: PageIndex::::set(PageIndexData { begin_used: 10, end_used: 11, overweight_count: 2, }); let short = vec![1; 16]; let long = vec![0; 17]; Pages::::insert(10, vec![(10, short.clone()), (10, long.clone())]); // Insert one good and one bad overweight msg: Overweight::::insert(0, (0, short.clone())); Overweight::::insert(1, (0, long.clone())); // Run the migration: pre_upgrade_checks::(); run_to_block(100); post_upgrade_checks::(); assert_eq!(RecordedMessages::take(), vec![short.clone(), short]); // Test the storage removal: assert!(!PageIndex::::exists()); assert_eq!(Pages::::iter_keys().count(), 0); assert_eq!(Overweight::::iter_keys().count(), 0); }); } fn run_to_block(n: u64) { assert!(n > System::block_number(), "Cannot go back in time"); while System::block_number() < n { AllPalletsWithSystem::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); AllPalletsWithSystem::on_initialize(System::block_number()); AllPalletsWithSystem::on_idle(System::block_number(), Weight::MAX); } } fn assert_only_event(e: Event) { assert_eq!(System::events().pop().expect("Event expected").event, e.clone().into()); assert_eq!(System::events().len(), 1, "Got events: {:?} but wanted {:?}", System::events(), e); System::reset_events(); } /// TESTING ONLY fn pre_upgrade_checks() { let index = PageIndex::::get(); // Check that all pages are present. assert!(index.begin_used <= index.end_used, "Invalid page index"); for p in index.begin_used..index.end_used { assert!(Pages::::contains_key(p), "Missing page"); assert!(Pages::::get(p).len() > 0, "Empty page"); } // Check that all overweight messages are present. for i in 0..index.overweight_count { assert!(Overweight::::contains_key(i), "Missing overweight message"); } } /// TESTING ONLY fn post_upgrade_checks() { let index = PageIndex::::get(); // Check that all pages are removed. for p in index.begin_used..index.end_used { assert!(!Pages::::contains_key(p), "Page should be gone"); } assert!(Pages::::iter_keys().next().is_none(), "Un-indexed pages"); // Check that all overweight messages are removed. for i in 0..index.overweight_count { assert!(!Overweight::::contains_key(i), "Overweight message should be gone"); } assert!(Overweight::::iter_keys().next().is_none(), "Un-indexed overweight messages"); }