// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot 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.

// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.

use super::*;
use assert_matches::assert_matches;
use polkadot_node_subsystem::{
	errors::RuntimeApiError,
	messages::{
		AllMessages, HypotheticalFrontierRequest, ParentHeadData, ProspectiveParachainsMessage,
		ProspectiveValidationDataRequest,
	},
};
use polkadot_node_subsystem_test_helpers as test_helpers;
use polkadot_primitives::{
	async_backing::{AsyncBackingParams, BackingState, Constraints, InboundHrmpLimitations},
	CommittedCandidateReceipt, HeadData, Header, PersistedValidationData, ScheduledCore,
	ValidationCodeHash,
};
use polkadot_primitives_test_helpers::make_candidate;
use std::sync::Arc;
use test_helpers::mock::new_leaf;

const ALLOWED_ANCESTRY_LEN: u32 = 3;
const ASYNC_BACKING_PARAMETERS: AsyncBackingParams =
	AsyncBackingParams { max_candidate_depth: 4, allowed_ancestry_len: ALLOWED_ANCESTRY_LEN };

const ASYNC_BACKING_DISABLED_ERROR: RuntimeApiError =
	RuntimeApiError::NotSupported { runtime_api_name: "test-runtime" };

const MAX_POV_SIZE: u32 = 1_000_000;

type VirtualOverseer = test_helpers::TestSubsystemContextHandle<ProspectiveParachainsMessage>;

fn dummy_constraints(
	min_relay_parent_number: BlockNumber,
	valid_watermarks: Vec<BlockNumber>,
	required_parent: HeadData,
	validation_code_hash: ValidationCodeHash,
) -> Constraints {
	Constraints {
		min_relay_parent_number,
		max_pov_size: MAX_POV_SIZE,
		max_code_size: 1_000_000,
		ump_remaining: 10,
		ump_remaining_bytes: 1_000,
		max_ump_num_per_candidate: 10,
		dmp_remaining_messages: vec![],
		hrmp_inbound: InboundHrmpLimitations { valid_watermarks },
		hrmp_channels_out: vec![],
		max_hrmp_num_per_candidate: 0,
		required_parent,
		validation_code_hash,
		upgrade_restriction: None,
		future_validation_code: None,
	}
}

struct TestState {
	availability_cores: Vec<CoreState>,
	validation_code_hash: ValidationCodeHash,
}

impl Default for TestState {
	fn default() -> Self {
		let chain_a = ParaId::from(1);
		let chain_b = ParaId::from(2);

		let availability_cores = vec![
			CoreState::Scheduled(ScheduledCore { para_id: chain_a, collator: None }),
			CoreState::Scheduled(ScheduledCore { para_id: chain_b, collator: None }),
		];
		let validation_code_hash = Hash::repeat_byte(42).into();

		Self { availability_cores, validation_code_hash }
	}
}

fn get_parent_hash(hash: Hash) -> Hash {
	Hash::from_low_u64_be(hash.to_low_u64_be() + 1)
}

fn test_harness<T: Future<Output = VirtualOverseer>>(
	test: impl FnOnce(VirtualOverseer) -> T,
) -> View {
	let pool = sp_core::testing::TaskExecutor::new();

	let (mut context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());

	let mut view = View::new();
	let subsystem = async move {
		if let Err(e) = run_iteration(&mut context, &mut view, &Metrics(None)).await {
			panic!("{:?}", e);
		}

		view
	};

	let test_fut = test(virtual_overseer);

	futures::pin_mut!(test_fut);
	futures::pin_mut!(subsystem);
	let (_, view) = futures::executor::block_on(future::join(
		async move {
			let mut virtual_overseer = test_fut.await;
			virtual_overseer.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await;
		},
		subsystem,
	));

	view
}

#[derive(Debug, Clone)]
struct PerParaData {
	min_relay_parent: BlockNumber,
	head_data: HeadData,
	pending_availability: Vec<CandidatePendingAvailability>,
}

impl PerParaData {
	pub fn new(min_relay_parent: BlockNumber, head_data: HeadData) -> Self {
		Self { min_relay_parent, head_data, pending_availability: Vec::new() }
	}

	pub fn new_with_pending(
		min_relay_parent: BlockNumber,
		head_data: HeadData,
		pending: Vec<CandidatePendingAvailability>,
	) -> Self {
		Self { min_relay_parent, head_data, pending_availability: pending }
	}
}

struct TestLeaf {
	number: BlockNumber,
	hash: Hash,
	para_data: Vec<(ParaId, PerParaData)>,
}

impl TestLeaf {
	pub fn para_data(&self, para_id: ParaId) -> &PerParaData {
		self.para_data
			.iter()
			.find_map(|(p_id, data)| if *p_id == para_id { Some(data) } else { None })
			.unwrap()
	}
}

async fn send_block_header(virtual_overseer: &mut VirtualOverseer, hash: Hash, number: u32) {
	let header = Header {
		parent_hash: get_parent_hash(hash),
		number,
		state_root: Hash::zero(),
		extrinsics_root: Hash::zero(),
		digest: Default::default(),
	};

	assert_matches!(
		virtual_overseer.recv().await,
		AllMessages::ChainApi(
			ChainApiMessage::BlockHeader(parent, tx)
		) if parent == hash => {
			tx.send(Ok(Some(header))).unwrap();
		}
	);
}

async fn activate_leaf(
	virtual_overseer: &mut VirtualOverseer,
	leaf: &TestLeaf,
	test_state: &TestState,
) {
	activate_leaf_with_params(virtual_overseer, leaf, test_state, ASYNC_BACKING_PARAMETERS).await;
}

async fn activate_leaf_with_params(
	virtual_overseer: &mut VirtualOverseer,
	leaf: &TestLeaf,
	test_state: &TestState,
	async_backing_params: AsyncBackingParams,
) {
	let TestLeaf { number, hash, .. } = leaf;

	let activated = new_leaf(*hash, *number);

	virtual_overseer
		.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(
			activated,
		))))
		.await;

	handle_leaf_activation(virtual_overseer, leaf, test_state, async_backing_params).await;
}

