ronantakizawa/codereview-bench huggingface dataset:
Repository: paritytech/substrate
File: primitives/runtime/src/traits.rs (Rust)

Code before:
	+ Eq
	+ PartialEq
	+ Hasher<Out = <Self as Hash>::Output>
{
	/// The hash type produced.
	type Output: HashOutput;

	/// Produce the hash of some byte-slice.
	fn hash(s: &[u8]) -> Self::Output {
		<Self as Hasher>::hash(s)
	}

	/// Produce the hash of some codec-encodable value.
	fn hash_of<S: Encode>(s: &S) -> Self::Output {
		Encode::using_encoded(s, <Self as Hasher>::hash)
	}

	/// The ordered Patricia tree root of the given `input`.
	fn ordered_trie_root(input: Vec<Vec<u8>>, state_version: StateVersion) -> Self::Output;

	/// The Patricia tree root of the given mapping.
	fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>, state_version: StateVersion) -> Self::Output;
}

/// A generic hash output type trait.
pub trait HashOutput:
	Member
	+ MaybeSerializeDeserialize
	+ MaybeDisplay
	+ Debug
	+ sp_std::hash::Hash
	+ AsRef<[u8]>
	+ AsMut<[u8]>
	+ Copy
	+ Ord
	+ Default
	+ Encode
	+ Decode
	+ EncodeLike
	+ MaxEncodedLen
	+ TypeInfo
{
}

impl<T> HashOutput for T where
	T: Member
		+ MaybeSerializeDeserialize
		+ MaybeDisplay
		+ Debug
		+ sp_std::hash::Hash
		+ AsRef<[u8]>

Review comment (suggestion):
```suggestion
/// Super trait with all the attributes for a hashing output.
pub trait HashOutput:
```

Code after:
	+ Eq
	+ PartialEq
	+ Hasher<Out = <Self as Hash>::Output>
{
	/// The hash type produced.
	type Output: HashOutput;

	/// Produce the hash of some byte-slice.
	fn hash(s: &[u8]) -> Self::Output {
		<Self as Hasher>::hash(s)
	}

	/// Produce the hash of some codec-encodable value.
	fn hash_of<S: Encode>(s: &S) -> Self::Output {
		Encode::using_encoded(s, <Self as Hasher>::hash)
	}

	/// The ordered Patricia tree root of the given `input`.
	fn ordered_trie_root(input: Vec<Vec<u8>>, state_version: StateVersion) -> Self::Output;

	/// The Patricia tree root of the given mapping.
	fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>, state_version: StateVersion) -> Self::Output;
}

/// Super trait with all the attributes for a hashing output.
pub trait HashOutput:
	Member
	+ MaybeSerializeDeserialize
	+ MaybeDisplay
	+ MaybeFromStr
	+ Debug
	+ sp_std::hash::Hash
	+ AsRef<[u8]>
	+ AsMut<[u8]>
	+ Copy
	+ Ord
	+ Default
	+ Encode
	+ Decode
	+ EncodeLike
	+ MaxEncodedLen
	+ TypeInfo
{
}

impl<T> HashOutput for T where
	T: Member
		+ MaybeSerializeDeserialize
		+ MaybeDisplay
		+ MaybeFromStr
		+ Debug
Repository: paritytech/substrate
File: primitives/runtime/src/generic/header.rs (Rust)

Code before:
}

#[cfg(feature = "serde")]
pub fn deserialize_number<'a, D, T: Copy + Into<U256> + TryFrom<U256>>(d: D) -> Result<T, D::Error>
where
	D: serde::Deserializer<'a>,
{
	let u256: U256 = serde::Deserialize::deserialize(d)?;
	TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
}

impl<Number, Hash> traits::Header for Header<Number, Hash>
where
	Number: Member
		+ MaybeSerializeDeserialize
		+ MaybeFromStr
		+ Debug
		+ sp_std::hash::Hash
		+ MaybeDisplay
		+ AtLeast32BitUnsigned
		+ Codec
		+ Copy
		+ Into<U256>
		+ TryFrom<U256>,
	Hash: HashT,
	Hash::Output: HashOutput,
{
	type Number = Number;
	type Hash = <Hash as HashT>::Output;
	type Hashing = Hash;

	fn new(
		number: Self::Number,
		extrinsics_root: Self::Hash,
		state_root: Self::Hash,
		parent_hash: Self::Hash,
		digest: Digest,
	) -> Self {
		Self { number, extrinsics_root, state_root, parent_hash, digest }
	}
	fn number(&self) -> &Self::Number {
		&self.number
	}

	fn set_number(&mut self, num: Self::Number) {
		self.number = num
	}
	fn extrinsics_root(&self) -> &Self::Hash {
		&self.extrinsics_root
	}


Review comment (suggestion):
```suggestion
```
The `HashT::Output` already has this bound

Code after:
}

