1use crate::{
22 extrinsic::{
23 bench::{Benchmark, BenchmarkParams as ExtrinsicBenchmarkParams},
24 ExtrinsicBuilder,
25 },
26 overhead::{
27 command::ChainType::{Relaychain, Teyrchain, Unknown},
28 fake_runtime_api,
29 remark_builder::BizinikiwiRemarkBuilder,
30 template::TemplateData,
31 },
32 shared::{
33 genesis_state,
34 genesis_state::{GenesisStateHandler, SpecGenesisSource},
35 HostInfoParams, WeightParams,
36 },
37};
38use clap::{error::ErrorKind, Args, CommandFactory, Parser};
39use codec::Decode;
40#[cfg(feature = "teyrchain-benchmarks")]
41use codec::Encode;
42use fake_runtime_api::RuntimeApi as FakeRuntimeApi;
43use genesis_state::WARN_SPEC_GENESIS_CTOR;
44use log::info;
45#[cfg(feature = "teyrchain-benchmarks")]
47use pezcumulus_client_teyrchain_inherent::MockValidationDataInherentDataProvider;
48use pezframe_support::Deserialize;
49use pezkuwi_subxt::{client::RuntimeVersion, ext::futures, Metadata};
50#[cfg(feature = "teyrchain-benchmarks")]
51use pezkuwi_teyrchain_primitives::primitives::Id as ParaId;
52use pezsc_block_builder::BlockBuilderApi;
53use pezsc_chain_spec::{ChainSpec, ChainSpecExtension, GenesisBlockBuilder};
54use pezsc_cli::{CliConfiguration, Database, ImportParams, Result, SharedParams};
55use pezsc_client_api::{execution_extensions::ExecutionExtensions, UsageProvider};
56use pezsc_client_db::{BlocksPruning, DatabaseSettings};
57use pezsc_executor::WasmExecutor;
58use pezsc_runtime_utilities::fetch_latest_metadata_from_code_blob;
59use pezsc_service::{new_client, new_db_backend, BasePath, ClientConfig, TFullClient, TaskManager};
60use pezsp_api::{ApiExt, CallApiAt, Core, ProvideRuntimeApi};
61use pezsp_blockchain::HeaderBackend;
62use pezsp_core::H256;
63use pezsp_inherents::{InherentData, InherentDataProvider};
64use pezsp_runtime::{
65 generic,
66 traits::{BlakeTwo256, Block as BlockT},
67 DigestItem, OpaqueExtrinsic,
68};
69use pezsp_storage::Storage;
70use pezsp_wasm_interface::HostFunctions;
71use serde::Serialize;
72use serde_json::{json, Value};
73use std::{
74 fmt::{Debug, Display, Formatter},
75 fs,
76 path::PathBuf,
77 sync::Arc,
78};
79
80const DEFAULT_PARA_ID: u32 = 100;
81const LOG_TARGET: &'static str = "pezframe::benchmark::overhead";
82
83#[derive(Debug, Parser)]
85pub struct OverheadCmd {
86 #[allow(missing_docs)]
87 #[clap(flatten)]
88 pub shared_params: SharedParams,
89
90 #[allow(missing_docs)]
91 #[clap(flatten)]
92 pub import_params: ImportParams,
93
94 #[allow(missing_docs)]
95 #[clap(flatten)]
96 pub params: OverheadParams,
97}
98
99#[derive(Debug, Default, Serialize, Clone, PartialEq, Args)]
101pub struct OverheadParams {
102 #[allow(missing_docs)]
103 #[clap(flatten)]
104 pub weight: WeightParams,
105
106 #[allow(missing_docs)]
107 #[clap(flatten)]
108 pub bench: ExtrinsicBenchmarkParams,
109
110 #[allow(missing_docs)]
111 #[clap(flatten)]
112 pub hostinfo: HostInfoParams,
113
114 #[arg(long, value_name = "PATH")]
118 pub header: Option<PathBuf>,
119
120 #[arg(long)]
124 pub enable_trie_cache: bool,
125
126 #[arg(
128 long,
129 value_name = "PATH",
130 conflicts_with = "chain",
131 required_if_eq("genesis_builder", "runtime")
132 )]
133 pub runtime: Option<PathBuf>,
134
135 #[arg(long, default_value = pezsp_genesis_builder::DEV_RUNTIME_PRESET)]
140 pub genesis_builder_preset: String,
141
142 #[arg(long, value_enum, alias = "genesis-builder-policy")]
148 pub genesis_builder: Option<GenesisBuilderPolicy>,
149
150 #[arg(long)]
153 pub para_id: Option<u32>,
154}
155
156#[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy, Serialize)]
158#[clap(rename_all = "kebab-case")]
159pub enum GenesisBuilderPolicy {
160 Runtime,
163 SpecRuntime,
165 #[value(alias = "spec")]
167 SpecGenesis,
168}
169
170#[derive(Serialize, Clone, PartialEq, Copy)]
172pub(crate) enum BenchmarkType {
173 Extrinsic,
175 Block,
177}
178
179pub type TeyrchainHostFunctions = (
181 pezcumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
182 pezsp_io::BizinikiwiHostFunctions,
183);
184
185pub type BlockNumber = u32;
186
187pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
189
190pub type OpaqueBlock = generic::Block<Header, OpaqueExtrinsic>;
192
193type OverheadClient<Block, HF> = TFullClient<Block, FakeRuntimeApi, WasmExecutor<HF>>;
195
196fn create_inherent_data<Client: UsageProvider<Block> + HeaderBackend<Block>, Block: BlockT>(
203 client: &Arc<Client>,
204 chain_type: &ChainType,
205) -> InherentData {
206 let genesis = client.usage_info().chain.best_hash;
207 let header = client.header(genesis).unwrap().unwrap();
208
209 let mut inherent_data = InherentData::new();
210
211 #[cfg(feature = "teyrchain-benchmarks")]
214 if let Teyrchain(para_id) = chain_type {
215 let teyrchain_validation_data_provider = MockValidationDataInherentDataProvider::<()> {
216 para_id: ParaId::from(*para_id),
217 current_para_block_head: Some(header.encode().into()),
218 relay_offset: 0,
219 ..Default::default()
220 };
221 let _ = futures::executor::block_on(
222 teyrchain_validation_data_provider.provide_inherent_data(&mut inherent_data),
223 );
224 }
225 #[cfg(not(feature = "teyrchain-benchmarks"))]
226 if let Teyrchain(_) = chain_type {
227 log::warn!("Teyrchain benchmark inherents not available. Enable the 'teyrchain-benchmarks' feature.");
228 }
229
230 let para_inherent = pezkuwi_primitives::InherentData {
232 bitfields: Vec::new(),
233 backed_candidates: Vec::new(),
234 disputes: Vec::new(),
235 parent_header: header,
236 };
237
238 let timestamp =
240 pezsp_timestamp::InherentDataProvider::new(std::time::Duration::default().into());
241
242 let _ = futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data));
243 let _ =
244 inherent_data.put_data(pezkuwi_primitives::TEYRCHAINS_INHERENT_IDENTIFIER, ¶_inherent);
245
246 inherent_data
247}
248
249fn identify_chain(metadata: &Metadata, para_id: Option<u32>) -> ChainType {
254 let teyrchain_info_exists = metadata.pallet_by_name("TeyrchainInfo").is_some();
255 let teyrchain_system_exists = metadata.pallet_by_name("TeyrchainSystem").is_some();
256 let para_inherent_exists = metadata.pallet_by_name("ParaInherent").is_some();
257
258 log::debug!("{} TeyrchainSystem", if teyrchain_system_exists { "✅" } else { "❌" });
259 log::debug!("{} TeyrchainInfo", if teyrchain_info_exists { "✅" } else { "❌" });
260 log::debug!("{} ParaInherent", if para_inherent_exists { "✅" } else { "❌" });
261
262 let chain_type = if teyrchain_system_exists && teyrchain_info_exists {
263 Teyrchain(para_id.unwrap_or(DEFAULT_PARA_ID))
264 } else if para_inherent_exists {
265 Relaychain
266 } else {
267 Unknown
268 };
269
270 log::info!(target: LOG_TARGET, "Identified Chain type from metadata: {}", chain_type);
271
272 chain_type
273}
274
275#[derive(Deserialize, Serialize, Clone, ChainSpecExtension)]
276pub struct TeyrchainExtension {
277 pub para_id: Option<u32>,
279}
280
281impl OverheadCmd {
282 fn state_handler_from_cli<HF: HostFunctions>(
283 &self,
284 chain_spec_from_api: Option<Box<dyn ChainSpec>>,
285 ) -> Result<(GenesisStateHandler, Option<u32>)> {
286 let genesis_builder_to_source = || match self.params.genesis_builder {
287 Some(GenesisBuilderPolicy::Runtime) | Some(GenesisBuilderPolicy::SpecRuntime) => {
288 SpecGenesisSource::Runtime(self.params.genesis_builder_preset.clone())
289 },
290 Some(GenesisBuilderPolicy::SpecGenesis) | None => {
291 log::warn!(target: LOG_TARGET, "{WARN_SPEC_GENESIS_CTOR}");
292 SpecGenesisSource::SpecJson
293 },
294 };
295
296 if let Some(chain_spec) = chain_spec_from_api {
298 log::debug!(target: LOG_TARGET, "Initializing state handler with chain-spec from API: {:?}", chain_spec);
299
300 let source = genesis_builder_to_source();
301 return Ok((GenesisStateHandler::ChainSpec(chain_spec, source), self.params.para_id));
302 };
303
304 if let Some(chain_spec_path) = &self.shared_params.chain {
306 log::debug!(target: LOG_TARGET,
307 "Initializing state handler with chain-spec from path: {:?}",
308 chain_spec_path
309 );
310 let (chain_spec, para_id_from_chain_spec) =
311 genesis_state::chain_spec_from_path::<HF>(chain_spec_path.to_string().into())?;
312
313 let source = genesis_builder_to_source();
314
315 return Ok((
316 GenesisStateHandler::ChainSpec(chain_spec, source),
317 self.params.para_id.or(para_id_from_chain_spec),
318 ));
319 };
320
321 if let Some(runtime_path) = &self.params.runtime {
324 log::debug!(target: LOG_TARGET, "Initializing state handler with runtime from path: {:?}", runtime_path);
325
326 let runtime_blob = fs::read(runtime_path)?;
327 return Ok((
328 GenesisStateHandler::Runtime(
329 runtime_blob,
330 Some(self.params.genesis_builder_preset.clone()),
331 ),
332 self.params.para_id,
333 ));
334 };
335
336 Err("Neither a runtime nor a chain-spec were specified".to_string().into())
337 }
338
339 fn check_args(
340 &self,
341 chain_spec: &Option<Box<dyn ChainSpec>>,
342 ) -> std::result::Result<(), (ErrorKind, String)> {
343 if chain_spec.is_none()
344 && self.params.runtime.is_none()
345 && self.shared_params.chain.is_none()
346 {
347 return Err((
348 ErrorKind::MissingRequiredArgument,
349 "Provide either a runtime via `--runtime` or a chain spec via `--chain`"
350 .to_string(),
351 ));
352 }
353
354 match self.params.genesis_builder {
355 Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::SpecRuntime) => {
356 if chain_spec.is_none() && self.shared_params.chain.is_none() {
357 return Err((
358 ErrorKind::MissingRequiredArgument,
359 "Provide a chain spec via `--chain`.".to_string(),
360 ));
361 }
362 },
363 _ => {},
364 };
365 Ok(())
366 }
367
368 pub fn run_with_default_builder_and_spec<Block, ExtraHF>(
373 &self,
374 chain_spec: Option<Box<dyn ChainSpec>>,
375 ) -> Result<()>
376 where
377 Block: BlockT<Extrinsic = OpaqueExtrinsic, Hash = H256>,
378 ExtraHF: HostFunctions,
379 {
380 self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(
381 Box::new(|metadata, hash, version| {
382 let genesis = pezkuwi_subxt::utils::H256::from(hash.to_fixed_bytes());
383 Box::new(BizinikiwiRemarkBuilder::new(metadata, genesis, version)) as Box<_>
384 }),
385 chain_spec,
386 )
387 }
388
389 pub fn run_with_extrinsic_builder_and_spec<Block, ExtraHF>(
395 &self,
396 ext_builder_provider: Box<
397 dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
398 >,
399 chain_spec: Option<Box<dyn ChainSpec>>,
400 ) -> Result<()>
401 where
402 Block: BlockT<Extrinsic = OpaqueExtrinsic>,
403 ExtraHF: HostFunctions,
404 {
405 if let Err((error_kind, msg)) = self.check_args(&chain_spec) {
406 let mut cmd = OverheadCmd::command();
407 cmd.error(error_kind, msg).exit();
408 };
409
410 let (state_handler, para_id) =
411 self.state_handler_from_cli::<(TeyrchainHostFunctions, ExtraHF)>(chain_spec)?;
412
413 let executor = WasmExecutor::<(TeyrchainHostFunctions, ExtraHF)>::builder()
414 .with_allow_missing_host_functions(true)
415 .build();
416
417 let opaque_metadata =
418 fetch_latest_metadata_from_code_blob(&executor, state_handler.get_code_bytes()?)
419 .map_err(|_| {
420 <&str as Into<pezsc_cli::Error>>::into("Unable to fetch latest stable metadata")
421 })?;
422 let metadata = pezkuwi_subxt::Metadata::decode(&mut (*opaque_metadata).as_slice())?;
423
424 let chain_type = identify_chain(&metadata, para_id);
426
427 let genesis_patcher = match chain_type {
430 Teyrchain(para_id) => {
431 Some(Box::new(move |value| patch_genesis(value, Some(para_id))) as Box<_>)
432 },
433 _ => None,
434 };
435
436 let client = self.build_client_components::<Block, (TeyrchainHostFunctions, ExtraHF)>(
437 state_handler.build_storage::<(TeyrchainHostFunctions, ExtraHF)>(genesis_patcher)?,
438 executor,
439 &chain_type,
440 )?;
441
442 let inherent_data = create_inherent_data(&client, &chain_type);
443
444 let (ext_builder, runtime_name) = {
445 let genesis = client.usage_info().chain.best_hash;
446 let version = client.runtime_api().version(genesis).unwrap();
447 let runtime_name = version.spec_name;
448 let runtime_version = RuntimeVersion {
449 spec_version: version.spec_version,
450 transaction_version: version.transaction_version,
451 };
452
453 (ext_builder_provider(metadata, genesis, runtime_version), runtime_name)
454 };
455
456 self.run(
457 runtime_name.to_string(),
458 client,
459 inherent_data,
460 Default::default(),
461 &*ext_builder,
462 chain_type.requires_proof_recording(),
463 )
464 }
465
466 pub fn run_with_extrinsic_builder<Block, ExtraHF>(
468 &self,
469 ext_builder_provider: Box<
470 dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
471 >,
472 ) -> Result<()>
473 where
474 Block: BlockT<Extrinsic = OpaqueExtrinsic>,
475 ExtraHF: HostFunctions,
476 {
477 self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(ext_builder_provider, None)
478 }
479
480 fn build_client_components<Block, HF>(
481 &self,
482 genesis_storage: Storage,
483 executor: WasmExecutor<HF>,
484 chain_type: &ChainType,
485 ) -> Result<Arc<OverheadClient<Block, HF>>>
486 where
487 Block: BlockT,
488 HF: HostFunctions,
489 {
490 let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone()));
491
492 let base_path = match &self.shared_params.base_path {
493 None => BasePath::new_temp_dir()?,
494 Some(path) => BasePath::from(path.clone()),
495 };
496
497 let database_source = self.database_config(
498 &base_path.path().to_path_buf(),
499 self.database_cache_size()?.unwrap_or(1024),
500 self.database()?.unwrap_or(Database::Auto),
501 )?;
502
503 let backend = new_db_backend(DatabaseSettings {
504 trie_cache_maximum_size: self.trie_cache_maximum_size()?,
505 state_pruning: None,
506 blocks_pruning: BlocksPruning::KeepAll,
507 source: database_source,
508 metrics_registry: None,
509 })?;
510
511 let genesis_block_builder = GenesisBlockBuilder::new_with_storage(
512 genesis_storage,
513 true,
514 backend.clone(),
515 executor.clone(),
516 )?;
517
518 let tokio_runtime = pezsc_cli::build_runtime()?;
519 let task_manager = TaskManager::new(tokio_runtime.handle().clone(), None)
520 .map_err(|_| "Unable to build task manager")?;
521
522 let client: Arc<OverheadClient<Block, HF>> = Arc::new(new_client(
523 backend.clone(),
524 executor,
525 genesis_block_builder,
526 Default::default(),
527 Default::default(),
528 extensions,
529 Box::new(task_manager.spawn_handle()),
530 None,
531 None,
532 ClientConfig {
533 offchain_worker_enabled: false,
534 offchain_indexing_api: false,
535 wasm_runtime_overrides: None,
536 no_genesis: false,
537 wasm_runtime_substitutes: Default::default(),
538 enable_import_proof_recording: chain_type.requires_proof_recording(),
539 },
540 )?);
541
542 Ok(client)
543 }
544
545 pub fn run<Block, C>(
550 &self,
551 chain_name: String,
552 client: Arc<C>,
553 inherent_data: pezsp_inherents::InherentData,
554 digest_items: Vec<DigestItem>,
555 ext_builder: &dyn ExtrinsicBuilder,
556 should_record_proof: bool,
557 ) -> Result<()>
558 where
559 Block: BlockT<Extrinsic = OpaqueExtrinsic>,
560 C: ProvideRuntimeApi<Block>
561 + CallApiAt<Block>
562 + UsageProvider<Block>
563 + pezsp_blockchain::HeaderBackend<Block>,
564 C::Api: ApiExt<Block> + BlockBuilderApi<Block>,
565 {
566 if ext_builder.pezpallet() != "system" || ext_builder.extrinsic() != "remark" {
567 return Err(format!("The extrinsic builder is required to build `System::Remark` extrinsics but builds `{}` extrinsics instead", ext_builder.name()).into());
568 }
569
570 let bench = Benchmark::new(
571 client,
572 self.params.bench.clone(),
573 inherent_data,
574 digest_items,
575 should_record_proof,
576 );
577
578 {
580 let (stats, proof_size) = bench.bench_block()?;
581 info!(target: LOG_TARGET, "Per-block execution overhead [ns]:\n{:?}", stats);
582 let template = TemplateData::new(
583 BenchmarkType::Block,
584 &chain_name,
585 &self.params,
586 &stats,
587 proof_size,
588 )?;
589 template.write(&self.params.weight.weight_path)?;
590 }
591 {
593 let (stats, proof_size) = bench.bench_extrinsic(ext_builder)?;
594 info!(target: LOG_TARGET, "Per-extrinsic execution overhead [ns]:\n{:?}", stats);
595 let template = TemplateData::new(
596 BenchmarkType::Extrinsic,
597 &chain_name,
598 &self.params,
599 &stats,
600 proof_size,
601 )?;
602 template.write(&self.params.weight.weight_path)?;
603 }
604
605 Ok(())
606 }
607}
608
609impl BenchmarkType {
610 pub(crate) fn short_name(&self) -> &'static str {
612 match self {
613 Self::Extrinsic => "extrinsic",
614 Self::Block => "block",
615 }
616 }
617
618 pub(crate) fn long_name(&self) -> &'static str {
620 match self {
621 Self::Extrinsic => "ExtrinsicBase",
622 Self::Block => "BlockExecution",
623 }
624 }
625}
626
627#[derive(Clone, PartialEq, Debug)]
628enum ChainType {
629 Teyrchain(u32),
630 Relaychain,
631 Unknown,
632}
633
634impl Display for ChainType {
635 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
636 match self {
637 ChainType::Teyrchain(id) => write!(f, "Teyrchain(paraid = {})", id),
638 ChainType::Relaychain => write!(f, "Relaychain"),
639 ChainType::Unknown => write!(f, "Unknown"),
640 }
641 }
642}
643
644impl ChainType {
645 fn requires_proof_recording(&self) -> bool {
646 match self {
647 Teyrchain(_) => true,
648 Relaychain => false,
649 Unknown => false,
650 }
651 }
652}
653
654fn patch_genesis(mut input_value: Value, para_id: Option<u32>) -> Value {
657 if let Some(para_id) = para_id {
661 pezsc_chain_spec::json_patch::merge(
662 &mut input_value,
663 json!({
664 "teyrchainInfo": {
665 "teyrchainId": para_id,
666 }
667 }),
668 );
669 log::debug!(target: LOG_TARGET, "Genesis Config Json");
670 log::debug!(target: LOG_TARGET, "{}", input_value);
671 }
672 input_value
673}
674
675impl CliConfiguration for OverheadCmd {
677 fn shared_params(&self) -> &SharedParams {
678 &self.shared_params
679 }
680
681 fn import_params(&self) -> Option<&ImportParams> {
682 Some(&self.import_params)
683 }
684
685 fn base_path(&self) -> Result<Option<BasePath>> {
686 Ok(Some(BasePath::new_temp_dir()?))
687 }
688
689 fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
690 if self.params.enable_trie_cache {
691 Ok(self.import_params().map(|x| x.trie_cache_maximum_size()).unwrap_or_default())
692 } else {
693 Ok(None)
694 }
695 }
696}
697
698#[cfg(test)]
699mod tests {
700 use crate::{
701 overhead::command::{identify_chain, ChainType, TeyrchainHostFunctions, DEFAULT_PARA_ID},
702 OverheadCmd,
703 };
704 use clap::Parser;
705 use codec::Decode;
706 use pezsc_executor::WasmExecutor;
707
708 #[test]
709 fn test_chain_type_relaychain() {
710 let executor: WasmExecutor<TeyrchainHostFunctions> = WasmExecutor::builder().build();
711 let code_bytes = zagros_runtime::WASM_BINARY
712 .expect("To run this test, build the wasm binary of zagros-runtime")
713 .to_vec();
714 let opaque_metadata =
715 super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
716 let metadata = pezkuwi_subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
717 let chain_type = identify_chain(&metadata, None);
718 assert_eq!(chain_type, ChainType::Relaychain);
719 assert_eq!(chain_type.requires_proof_recording(), false);
720 }
721
722 #[test]
723 fn test_chain_type_teyrchain() {
724 let executor: WasmExecutor<TeyrchainHostFunctions> = WasmExecutor::builder().build();
725 let code_bytes = pezcumulus_test_runtime::WASM_BINARY
726 .expect("To run this test, build the wasm binary of pezcumulus-test-runtime")
727 .to_vec();
728 let opaque_metadata =
729 super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
730 let metadata = pezkuwi_subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
731 let chain_type = identify_chain(&metadata, Some(100));
732 assert_eq!(chain_type, ChainType::Teyrchain(100));
733 assert!(chain_type.requires_proof_recording());
734 assert_eq!(identify_chain(&metadata, None), ChainType::Teyrchain(DEFAULT_PARA_ID));
735 }
736
737 #[test]
738 fn test_chain_type_custom() {
739 let executor: WasmExecutor<TeyrchainHostFunctions> = WasmExecutor::builder().build();
740 let code_bytes = bizinikiwi_test_runtime::WASM_BINARY
741 .expect("To run this test, build the wasm binary of bizinikiwi-test-runtime")
742 .to_vec();
743 let opaque_metadata =
744 super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
745 let metadata = pezkuwi_subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
746 let chain_type = identify_chain(&metadata, None);
747 assert_eq!(chain_type, ChainType::Unknown);
748 assert_eq!(chain_type.requires_proof_recording(), false);
749 }
750
751 fn cli_succeed(args: &[&str]) -> Result<(), clap::Error> {
752 let cmd = OverheadCmd::try_parse_from(args)?;
753 assert!(cmd.check_args(&None).is_ok());
754 Ok(())
755 }
756
757 fn cli_fail(args: &[&str]) {
758 let cmd = OverheadCmd::try_parse_from(args);
759 if let Ok(cmd) = cmd {
760 assert!(cmd.check_args(&None).is_err());
761 }
762 }
763
764 #[test]
765 fn test_cli_conflicts() -> Result<(), clap::Error> {
766 cli_succeed(&["test", "--runtime", "path/to/runtime", "--genesis-builder", "runtime"])?;
768 cli_succeed(&["test", "--runtime", "path/to/runtime"])?;
769 cli_succeed(&[
770 "test",
771 "--runtime",
772 "path/to/runtime",
773 "--genesis-builder-preset",
774 "preset",
775 ])?;
776 cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec"]);
777 cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-genesis"]);
778 cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-runtime"]);
779
780 cli_succeed(&["test", "--chain", "path/to/spec"])?;
782 cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec"])?;
783 cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-genesis"])?;
784 cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-runtime"])?;
785 cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "none"]);
786 cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "runtime"]);
787 cli_fail(&[
788 "test",
789 "--chain",
790 "path/to/spec",
791 "--genesis-builder",
792 "runtime",
793 "--genesis-builder-preset",
794 "preset",
795 ]);
796 Ok(())
797 }
798}