async fn handle_leaf_activation(
	virtual_overseer: &mut VirtualOverseer,
	leaf: &TestLeaf,
	test_state: &TestState,
	async_backing_params: AsyncBackingParams,
) {
	let TestLeaf { number, hash, para_data } = leaf;

	assert_matches!(
		virtual_overseer.recv().await,
		AllMessages::RuntimeApi(
			RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx))
		) if parent == *hash => {
			tx.send(Ok(async_backing_params)).unwrap();
		}
	);

	assert_matches!(
		virtual_overseer.recv().await,
		AllMessages::RuntimeApi(
			RuntimeApiMessage::Request(parent, RuntimeApiRequest::AvailabilityCores(tx))
		) if parent == *hash => {
			tx.send(Ok(test_state.availability_cores.clone())).unwrap();
		}
	);

	send_block_header(virtual_overseer, *hash, *number).await;

	// Check that subsystem job issues a request for ancestors.
	let min_min = para_data.iter().map(|(_, data)| data.min_relay_parent).min().unwrap_or(*number);
	let ancestry_len = number - min_min;
	let ancestry_hashes: Vec<Hash> =
		std::iter::successors(Some(*hash), |h| Some(get_parent_hash(*h)))
			.skip(1)
			.take(ancestry_len as usize)
			.collect();
	let ancestry_numbers = (min_min..*number).rev();
	let ancestry_iter = ancestry_hashes.clone().into_iter().zip(ancestry_numbers).peekable();
	if ancestry_len > 0 {
		assert_matches!(
			virtual_overseer.recv().await,
			AllMessages::ChainApi(
				ChainApiMessage::Ancestors{hash: block_hash, k, response_channel: tx}
			) if block_hash == *hash && k == ALLOWED_ANCESTRY_LEN as usize => {
				tx.send(Ok(ancestry_hashes.clone())).unwrap();
			}
		);

		assert_matches!(
			virtual_overseer.recv().await,
			AllMessages::RuntimeApi(
				RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
			) if parent == *hash => {
				tx.send(Ok(1)).unwrap();
			}
		);
	}

	for (hash, number) in ancestry_iter {
		send_block_header(virtual_overseer, hash, number).await;
		assert_matches!(
			virtual_overseer.recv().await,
			AllMessages::RuntimeApi(
				RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
			) if parent == hash => {
				tx.send(Ok(1)).unwrap();
			}
		);
	}

	for _ in 0..test_state.availability_cores.len() {
		let message = virtual_overseer.recv().await;
		// Get the para we are working with since the order is not deterministic.
		let para_id = match message {
			AllMessages::RuntimeApi(RuntimeApiMessage::Request(
				_,
				RuntimeApiRequest::ParaBackingState(p_id, _),
			)) => p_id,
			_ => panic!("received unexpected message {:?}", message),
		};

		let PerParaData { min_relay_parent, head_data, pending_availability } =
			leaf.para_data(para_id);
		let constraints = dummy_constraints(
			*min_relay_parent,
			vec![*number],
			head_data.clone(),
			test_state.validation_code_hash,
		);
		let backing_state =
			BackingState { constraints, pending_availability: pending_availability.clone() };

		assert_matches!(
			message,
			AllMessages::RuntimeApi(
				RuntimeApiMessage::Request(parent, RuntimeApiRequest::ParaBackingState(p_id, tx))
			) if parent == *hash && p_id == para_id => {
				tx.send(Ok(Some(backing_state))).unwrap();
			}
		);

		for pending in pending_availability {
			send_block_header(
				virtual_overseer,
				pending.descriptor.relay_parent,
				pending.relay_parent_number,
			)
			.await;
		}
	}

	// Get minimum relay parents.
	let (tx, rx) = oneshot::channel();
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::GetMinimumRelayParents(*hash, tx),
		})
		.await;
	let mut resp = rx.await.unwrap();
	resp.sort();
	let mrp_response: Vec<(ParaId, BlockNumber)> = para_data
		.iter()
		.map(|(para_id, data)| (*para_id, data.min_relay_parent))
		.collect();
	assert_eq!(resp, mrp_response);
}

async fn deactivate_leaf(virtual_overseer: &mut VirtualOverseer, hash: Hash) {
	virtual_overseer
		.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::stop_work(
			hash,
		))))
		.await;
}

async fn introduce_candidate(
	virtual_overseer: &mut VirtualOverseer,
	candidate: CommittedCandidateReceipt,
	pvd: PersistedValidationData,
) {
	let req = IntroduceCandidateRequest {
		candidate_para: candidate.descriptor().para_id,
		candidate_receipt: candidate,
		persisted_validation_data: pvd,
	};
	let (tx, _) = oneshot::channel();
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::IntroduceCandidate(req, tx),
		})
		.await;
}

async fn second_candidate(
	virtual_overseer: &mut VirtualOverseer,
	candidate: CommittedCandidateReceipt,
) {
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::CandidateSeconded(
				candidate.descriptor.para_id,
				candidate.hash(),
			),
		})
		.await;
}

async fn back_candidate(
	virtual_overseer: &mut VirtualOverseer,
	candidate: &CommittedCandidateReceipt,
	candidate_hash: CandidateHash,
) {
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::CandidateBacked(
				candidate.descriptor.para_id,
				candidate_hash,
			),
		})
		.await;
}

async fn get_membership(
	virtual_overseer: &mut VirtualOverseer,
	para_id: ParaId,
	candidate_hash: CandidateHash,
	expected_membership_response: Vec<(Hash, Vec<usize>)>,
) {
	let (tx, rx) = oneshot::channel();
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::GetTreeMembership(para_id, candidate_hash, tx),
		})
		.await;
	let resp = rx.await.unwrap();
	assert_eq!(resp, expected_membership_response);
}

async fn get_backable_candidates(
	virtual_overseer: &mut VirtualOverseer,
	leaf: &TestLeaf,
	para_id: ParaId,
	ancestors: Ancestors,
	count: u32,
	expected_result: Vec<(CandidateHash, Hash)>,
) {
	let (tx, rx) = oneshot::channel();
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::GetBackableCandidates(
				leaf.hash, para_id, count, ancestors, tx,
			),
		})
		.await;
	let resp = rx.await.unwrap();
	assert_eq!(resp.len(), expected_result.len());
	assert_eq!(resp, expected_result);
}