#[cfg(feature = "serde")]
pub fn deserialize_number<'a, D, T: Copy + Into<U256> + TryFrom<U256>>(d: D) -> Result<T, D::Error>
where
	D: serde::Deserializer<'a>,
{
	let u256: U256 = serde::Deserialize::deserialize(d)?;
	TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
}

impl<Number, Hash> traits::Header for Header<Number, Hash>
where
	Number: Member
		+ MaybeSerializeDeserialize
		+ MaybeFromStr
		+ Debug
		+ sp_std::hash::Hash
		+ MaybeDisplay
		+ AtLeast32BitUnsigned
		+ Codec
		+ Copy
		+ Into<U256>
		+ TryFrom<U256>,
	Hash: HashT,
{
	type Number = Number;
	type Hash = <Hash as HashT>::Output;
	type Hashing = Hash;

	fn new(
		number: Self::Number,
		extrinsics_root: Self::Hash,
		state_root: Self::Hash,
		parent_hash: Self::Hash,
		digest: Digest,
	) -> Self {
		Self { number, extrinsics_root, state_root, parent_hash, digest }
	}
	fn number(&self) -> &Self::Number {
		&self.number
	}

	fn set_number(&mut self, num: Self::Number) {
		self.number = num
	}
	fn extrinsics_root(&self) -> &Self::Hash {
		&self.extrinsics_root
	}

	fn set_extrinsics_root(&mut self, root: Self::Hash) {
Repository: paritytech/substrate
File: frame/democracy/src/lib.rs (Rust)

Code before:
use sp_std::prelude::*;

mod conviction;
mod types;
mod vote;
mod vote_threshold;
pub mod weights;
pub use conviction::Conviction;
pub use pallet::*;
pub use types::{
	Delegations, MetadataOwner, PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus,
	Tally, UnvoteScope,
};
pub use vote::{AccountVote, Vote, Voting};
pub use vote_threshold::{Approved, VoteThreshold};
pub use weights::WeightInfo;

#[cfg(test)]
mod tests;

#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;

pub mod migrations;

pub const DEMOCRACY_ID: LockIdentifier = *b"democrac";

type BalanceOf<T> =
	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
	<T as frame_system::Config>::AccountId,
>>::NegativeImbalance;
pub type CallOf<T> = <T as frame_system::Config>::RuntimeCall;
pub type BoundedCallOf<T> = Bounded<CallOf<T>>;
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;

#[frame_support::pallet]
pub mod pallet {
	use super::{DispatchResult, *};
	use frame_support::pallet_prelude::*;
	use frame_system::pallet_prelude::*;
	use sp_core::H256;

	/// The current storage version.
	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);

	#[pallet::pallet]
	#[pallet::storage_version(STORAGE_VERSION)]
	pub struct Pallet<T>(_);

	#[pallet::config]

Review comment (suggestion):
```suggestion
pub(super) const DEMOCRACY_ID: LockIdentifier = *b"democrac";
```

Code after:
use sp_std::prelude::*;

mod conviction;
mod types;
mod vote;
mod vote_threshold;
pub mod weights;
pub use conviction::Conviction;
pub use pallet::*;
pub use types::{
	Delegations, MetadataOwner, PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus,
	Tally, UnvoteScope,
};
pub use vote::{AccountVote, Vote, Voting};
pub use vote_threshold::{Approved, VoteThreshold};
pub use weights::WeightInfo;

#[cfg(test)]
mod tests;

#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;

pub mod migrations;

pub(crate) const DEMOCRACY_ID: LockIdentifier = *b"democrac";

type BalanceOf<T> =
	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
	<T as frame_system::Config>::AccountId,
>>::NegativeImbalance;
pub type CallOf<T> = <T as frame_system::Config>::RuntimeCall;
pub type BoundedCallOf<T> = Bounded<CallOf<T>>;
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;

#[frame_support::pallet]
pub mod pallet {
	use super::{DispatchResult, *};
	use frame_support::pallet_prelude::*;
	use frame_system::pallet_prelude::*;
	use sp_core::H256;

	/// The current storage version.
	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);

	#[pallet::pallet]
	#[pallet::storage_version(STORAGE_VERSION)]
	pub struct Pallet<T>(_);

	#[pallet::config]
Repository: paritytech/substrate
File: primitives/runtime/src/traits.rs (Rust)

Code before:

/// Infallible conversion trait. Generic over both source and destination types.
pub trait Convert<A, B> {
	/// Make conversion.
	fn convert(a: A) -> B;
}

/// Fallible conversion trait returning a Result. Generic over both source and destination types.
///
/// In case an `Error` is returned, it must alwats be the operand which is returned.
pub trait TryConvert<A, B> {
	/// Make conversion.
	fn try_convert(a: A) -> Result<B, A>;
}

/// Reversing infallible conversion trait. Generic over both source and destination types.
///
/// This specifically reverses the conversion.
pub trait ConvertBack<A, B>: Convert<A, B> {
	/// Make conversion back.
	fn convert_back(b: B) -> A;
}

/// Fallible conversion trait returning a Result. Generic over both source and destination types.
///
/// In case an `Error` is returned, it must alwats be the operand which is returned.
pub trait TryConvertBack<A, B>: TryConvert<A, B> {
	/// Make conversion.
	fn try_convert_back(b: B) -> Result<A, B>;
}

impl<A, B: Default> Convert<A, B> for () {
	fn convert(_: A) -> B {
		Default::default()
	}
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<A, B> TryConvert<A, B> for Tuple {
	fn try_convert(a: A) -> Result<B, A> {
		for_tuples!( #(
			let a = match Tuple::try_convert(a) {
				Ok(b) => return Ok(b),
				Err(a) => a,
			};
		)* );
		Err(a)
	}
}

#[impl_trait_for_tuples::impl_for_tuples(30)]

Review comment (suggestion):
```suggestion
/// In case an `Error` is returned, it must always be the operand which is returned.
```

Code after:

/// Infallible conversion trait. Generic over both source and destination types.
pub trait Convert<A, B> {
	/// Make conversion.
	fn convert(a: A) -> B;
}

impl<A, B: Default> Convert<A, B> for () {
	fn convert(_: A) -> B {
		Default::default()
	}
}

/// Reversing infallible conversion trait. Generic over both source and destination types.
///
/// This specifically reverses the conversion.
pub trait ConvertBack<A, B>: Convert<A, B> {
	/// Make conversion back.
	fn convert_back(b: B) -> A;
}

/// Fallible conversion trait returning an [Option]. Generic over both source and destination types.
pub trait MaybeConvert<A, B> {
	/// Attempt to make conversion.
	fn maybe_convert(a: A) -> Option<B>;
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<A: Clone, B> MaybeConvert<A, B> for Tuple {
	fn maybe_convert(a: A) -> Option<B> {
		for_tuples!( #(
			match Tuple::maybe_convert(a.clone()) {
				Some(b) => return Some(b),
				None => {},
			}
		)* );
		None
	}
}

/// Reversing fallible conversion trait returning an [Option]. Generic over both source and
/// destination types.
pub trait MaybeConvertBack<A, B>: MaybeConvert<A, B> {
	/// Attempt to make conversion back.
	fn maybe_convert_back(b: B) -> Option<A>;
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<A: Clone, B: Clone> MaybeConvertBack<A, B> for Tuple {
	fn maybe_convert_back(b: B) -> Option<A> {
		for_tuples!( #(
Repository: paritytech/substrate
File: primitives/runtime/src/traits.rs (Rust)

Code before:
pub trait Convert<A, B> {
	/// Make conversion.
	fn convert(a: A) -> B;
}

/// Fallible conversion trait returning a Result. Generic over both source and destination types.
///
/// In case an `Error` is returned, it must alwats be the operand which is returned.
pub trait TryConvert<A, B> {
	/// Make conversion.
	fn try_convert(a: A) -> Result<B, A>;
}

/// Reversing infallible conversion trait. Generic over both source and destination types.
///
/// This specifically reverses the conversion.
pub trait ConvertBack<A, B>: Convert<A, B> {
	/// Make conversion back.
	fn convert_back(b: B) -> A;
}

/// Fallible conversion trait returning a Result. Generic over both source and destination types.
///
/// In case an `Error` is returned, it must alwats be the operand which is returned.
pub trait TryConvertBack<A, B>: TryConvert<A, B> {
	/// Make conversion.
	fn try_convert_back(b: B) -> Result<A, B>;
}

impl<A, B: Default> Convert<A, B> for () {
	fn convert(_: A) -> B {
		Default::default()
	}
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<A, B> TryConvert<A, B> for Tuple {
	fn try_convert(a: A) -> Result<B, A> {
		for_tuples!( #(
			let a = match Tuple::try_convert(a) {
				Ok(b) => return Ok(b),
				Err(a) => a,
			};
		)* );
		Err(a)
	}
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<A, B> TryConvertBack<A, B> for Tuple {
	fn try_convert_back(b: B) -> Result<A, B> {

Review comment (suggestion):
```suggestion
	/// Attempt to reverse the conversion and return the input value in the error case.
```

Code after:
pub trait Convert<A, B> {
	/// Make conversion.
	fn convert(a: A) -> B;
}

impl<A, B: Default> Convert<A, B> for () {
	fn convert(_: A) -> B {
		Default::default()
	}
}

/// Reversing infallible conversion trait. Generic over both source and destination types.
///
/// This specifically reverses the conversion.
pub trait ConvertBack<A, B>: Convert<A, B> {
	/// Make conversion back.
	fn convert_back(b: B) -> A;
}

/// Fallible conversion trait returning an [Option]. Generic over both source and destination types.
pub trait MaybeConvert<A, B> {
	/// Attempt to make conversion.
	fn maybe_convert(a: A) -> Option<B>;
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<A: Clone, B> MaybeConvert<A, B> for Tuple {
	fn maybe_convert(a: A) -> Option<B> {
		for_tuples!( #(
			match Tuple::maybe_convert(a.clone()) {
				Some(b) => return Some(b),
				None => {},
			}
		)* );
		None
	}
}

/// Reversing fallible conversion trait returning an [Option]. Generic over both source and
/// destination types.
pub trait MaybeConvertBack<A, B>: MaybeConvert<A, B> {
	/// Attempt to make conversion back.
	fn maybe_convert_back(b: B) -> Option<A>;
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<A: Clone, B: Clone> MaybeConvertBack<A, B> for Tuple {
	fn maybe_convert_back(b: B) -> Option<A> {
		for_tuples!( #(
			match Tuple::maybe_convert_back(b.clone()) {
				Some(a) => return Some(a),
Repository: paritytech/substrate
File: frame/glutton/src/tests.rs (Rust)

Code before:
		assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), FixedU64::from_u32(10)),);
		// > 1000% is not
		assert_noop!(
			Glutton::set_compute(RuntimeOrigin::root(), FixedU64::from_float(10.01)),
			Error::<Test>::InsaneLimit
		);
	});
}

#[test]
fn setting_storage_works() {
	new_test_ext().execute_with(|| {
		assert!(Storage::<Test>::get().is_zero());

		assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(0.3)));
		assert_eq!(Storage::<Test>::get(), FixedU64::from_float(0.3));
		System::assert_last_event(
			Event::StorageLimitSet { storage: FixedU64::from_float(0.3) }.into(),
		);

		assert_noop!(
			Glutton::set_storage(RuntimeOrigin::signed(1), FixedU64::from_float(0.3)),
			DispatchError::BadOrigin
		);
		assert_noop!(
			Glutton::set_storage(RuntimeOrigin::none(), FixedU64::from_float(0.3)),
			DispatchError::BadOrigin
		);
	});
}

#[test]
fn setting_storage_respects_limit() {
	new_test_ext().execute_with(|| {
		// < 1000% is fine
		assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(9.99)),);
		// == 1000% is fine
		assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_u32(10)),);
		// > 1000% is not
		assert_noop!(
			Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(10.01)),
			Error::<Test>::InsaneLimit
		);
	});
}

#[test]
fn on_idle_works() {
	new_test_ext().execute_with(|| {
		set_limits(One::one(), One::one());


Review comment (suggestion):
```suggestion
			Glutton::set_storage(RuntimeOrigin::none(), FixedU64::from_float(0.5)),
```

Code after:
		assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), FixedU64::from_u32(10)),);
		// > 1000% is not
		assert_noop!(
			Glutton::set_compute(RuntimeOrigin::root(), FixedU64::from_float(10.01)),
			Error::<Test>::InsaneLimit
		);
	});
}