async fn get_hypothetical_frontier(
	virtual_overseer: &mut VirtualOverseer,
	candidate_hash: CandidateHash,
	receipt: CommittedCandidateReceipt,
	persisted_validation_data: PersistedValidationData,
	fragment_tree_relay_parent: Hash,
	backed_in_path_only: bool,
	expected_depths: Vec<usize>,
) {
	let hypothetical_candidate = HypotheticalCandidate::Complete {
		candidate_hash,
		receipt: Arc::new(receipt),
		persisted_validation_data,
	};
	let request = HypotheticalFrontierRequest {
		candidates: vec![hypothetical_candidate.clone()],
		fragment_tree_relay_parent: Some(fragment_tree_relay_parent),
		backed_in_path_only,
	};
	let (tx, rx) = oneshot::channel();
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::GetHypotheticalFrontier(request, tx),
		})
		.await;
	let resp = rx.await.unwrap();
	let expected_frontier = if expected_depths.is_empty() {
		vec![(hypothetical_candidate, vec![])]
	} else {
		vec![(hypothetical_candidate, vec![(fragment_tree_relay_parent, expected_depths)])]
	};
	assert_eq!(resp, expected_frontier);
}

async fn get_pvd(
	virtual_overseer: &mut VirtualOverseer,
	para_id: ParaId,
	candidate_relay_parent: Hash,
	parent_head_data: HeadData,
	expected_pvd: Option<PersistedValidationData>,
) {
	let request = ProspectiveValidationDataRequest {
		para_id,
		candidate_relay_parent,
		parent_head_data: ParentHeadData::OnlyHash(parent_head_data.hash()),
	};
	let (tx, rx) = oneshot::channel();
	virtual_overseer
		.send(overseer::FromOrchestra::Communication {
			msg: ProspectiveParachainsMessage::GetProspectiveValidationData(request, tx),
		})
		.await;
	let resp = rx.await.unwrap();
	assert_eq!(resp, expected_pvd);
}

#[test]
fn should_do_no_work_if_async_backing_disabled_for_leaf() {
	async fn activate_leaf_async_backing_disabled(virtual_overseer: &mut VirtualOverseer) {
		let hash = Hash::from_low_u64_be(130);

		// Start work on some new parent.
		virtual_overseer
			.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(
				ActiveLeavesUpdate::start_work(new_leaf(hash, 1)),
			)))
			.await;

		assert_matches!(
			virtual_overseer.recv().await,
			AllMessages::RuntimeApi(
				RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx))
			) if parent == hash => {
				tx.send(Err(ASYNC_BACKING_DISABLED_ERROR)).unwrap();
			}
		);
	}

	let view = test_harness(|mut virtual_overseer| async move {
		activate_leaf_async_backing_disabled(&mut virtual_overseer).await;

		virtual_overseer
	});

	assert!(view.active_leaves.is_empty());
	assert!(view.candidate_storage.is_empty());
}

// Send some candidates and make sure all are found:
// - Two for the same leaf A
// - One for leaf B on parachain 1
// - One for leaf C on parachain 2
#[test]
fn send_candidates_and_check_if_found() {
	let test_state = TestState::default();
	let view = test_harness(|mut virtual_overseer| async move {
		// Leaf A
		let leaf_a = TestLeaf {
			number: 100,
			hash: Hash::from_low_u64_be(130),
			para_data: vec![
				(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
				(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
			],
		};
		// Leaf B
		let leaf_b = TestLeaf {
			number: 101,
			hash: Hash::from_low_u64_be(131),
			para_data: vec![
				(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
				(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
			],
		};
		// Leaf C
		let leaf_c = TestLeaf {
			number: 102,
			hash: Hash::from_low_u64_be(132),
			para_data: vec![
				(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
				(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
			],
		};

		// Activate leaves.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
		activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
		activate_leaf(&mut virtual_overseer, &leaf_c, &test_state).await;

		// Candidate A1
		let (candidate_a1, pvd_a1) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1, 2, 3]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a1 = candidate_a1.hash();
		let response_a1 = vec![(leaf_a.hash, vec![0])];

		// Candidate A2
		let (candidate_a2, pvd_a2) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			2.into(),
			HeadData(vec![2, 3, 4]),
			HeadData(vec![2]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a2 = candidate_a2.hash();
		let response_a2 = vec![(leaf_a.hash, vec![0])];

		// Candidate B
		let (candidate_b, pvd_b) = make_candidate(
			leaf_b.hash,
			leaf_b.number,
			1.into(),
			HeadData(vec![3, 4, 5]),
			HeadData(vec![3]),
			test_state.validation_code_hash,
		);
		let candidate_hash_b = candidate_b.hash();
		let response_b = vec![(leaf_b.hash, vec![0])];

		// Candidate C
		let (candidate_c, pvd_c) = make_candidate(
			leaf_c.hash,
			leaf_c.number,
			2.into(),
			HeadData(vec![6, 7, 8]),
			HeadData(vec![4]),
			test_state.validation_code_hash,
		);
		let candidate_hash_c = candidate_c.hash();
		let response_c = vec![(leaf_c.hash, vec![0])];

		// Introduce candidates.
		introduce_candidate(&mut virtual_overseer, candidate_a1, pvd_a1).await;
		introduce_candidate(&mut virtual_overseer, candidate_a2, pvd_a2).await;
		introduce_candidate(&mut virtual_overseer, candidate_b, pvd_b).await;
		introduce_candidate(&mut virtual_overseer, candidate_c, pvd_c).await;

		// Check candidate tree membership.
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, response_a1).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, response_a2).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, response_b).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, response_c).await;

		// The candidates should not be found on other parachains.
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a1, vec![]).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a2, vec![]).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_b, vec![]).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_c, vec![]).await;

		virtual_overseer
	});

	assert_eq!(view.active_leaves.len(), 3);
	assert_eq!(view.candidate_storage.len(), 2);
	// Two parents and two candidates per para.
	assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2));
	assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (2, 2));
}

// Send some candidates, check if the candidate won't be found once its relay parent leaves the
// view.
#[test]
fn check_candidate_parent_leaving_view() {
	let test_state = TestState::default();
	let view = test_harness(|mut virtual_overseer| async move {
		// Leaf A
		let leaf_a = TestLeaf {
			number: 100,
			hash: Hash::from_low_u64_be(130),
			para_data: vec![
				(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
				(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
			],
		};
		// Leaf B
		let leaf_b = TestLeaf {
			number: 101,
			hash: Hash::from_low_u64_be(131),
			para_data: vec![
				(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
				(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
			],
		};
		// Leaf C
		let leaf_c = TestLeaf {
			number: 102,
			hash: Hash::from_low_u64_be(132),
			para_data: vec![
				(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
				(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
			],
		};

		// Activate leaves.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
		activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
		activate_leaf(&mut virtual_overseer, &leaf_c, &test_state).await;

		// Candidate A1
		let (candidate_a1, pvd_a1) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1, 2, 3]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a1 = candidate_a1.hash();

		// Candidate A2
		let (candidate_a2, pvd_a2) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			2.into(),
			HeadData(vec![2, 3, 4]),
			HeadData(vec![2]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a2 = candidate_a2.hash();

		// Candidate B
		let (candidate_b, pvd_b) = make_candidate(
			leaf_b.hash,
			leaf_b.number,
			1.into(),
			HeadData(vec![3, 4, 5]),
			HeadData(vec![3]),
			test_state.validation_code_hash,
		);
		let candidate_hash_b = candidate_b.hash();
		let response_b = vec![(leaf_b.hash, vec![0])];

		// Candidate C
		let (candidate_c, pvd_c) = make_candidate(
			leaf_c.hash,
			leaf_c.number,
			2.into(),
			HeadData(vec![6, 7, 8]),
			HeadData(vec![4]),
			test_state.validation_code_hash,
		);
		let candidate_hash_c = candidate_c.hash();
		let response_c = vec![(leaf_c.hash, vec![0])];

		// Introduce candidates.
		introduce_candidate(&mut virtual_overseer, candidate_a1, pvd_a1).await;
		introduce_candidate(&mut virtual_overseer, candidate_a2, pvd_a2).await;
		introduce_candidate(&mut virtual_overseer, candidate_b, pvd_b).await;
		introduce_candidate(&mut virtual_overseer, candidate_c, pvd_c).await;

		// Deactivate leaf A.
		deactivate_leaf(&mut virtual_overseer, leaf_a.hash).await;

		// Candidates A1 and A2 should be gone. Candidates B and C should remain.
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, vec![]).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, vec![]).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, response_b).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, response_c.clone()).await;

		// Deactivate leaf B.
		deactivate_leaf(&mut virtual_overseer, leaf_b.hash).await;

		// Candidate B should be gone, C should remain.
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, vec![]).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, vec![]).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, vec![]).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, response_c).await;

		// Deactivate leaf C.
		deactivate_leaf(&mut virtual_overseer, leaf_c.hash).await;

		// Candidate C should be gone.
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, vec![]).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, vec![]).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, vec![]).await;
		get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, vec![]).await;

		virtual_overseer
	});

	assert_eq!(view.active_leaves.len(), 0);
	assert_eq!(view.candidate_storage.len(), 0);
}

// Introduce a candidate to multiple forks, see how the membership is returned.
#[test]
fn check_candidate_on_multiple_forks() {
	let test_state = TestState::default();
	let view = test_harness(|mut virtual_overseer| async move {
		// Leaf A
		let leaf_a = TestLeaf {
			number: 100,
			hash: Hash::from_low_u64_be(130),
			para_data: vec![
				(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
				(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
			],
		};
		// Leaf B
		let leaf_b = TestLeaf {
			number: 101,
			hash: Hash::from_low_u64_be(131),
			para_data: vec![
				(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
				(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
			],
		};
		// Leaf C
		let leaf_c = TestLeaf {
			number: 102,
			hash: Hash::from_low_u64_be(132),
			para_data: vec![
				(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
				(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
			],
		};

		// Activate leaves.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
		activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
		activate_leaf(&mut virtual_overseer, &leaf_c, &test_state).await;

		// Candidate on leaf A.
		let (candidate_a, pvd_a) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1, 2, 3]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a = candidate_a.hash();
		let response_a = vec![(leaf_a.hash, vec![0])];

		// Candidate on leaf B.
		let (candidate_b, pvd_b) = make_candidate(
			leaf_b.hash,
			leaf_b.number,
			1.into(),
			HeadData(vec![3, 4, 5]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_b = candidate_b.hash();
		let response_b = vec![(leaf_b.hash, vec![0])];

		// Candidate on leaf C.
		let (candidate_c, pvd_c) = make_candidate(
			leaf_c.hash,
			leaf_c.number,
			1.into(),
			HeadData(vec![5, 6, 7]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_c = candidate_c.hash();
		let response_c = vec![(leaf_c.hash, vec![0])];

		// Introduce candidates on all three leaves.
		introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
		introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await;
		introduce_candidate(&mut virtual_overseer, candidate_c.clone(), pvd_c).await;

		// Check candidate tree membership.
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a, response_a).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, response_b).await;
		get_membership(&mut virtual_overseer, 1.into(), candidate_hash_c, response_c).await;

		virtual_overseer
	});

	assert_eq!(view.active_leaves.len(), 3);
	assert_eq!(view.candidate_storage.len(), 2);
	// Three parents and three candidates on para 1.
	assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (3, 3));
	assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0));
}

// Backs some candidates and tests `GetBackableCandidates` when requesting a single candidate.
#[test]
fn check_backable_query_single_candidate() {
	let test_state = TestState::default();
	let view = test_harness(|mut virtual_overseer| async move {
		// Leaf A
		let leaf_a = TestLeaf {
			number: 100,
			hash: Hash::from_low_u64_be(130),
			para_data: vec![
				(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
				(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
			],
		};

		// Activate leaves.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;

		// Candidate A
		let (candidate_a, pvd_a) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1, 2, 3]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a = candidate_a.hash();

		// Candidate B
		let (mut candidate_b, pvd_b) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1]),
			HeadData(vec![2]),
			test_state.validation_code_hash,
		);
		// Set a field to make this candidate unique.
		candidate_b.descriptor.para_head = Hash::from_low_u64_le(1000);
		let candidate_hash_b = candidate_b.hash();

		// Introduce candidates.
		introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
		introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await;

		// Should not get any backable candidates.
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			1.into(),
			vec![candidate_hash_a].into_iter().collect(),
			1,
			vec![],
		)
		.await;
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			1.into(),
			vec![candidate_hash_a].into_iter().collect(),
			0,
			vec![],
		)
		.await;
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			1.into(),
			Ancestors::new(),
			0,
			vec![],
		)
		.await;

		// Second candidates.
		second_candidate(&mut virtual_overseer, candidate_a.clone()).await;
		second_candidate(&mut virtual_overseer, candidate_b.clone()).await;

		// Should not get any backable candidates.
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			1.into(),
			vec![candidate_hash_a].into_iter().collect(),
			1,
			vec![],
		)
		.await;

		// Back candidates.
		back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await;
		back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await;

		// Should not get any backable candidates for the other para.
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			2.into(),
			Ancestors::new(),
			1,
			vec![],
		)
		.await;
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			2.into(),
			vec![candidate_hash_a].into_iter().collect(),
			1,
			vec![],
		)
		.await;

		// Get backable candidate.
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			1.into(),
			Ancestors::new(),
			1,
			vec![(candidate_hash_a, leaf_a.hash)],
		)
		.await;
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			1.into(),
			vec![candidate_hash_a].into_iter().collect(),
			1,
			vec![(candidate_hash_b, leaf_a.hash)],
		)
		.await;

		// Wrong path
		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			1.into(),
			vec![candidate_hash_b].into_iter().collect(),
			1,
			vec![(candidate_hash_a, leaf_a.hash)],
		)
		.await;

		virtual_overseer
	});

	assert_eq!(view.active_leaves.len(), 1);
	assert_eq!(view.candidate_storage.len(), 2);
	// Two parents and two candidates on para 1.
	assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2));
	assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0));
}