#[test]
fn setting_storage_works() {
	new_test_ext().execute_with(|| {
		assert!(Storage::<Test>::get().is_zero());

		assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(0.3)));
		assert_eq!(Storage::<Test>::get(), FixedU64::from_float(0.3));
		System::assert_last_event(
			Event::StorageLimitSet { storage: FixedU64::from_float(0.3) }.into(),
		);

		assert_noop!(
			Glutton::set_storage(RuntimeOrigin::signed(1), FixedU64::from_float(0.5)),
			DispatchError::BadOrigin
		);
		assert_noop!(
			Glutton::set_storage(RuntimeOrigin::none(), FixedU64::from_float(0.5)),
			DispatchError::BadOrigin
		);
	});
}

#[test]
fn setting_storage_respects_limit() {
	new_test_ext().execute_with(|| {
		// < 1000% is fine
		assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(9.99)),);
		// == 1000% is fine
		assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_u32(10)),);
		// > 1000% is not
		assert_noop!(
			Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(10.01)),
			Error::<Test>::InsaneLimit
		);
	});
}

#[test]
fn on_idle_works() {
	new_test_ext().execute_with(|| {
		set_limits(One::one(), One::one());
Repository: paritytech/substrate
File: utils/frame/try-runtime/cli/tests/create_snapshot.rs (Rust)

Code before:
			}

			// Start a node and wait for it to begin finalizing blocks
			let mut node = common::KillChildOnDrop(common::start_node());
			let ws_url = common::extract_info_from_output(node.stderr.take().unwrap()).0.ws_url;
			common::wait_n_finalized_blocks(1, &ws_url).await;

			// Try to create a snapshot.
			let mut snapshot_creation = create_snapshot(&ws_url);
			let re = Regex::new(r#".*writing snapshot of (\d+) bytes to .*"#).unwrap();
			let matched =
				common::wait_for_stream_pattern_match(snapshot_creation.stderr.take().unwrap(), re)
					.await;

			// Assert that the snapshot creation succeded.
			assert!(matched.is_ok());

			let snapshot = fs::read_dir(".")
				.expect("Failed to read the current directory")
				.filter_map(Result::ok)
				.find(|entry| {
					entry.path().is_file() &&
						entry.path().extension().map(|ext| ext == "snap").unwrap_or(false)
				});

			assert!(snapshot.is_some());
		})
		.await;
	}
}