// Backs some candidates and tests `GetBackableCandidates` when requesting a multiple candidates.
#[test]
fn check_backable_query_multiple_candidates() {
	macro_rules! make_and_back_candidate {
		($test_state:ident, $virtual_overseer:ident, $leaf:ident, $parent:expr, $index:expr) => {{
			let (mut candidate, pvd) = make_candidate(
				$leaf.hash,
				$leaf.number,
				1.into(),
				$parent.commitments.head_data.clone(),
				HeadData(vec![$index]),
				$test_state.validation_code_hash,
			);
			// Set a field to make this candidate unique.
			candidate.descriptor.para_head = Hash::from_low_u64_le($index);
			let candidate_hash = candidate.hash();
			introduce_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await;
			second_candidate(&mut $virtual_overseer, candidate.clone()).await;
			back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await;

			(candidate, candidate_hash)
		}};
	}

	// Parachain 1 looks like this:
	//          +---A----+
	//          |        |
	//     +----B---+    C
	//     |    |   |    |
	//     D    E   F    H
	//              |    |
	//              G    I
	//                   |
	//                   J
	{
		let test_state = TestState::default();
		let view = test_harness(|mut virtual_overseer| async move {
			// Leaf A
			let leaf_a = TestLeaf {
				number: 100,
				hash: Hash::from_low_u64_be(130),
				para_data: vec![
					(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
					(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
				],
			};

			// Activate leaves.
			activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;

			// Candidate A
			let (candidate_a, pvd_a) = make_candidate(
				leaf_a.hash,
				leaf_a.number,
				1.into(),
				HeadData(vec![1, 2, 3]),
				HeadData(vec![1]),
				test_state.validation_code_hash,
			);
			let candidate_hash_a = candidate_a.hash();
			introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
			second_candidate(&mut virtual_overseer, candidate_a.clone()).await;
			back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await;

			let (candidate_b, candidate_hash_b) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_a, 2);
			let (candidate_c, candidate_hash_c) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_a, 3);
			let (_candidate_d, candidate_hash_d) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 4);
			let (_candidate_e, candidate_hash_e) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 5);
			let (candidate_f, candidate_hash_f) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 6);
			let (_candidate_g, candidate_hash_g) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_f, 7);
			let (candidate_h, candidate_hash_h) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_c, 8);
			let (candidate_i, candidate_hash_i) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_h, 9);
			let (_candidate_j, candidate_hash_j) =
				make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_i, 10);

			// Should not get any backable candidates for the other para.
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				2.into(),
				Ancestors::new(),
				1,
				vec![],
			)
			.await;
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				2.into(),
				Ancestors::new(),
				5,
				vec![],
			)
			.await;
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				2.into(),
				vec![candidate_hash_a].into_iter().collect(),
				1,
				vec![],
			)
			.await;

			// Test various scenarios with various counts.

			// empty required_path
			{
				get_backable_candidates(
					&mut virtual_overseer,
					&leaf_a,
					1.into(),
					Ancestors::new(),
					1,
					vec![(candidate_hash_a, leaf_a.hash)],
				)
				.await;
				get_backable_candidates(
					&mut virtual_overseer,
					&leaf_a,
					1.into(),
					Ancestors::new(),
					4,
					vec![
						(candidate_hash_a, leaf_a.hash),
						(candidate_hash_b, leaf_a.hash),
						(candidate_hash_f, leaf_a.hash),
						(candidate_hash_g, leaf_a.hash),
					],
				)
				.await;
			}

			// required path of 1
			{
				get_backable_candidates(
					&mut virtual_overseer,
					&leaf_a,
					1.into(),
					vec![candidate_hash_a].into_iter().collect(),
					1,
					vec![(candidate_hash_b, leaf_a.hash)],
				)
				.await;
				get_backable_candidates(
					&mut virtual_overseer,
					&leaf_a,
					1.into(),
					vec![candidate_hash_a].into_iter().collect(),
					3,
					vec![
						(candidate_hash_b, leaf_a.hash),
						(candidate_hash_f, leaf_a.hash),
						(candidate_hash_g, leaf_a.hash),
					],
				)
				.await;

				// If the requested count exceeds the largest chain, return the longest
				// chain we can get.
				for count in 5..10 {
					get_backable_candidates(
						&mut virtual_overseer,
						&leaf_a,
						1.into(),
						vec![candidate_hash_a].into_iter().collect(),
						count,
						vec![
							(candidate_hash_c, leaf_a.hash),
							(candidate_hash_h, leaf_a.hash),
							(candidate_hash_i, leaf_a.hash),
							(candidate_hash_j, leaf_a.hash),
						],
					)
					.await;
				}
			}

			// required path of 2 and higher
			{
				get_backable_candidates(
					&mut virtual_overseer,
					&leaf_a,
					1.into(),
					vec![candidate_hash_a, candidate_hash_i, candidate_hash_h, candidate_hash_c]
						.into_iter()
						.collect(),
					1,
					vec![(candidate_hash_j, leaf_a.hash)],
				)
				.await;

				get_backable_candidates(
					&mut virtual_overseer,
					&leaf_a,
					1.into(),
					vec![candidate_hash_a, candidate_hash_b].into_iter().collect(),
					1,
					vec![(candidate_hash_d, leaf_a.hash)],
				)
				.await;

				// If the requested count exceeds the largest chain, return the longest
				// chain we can get.
				for count in 4..10 {
					get_backable_candidates(
						&mut virtual_overseer,
						&leaf_a,
						1.into(),
						vec![candidate_hash_a, candidate_hash_c].into_iter().collect(),
						count,
						vec![
							(candidate_hash_h, leaf_a.hash),
							(candidate_hash_i, leaf_a.hash),
							(candidate_hash_j, leaf_a.hash),
						],
					)
					.await;
				}
			}

			// No more candidates in any chain.
			{
				for count in 1..4 {
					get_backable_candidates(
						&mut virtual_overseer,
						&leaf_a,
						1.into(),
						vec![candidate_hash_a, candidate_hash_b, candidate_hash_e]
							.into_iter()
							.collect(),
						count,
						vec![],
					)
					.await;

					get_backable_candidates(
						&mut virtual_overseer,
						&leaf_a,
						1.into(),
						vec![
							candidate_hash_a,
							candidate_hash_c,
							candidate_hash_h,
							candidate_hash_i,
							candidate_hash_j,
						]
						.into_iter()
						.collect(),
						count,
						vec![],
					)
					.await;
				}
			}

			// Wrong paths.
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_b].into_iter().collect(),
				1,
				vec![(candidate_hash_a, leaf_a.hash)],
			)
			.await;
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_b, candidate_hash_f].into_iter().collect(),
				3,
				vec![
					(candidate_hash_a, leaf_a.hash),
					(candidate_hash_b, leaf_a.hash),
					(candidate_hash_d, leaf_a.hash),
				],
			)
			.await;
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_a, candidate_hash_h].into_iter().collect(),
				4,
				vec![
					(candidate_hash_c, leaf_a.hash),
					(candidate_hash_h, leaf_a.hash),
					(candidate_hash_i, leaf_a.hash),
					(candidate_hash_j, leaf_a.hash),
				],
			)
			.await;
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_e, candidate_hash_h].into_iter().collect(),
				2,
				vec![(candidate_hash_a, leaf_a.hash), (candidate_hash_b, leaf_a.hash)],
			)
			.await;

			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_a, candidate_hash_c, candidate_hash_d].into_iter().collect(),
				2,
				vec![(candidate_hash_h, leaf_a.hash), (candidate_hash_i, leaf_a.hash)],
			)
			.await;

			// Parachain fork.
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_a, candidate_hash_b, candidate_hash_c].into_iter().collect(),
				1,
				vec![],
			)
			.await;

			// Non-existent candidate.
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_a, CandidateHash(Hash::from_low_u64_be(100))]
					.into_iter()
					.collect(),
				2,
				vec![(candidate_hash_b, leaf_a.hash), (candidate_hash_d, leaf_a.hash)],
			)
			.await;

			// Requested count is zero.
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				Ancestors::new(),
				0,
				vec![],
			)
			.await;
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_a].into_iter().collect(),
				0,
				vec![],
			)
			.await;
			get_backable_candidates(
				&mut virtual_overseer,
				&leaf_a,
				1.into(),
				vec![candidate_hash_a, candidate_hash_b].into_iter().collect(),
				0,
				vec![],
			)
			.await;

			virtual_overseer
		});

		assert_eq!(view.active_leaves.len(), 1);
		assert_eq!(view.candidate_storage.len(), 2);
		// 10 candidates and 7 parents on para 1.
		assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (7, 10));
		assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0));
	}
}