Review comment (suggestion):
```suggestion
			assert!(snapshot_is_on_disk.is_some(), "Snapshot was not written to disk");
```

Code after:

	common::run_with_timeout(Duration::from_secs(60), async move {
		fn create_snapshot(ws_url: &str, snap_file: &PathBuf, at: Hash) -> Child {
			Command::new(cargo_bin("substrate"))
				.stdout(process::Stdio::piped())
				.stderr(process::Stdio::piped())
				.args(&["try-runtime", "--runtime=existing"])
				.args(&["create-snapshot", format!("--uri={}", ws_url).as_str()])
				.arg(snap_file)
				.args(&["--at", format!("{:?}", at).as_str()])
				.kill_on_drop(true)
				.spawn()
				.unwrap()
		}

		// Start a node and wait for it to begin finalizing blocks
		let mut node = common::KillChildOnDrop(common::start_node());
		let ws_url = common::extract_info_from_output(node.stderr.take().unwrap()).0.ws_url;
		common::wait_n_finalized_blocks(3, &ws_url).await;

		let block_number = 2;
		let block_hash = common::block_hash(block_number, &ws_url).await.unwrap();

		// Try to create a snapshot.
		let mut snapshot_creation = create_snapshot(&ws_url, &snap_file_path, block_hash);

		let re = Regex::new(r#".*writing snapshot of (\d+) bytes to .*"#).unwrap();
		let matched =
			common::wait_for_stream_pattern_match(snapshot_creation.stderr.take().unwrap(), re)
				.await;

		// Assert that the snapshot creation succeded.
		assert!(matched.is_ok(), "Failed to create snapshot");

		let snapshot_is_on_disk = Path::new(&snap_file_path).exists();
		assert!(snapshot_is_on_disk, "Snapshot was not written to disk");

		// Try and load the snapshot we have created by running `create-snapshot`.
		let snapshot_loading_result = Builder::<Block>::new()
			.mode(Mode::Offline(OfflineConfig {
				state_snapshot: SnapshotConfig { path: snap_file_path },
			}))
			.build()
			.await;

		assert!(snapshot_loading_result.is_ok(), "Snapshot couldn't be loaded");
	})
	.await;
}
Repository: paritytech/substrate
File: test-utils/cli/src/lib.rs (Rust)