// Test depth query.
#[test]
fn check_hypothetical_frontier_query() {
	let test_state = TestState::default();
	let view = test_harness(|mut virtual_overseer| async move {
		// Leaf A
		let leaf_a = TestLeaf {
			number: 100,
			hash: Hash::from_low_u64_be(130),
			para_data: vec![
				(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
				(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
			],
		};

		// Activate leaves.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;

		// Candidate A.
		let (candidate_a, pvd_a) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1, 2, 3]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a = candidate_a.hash();

		// Candidate B.
		let (candidate_b, pvd_b) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1]),
			HeadData(vec![2]),
			test_state.validation_code_hash,
		);
		let candidate_hash_b = candidate_b.hash();

		// Candidate C.
		let (candidate_c, pvd_c) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![2]),
			HeadData(vec![3]),
			test_state.validation_code_hash,
		);
		let candidate_hash_c = candidate_c.hash();

		// Get hypothetical frontier of candidate A before adding it.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_a,
			candidate_a.clone(),
			pvd_a.clone(),
			leaf_a.hash,
			false,
			vec![0],
		)
		.await;
		// Should work with `backed_in_path_only: true`, too.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_a,
			candidate_a.clone(),
			pvd_a.clone(),
			leaf_a.hash,
			true,
			vec![0],
		)
		.await;

		// Add candidate A.
		introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()).await;

		// Get frontier of candidate A after adding it.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_a,
			candidate_a.clone(),
			pvd_a.clone(),
			leaf_a.hash,
			false,
			vec![0],
		)
		.await;

		// Get hypothetical frontier of candidate B before adding it.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_b,
			candidate_b.clone(),
			pvd_b.clone(),
			leaf_a.hash,
			false,
			vec![1],
		)
		.await;

		// Add candidate B.
		introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()).await;

		// Get frontier of candidate B after adding it.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_b,
			candidate_b,
			pvd_b.clone(),
			leaf_a.hash,
			false,
			vec![1],
		)
		.await;

		// Get hypothetical frontier of candidate C before adding it.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_c,
			candidate_c.clone(),
			pvd_c.clone(),
			leaf_a.hash,
			false,
			vec![2],
		)
		.await;
		// Should be empty with `backed_in_path_only` because we haven't backed anything.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_c,
			candidate_c.clone(),
			pvd_c.clone(),
			leaf_a.hash,
			true,
			vec![],
		)
		.await;

		// Add candidate C.
		introduce_candidate(&mut virtual_overseer, candidate_c.clone(), pvd_c.clone()).await;

		// Get frontier of candidate C after adding it.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_c,
			candidate_c.clone(),
			pvd_c.clone(),
			leaf_a.hash,
			false,
			vec![2],
		)
		.await;
		// Should be empty with `backed_in_path_only` because we haven't backed anything.
		get_hypothetical_frontier(
			&mut virtual_overseer,
			candidate_hash_c,
			candidate_c.clone(),
			pvd_c.clone(),
			leaf_a.hash,
			true,
			vec![],
		)
		.await;

		virtual_overseer
	});

	assert_eq!(view.active_leaves.len(), 1);
	assert_eq!(view.candidate_storage.len(), 2);
}