Code before:
		let stderr = cmd.stderr.take().unwrap();

		let mut child = KillChildOnDrop(cmd);

		let ws_url = extract_info_from_output(stderr).0.ws_url;

		// Let it produce some blocks.
		wait_n_finalized_blocks(3, &ws_url).await;

		child.assert_still_running();

		// Stop the process
		child.stop();
	})
	.await
}

pub async fn block_hash(block_number: u64, url: &str) -> Result<Hash, String> {
	use substrate_rpc_client::{ws_client, ChainApi};

	let rpc = ws_client(url).await.unwrap();

	let result = ChainApi::<(), Hash, Header, ()>::block_hash(
		&rpc,
		Some(ListOrValue::Value(NumberOrHex::Number(block_number.saturating_sub(1)))),
	)
	.await
	.map_err(|_| "Couldn't get block hash".to_string())?;

	match result {
		ListOrValue::Value(maybe_block_hash) if maybe_block_hash.is_some() =>
			Ok(maybe_block_hash.unwrap()),
		_ => Err("Couldn't get block hash".to_string()),
	}
}

pub struct KillChildOnDrop(pub Child);

impl KillChildOnDrop {
	/// Stop the child and wait until it is finished.
	///
	/// Asserts if the exit status isn't success.
	pub fn stop(&mut self) {
		self.stop_with_signal(SIGINT);
	}

	/// Same as [`Self::stop`] but takes the `signal` that is sent to stop the child.
	pub fn stop_with_signal(&mut self, signal: Signal) {
		kill(Pid::from_raw(self.id().try_into().unwrap()), signal).unwrap();
		assert!(self.wait().unwrap().success());
	}

Review comment (bug):
This call gives me the block hash of the block that is not corresponding to the `block_number` passed as an argument. It returns the block hash that is associated with the `block_number+1` block.
(That is why I added `saturating_sub(1)` here)

Why is it so, or am I doing something wrong?

Code after:

		let mut child = KillChildOnDrop(cmd);

		let ws_url = extract_info_from_output(stderr).0.ws_url;

		// Let it produce some blocks.
		wait_n_finalized_blocks(3, &ws_url).await;

		child.assert_still_running();

		// Stop the process
		child.stop();
	})
	.await
}

pub async fn block_hash(block_number: u64, url: &str) -> Result<Hash, String> {
	use substrate_rpc_client::{ws_client, ChainApi};

	let rpc = ws_client(url).await.unwrap();

	let result = ChainApi::<(), Hash, Header, ()>::block_hash(
		&rpc,
		Some(ListOrValue::Value(NumberOrHex::Number(block_number))),
	)
	.await
	.map_err(|_| "Couldn't get block hash".to_string())?;

	match result {
		ListOrValue::Value(maybe_block_hash) if maybe_block_hash.is_some() =>
			Ok(maybe_block_hash.unwrap()),
		_ => Err("Couldn't get block hash".to_string()),
	}
}

pub struct KillChildOnDrop(pub Child);

impl KillChildOnDrop {
	/// Stop the child and wait until it is finished.
	///
	/// Asserts if the exit status isn't success.
	pub fn stop(&mut self) {
		self.stop_with_signal(SIGINT);
	}

	/// Same as [`Self::stop`] but takes the `signal` that is sent to stop the child.
	pub fn stop_with_signal(&mut self, signal: Signal) {
		kill(Pid::from_raw(self.id().try_into().unwrap()), signal).unwrap();
		assert!(self.wait().unwrap().success());
	}