#[test]
fn check_pvd_query() {
	let test_state = TestState::default();
	let view = test_harness(|mut virtual_overseer| async move {
		// Leaf A
		let leaf_a = TestLeaf {
			number: 100,
			hash: Hash::from_low_u64_be(130),
			para_data: vec![
				(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
				(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
			],
		};

		// Activate leaves.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;

		// Candidate A.
		let (candidate_a, pvd_a) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1, 2, 3]),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);

		// Candidate B.
		let (candidate_b, pvd_b) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![1]),
			HeadData(vec![2]),
			test_state.validation_code_hash,
		);

		// Candidate C.
		let (candidate_c, pvd_c) = make_candidate(
			leaf_a.hash,
			leaf_a.number,
			1.into(),
			HeadData(vec![2]),
			HeadData(vec![3]),
			test_state.validation_code_hash,
		);

		// Get pvd of candidate A before adding it.
		get_pvd(
			&mut virtual_overseer,
			1.into(),
			leaf_a.hash,
			HeadData(vec![1, 2, 3]),
			Some(pvd_a.clone()),
		)
		.await;

		// Add candidate A.
		introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()).await;
		back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await;

		// Get pvd of candidate A after adding it.
		get_pvd(
			&mut virtual_overseer,
			1.into(),
			leaf_a.hash,
			HeadData(vec![1, 2, 3]),
			Some(pvd_a.clone()),
		)
		.await;

		// Get pvd of candidate B before adding it.
		get_pvd(
			&mut virtual_overseer,
			1.into(),
			leaf_a.hash,
			HeadData(vec![1]),
			Some(pvd_b.clone()),
		)
		.await;

		// Add candidate B.
		introduce_candidate(&mut virtual_overseer, candidate_b, pvd_b.clone()).await;

		// Get pvd of candidate B after adding it.
		get_pvd(
			&mut virtual_overseer,
			1.into(),
			leaf_a.hash,
			HeadData(vec![1]),
			Some(pvd_b.clone()),
		)
		.await;

		// Get pvd of candidate C before adding it.
		get_pvd(
			&mut virtual_overseer,
			1.into(),
			leaf_a.hash,
			HeadData(vec![2]),
			Some(pvd_c.clone()),
		)
		.await;

		// Add candidate C.
		introduce_candidate(&mut virtual_overseer, candidate_c, pvd_c.clone()).await;

		// Get pvd of candidate C after adding it.
		get_pvd(
			&mut virtual_overseer,
			1.into(),
			leaf_a.hash,
			HeadData(vec![2]),
			Some(pvd_c.clone()),
		)
		.await;

		virtual_overseer
	});

	assert_eq!(view.active_leaves.len(), 1);
	assert_eq!(view.candidate_storage.len(), 2);
}

// Test simultaneously activating and deactivating leaves, and simultaneously deactivating
// multiple leaves.
#[test]
fn correctly_updates_leaves() {
	let test_state = TestState::default();
	let view = test_harness(|mut virtual_overseer| async move {
		// Leaf A
		let leaf_a = TestLeaf {
			number: 100,
			hash: Hash::from_low_u64_be(130),
			para_data: vec![
				(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
				(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
			],
		};
		// Leaf B
		let leaf_b = TestLeaf {
			number: 101,
			hash: Hash::from_low_u64_be(131),
			para_data: vec![
				(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
				(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
			],
		};
		// Leaf C
		let leaf_c = TestLeaf {
			number: 102,
			hash: Hash::from_low_u64_be(132),
			para_data: vec![
				(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
				(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
			],
		};

		// Activate leaves.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
		activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;

		// Try activating a duplicate leaf.
		activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;

		// Pass in an empty update.
		let update = ActiveLeavesUpdate::default();
		virtual_overseer
			.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
			.await;

		// Activate a leaf and remove one at the same time.
		let activated = new_leaf(leaf_c.hash, leaf_c.number);
		let update = ActiveLeavesUpdate {
			activated: Some(activated),
			deactivated: [leaf_b.hash][..].into(),
		};
		virtual_overseer
			.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
			.await;
		handle_leaf_activation(
			&mut virtual_overseer,
			&leaf_c,
			&test_state,
			ASYNC_BACKING_PARAMETERS,
		)
		.await;

		// Remove all remaining leaves.
		let update = ActiveLeavesUpdate {
			deactivated: [leaf_a.hash, leaf_c.hash][..].into(),
			..Default::default()
		};
		virtual_overseer
			.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
			.await;

		// Activate and deactivate the same leaf.
		let activated = new_leaf(leaf_a.hash, leaf_a.number);
		let update = ActiveLeavesUpdate {
			activated: Some(activated),
			deactivated: [leaf_a.hash][..].into(),
		};
		virtual_overseer
			.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
			.await;
		handle_leaf_activation(
			&mut virtual_overseer,
			&leaf_a,
			&test_state,
			ASYNC_BACKING_PARAMETERS,
		)
		.await;

		// Remove the leaf again. Send some unnecessary hashes.
		let update = ActiveLeavesUpdate {
			deactivated: [leaf_a.hash, leaf_b.hash, leaf_c.hash][..].into(),
			..Default::default()
		};
		virtual_overseer
			.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
			.await;

		virtual_overseer
	});

	assert_eq!(view.active_leaves.len(), 0);
	assert_eq!(view.candidate_storage.len(), 0);
}

#[test]
fn persists_pending_availability_candidate() {
	let mut test_state = TestState::default();
	let para_id = ParaId::from(1);
	test_state.availability_cores = test_state
		.availability_cores
		.into_iter()
		.filter(|core| match core {
			CoreState::Scheduled(scheduled_core) => scheduled_core.para_id == para_id,
			_ => false,
		})
		.collect();
	assert_eq!(test_state.availability_cores.len(), 1);

	test_harness(|mut virtual_overseer| async move {
		let para_head = HeadData(vec![1, 2, 3]);

		// Min allowed relay parent for leaf `a` which goes out of scope in the test.
		let candidate_relay_parent = Hash::from_low_u64_be(5);
		let candidate_relay_parent_number = 97;

		let leaf_a = TestLeaf {
			number: candidate_relay_parent_number + ALLOWED_ANCESTRY_LEN,
			hash: Hash::from_low_u64_be(2),
			para_data: vec![(
				para_id,
				PerParaData::new(candidate_relay_parent_number, para_head.clone()),
			)],
		};

		let leaf_b_hash = Hash::from_low_u64_be(1);
		let leaf_b_number = leaf_a.number + 1;

		// Activate leaf.
		activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;

		// Candidate A
		let (candidate_a, pvd_a) = make_candidate(
			candidate_relay_parent,
			candidate_relay_parent_number,
			para_id,
			para_head.clone(),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a = candidate_a.hash();

		// Candidate B, built on top of the candidate which is out of scope but pending
		// availability.
		let (candidate_b, pvd_b) = make_candidate(
			leaf_b_hash,
			leaf_b_number,
			para_id,
			HeadData(vec![1]),
			HeadData(vec![2]),
			test_state.validation_code_hash,
		);
		let candidate_hash_b = candidate_b.hash();

		introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
		second_candidate(&mut virtual_overseer, candidate_a.clone()).await;
		back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await;

		let candidate_a_pending_av = CandidatePendingAvailability {
			candidate_hash: candidate_hash_a,
			descriptor: candidate_a.descriptor.clone(),
			commitments: candidate_a.commitments.clone(),
			relay_parent_number: candidate_relay_parent_number,
			max_pov_size: MAX_POV_SIZE,
		};
		let leaf_b = TestLeaf {
			number: leaf_b_number,
			hash: leaf_b_hash,
			para_data: vec![(
				1.into(),
				PerParaData::new_with_pending(
					candidate_relay_parent_number + 1,
					para_head.clone(),
					vec![candidate_a_pending_av],
				),
			)],
		};
		activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;

		introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await;
		second_candidate(&mut virtual_overseer, candidate_b.clone()).await;
		back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await;

		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_b,
			para_id,
			vec![candidate_hash_a].into_iter().collect(),
			1,
			vec![(candidate_hash_b, leaf_b_hash)],
		)
		.await;

		virtual_overseer
	});
}

#[test]
fn backwards_compatible() {
	let mut test_state = TestState::default();
	let para_id = ParaId::from(1);
	test_state.availability_cores = test_state
		.availability_cores
		.into_iter()
		.filter(|core| match core {
			CoreState::Scheduled(scheduled_core) => scheduled_core.para_id == para_id,
			_ => false,
		})
		.collect();
	assert_eq!(test_state.availability_cores.len(), 1);

	test_harness(|mut virtual_overseer| async move {
		let para_head = HeadData(vec![1, 2, 3]);

		let leaf_b_hash = Hash::repeat_byte(15);
		let candidate_relay_parent = get_parent_hash(leaf_b_hash);
		let candidate_relay_parent_number = 100;

		let leaf_a = TestLeaf {
			number: candidate_relay_parent_number,
			hash: candidate_relay_parent,
			para_data: vec![(
				para_id,
				PerParaData::new(candidate_relay_parent_number, para_head.clone()),
			)],
		};

		// Activate leaf.
		activate_leaf_with_params(
			&mut virtual_overseer,
			&leaf_a,
			&test_state,
			AsyncBackingParams { allowed_ancestry_len: 0, max_candidate_depth: 0 },
		)
		.await;

		// Candidate A
		let (candidate_a, pvd_a) = make_candidate(
			candidate_relay_parent,
			candidate_relay_parent_number,
			para_id,
			para_head.clone(),
			HeadData(vec![1]),
			test_state.validation_code_hash,
		);
		let candidate_hash_a = candidate_a.hash();

		introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
		second_candidate(&mut virtual_overseer, candidate_a.clone()).await;
		back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await;

		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_a,
			para_id,
			Ancestors::new(),
			1,
			vec![(candidate_hash_a, candidate_relay_parent)],
		)
		.await;

		let leaf_b = TestLeaf {
			number: candidate_relay_parent_number + 1,
			hash: leaf_b_hash,
			para_data: vec![(
				para_id,
				PerParaData::new(candidate_relay_parent_number + 1, para_head.clone()),
			)],
		};
		activate_leaf_with_params(
			&mut virtual_overseer,
			&leaf_b,
			&test_state,
			AsyncBackingParams { allowed_ancestry_len: 0, max_candidate_depth: 0 },
		)
		.await;

		get_backable_candidates(
			&mut virtual_overseer,
			&leaf_b,
			para_id,
			Ancestors::new(),
			1,
			vec![],
		)
		.await;

		virtual_overseer
	});
}

#[test]
fn uses_ancestry_only_within_session() {
	test_harness(|mut virtual_overseer| async move {
		let number = 5;
		let hash = Hash::repeat_byte(5);
		let ancestry_len = 3;
		let session = 2;

		let ancestry_hashes =
			vec![Hash::repeat_byte(4), Hash::repeat_byte(3), Hash::repeat_byte(2)];
		let session_change_hash = Hash::repeat_byte(3);

		let activated = new_leaf(hash, number);

		virtual_overseer
			.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(
				ActiveLeavesUpdate::start_work(activated),
			)))
			.await;

		assert_matches!(
					virtual_overseer.recv().await,
					AllMessages::RuntimeApi(
						RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx))
					) if parent == hash => {
						tx.send(Ok(AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: ancestry_len
		})).unwrap(); 			}
				);

		assert_matches!(
			virtual_overseer.recv().await,
			AllMessages::RuntimeApi(
				RuntimeApiMessage::Request(parent, RuntimeApiRequest::AvailabilityCores(tx))
			) if parent == hash => {
				tx.send(Ok(Vec::new())).unwrap();
			}
		);

		send_block_header(&mut virtual_overseer, hash, number).await;

		assert_matches!(
			virtual_overseer.recv().await,
			AllMessages::ChainApi(
				ChainApiMessage::Ancestors{hash: block_hash, k, response_channel: tx}
			) if block_hash == hash && k == ancestry_len as usize => {
				tx.send(Ok(ancestry_hashes.clone())).unwrap();
			}
		);

		assert_matches!(
			virtual_overseer.recv().await,
			AllMessages::RuntimeApi(
				RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
			) if parent == hash => {
				tx.send(Ok(session)).unwrap();
			}
		);

		for (i, hash) in ancestry_hashes.into_iter().enumerate() {
			let number = number - (i + 1) as BlockNumber;
			send_block_header(&mut virtual_overseer, hash, number).await;
			assert_matches!(
				virtual_overseer.recv().await,
				AllMessages::RuntimeApi(
					RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
				) if parent == hash => {
					if hash == session_change_hash {
						tx.send(Ok(session - 1)).unwrap();
						break
					} else {
						tx.send(Ok(session)).unwrap();
					}
				}
			);
		}

		virtual_overseer
	});
}
