1
1
// SPDX-License-Identifier: GPL-3.0
2
2
3
3
use crate :: {
4
- cli:: {
5
- self ,
6
- traits:: { Cli , Select } ,
7
- } ,
4
+ cli:: { self } ,
8
5
common:: prompt:: display_message,
9
6
} ;
10
7
use clap:: { Args , Subcommand } ;
11
8
use frame_benchmarking_cli:: PalletCmd ;
12
- use pop_common:: { manifest:: from_path, Profile } ;
13
- use pop_parachains:: {
14
- build_project, get_preset_names, get_runtime_path, parse_genesis_builder_policy,
15
- run_pallet_benchmarking, runtime_binary_path,
16
- } ;
17
- use std:: { env:: current_dir, fs, path:: PathBuf } ;
9
+ use pallet:: BenchmarkPallet ;
18
10
19
- const GENESIS_BUILDER_NO_POLICY : & str = "none" ;
20
- const GENESIS_BUILDER_RUNTIME_POLICY : & str = "runtime" ;
11
+ mod pallet;
21
12
22
13
/// Arguments for benchmarking a project.
23
14
#[ derive( Args ) ]
@@ -40,361 +31,7 @@ impl Command {
40
31
pub ( crate ) fn execute ( args : BenchmarkArgs ) -> anyhow:: Result < ( ) > {
41
32
let mut cli = cli:: Cli ;
42
33
match args. command {
43
- Command :: Pallet ( mut cmd) => Command :: bechmark_pallet ( & mut cmd, & mut cli) ,
44
- }
45
- }
46
-
47
- fn bechmark_pallet ( cmd : & mut PalletCmd , cli : & mut impl Cli ) -> anyhow:: Result < ( ) > {
48
- if cmd. list . is_some ( ) || cmd. json_output {
49
- if let Err ( e) = run_pallet_benchmarking ( cmd) {
50
- return display_message ( & e. to_string ( ) , false , cli) ;
51
- }
52
- }
53
- cli. intro ( "Benchmarking your pallets" ) ?;
54
- cli. warning (
55
- "NOTE: the `pop bench pallet` is not yet battle tested - double check the results." ,
56
- ) ?;
57
- if let Some ( ref spec) = cmd. shared_params . chain {
58
- return display_message (
59
- & format ! (
60
- "Chain specs are not supported. Please remove `--chain={spec}` \
61
- and use `--runtime=<PATH>` instead"
62
- ) ,
63
- false ,
64
- cli,
65
- ) ;
66
- }
67
- // No runtime path provided, auto-detect the runtime WASM binary. If not found, build
68
- // the runtime.
69
- if cmd. runtime . is_none ( ) {
70
- match ensure_runtime_binary_exists ( cli, & Profile :: Release ) {
71
- Ok ( runtime_binary_path) => cmd. runtime = Some ( runtime_binary_path) ,
72
- Err ( e) => {
73
- return display_message ( & e. to_string ( ) , false , cli) ;
74
- } ,
75
- }
76
- }
77
- // Prompt user only if no genesis builder policy is set
78
- if cmd. genesis_builder . is_none ( ) {
79
- if let Err ( e) = guide_user_to_configure_genesis ( cmd, cli) {
80
- return display_message ( & e. to_string ( ) , false , cli) ;
81
- }
82
- }
83
-
84
- cli. warning ( "NOTE: this may take some time..." ) ?;
85
- cli. info ( "Benchmarking and generating weight file..." ) ?;
86
- if let Err ( e) = run_pallet_benchmarking ( cmd) {
87
- return display_message ( & e. to_string ( ) , false , cli) ;
88
- }
89
- display_message ( "Benchmark completed successfully!" , true , cli) ?;
90
- Ok ( ( ) )
91
- }
92
- }
93
-
94
- // Locate runtime WASM binary. If it doesn't exist, trigger build.
95
- fn ensure_runtime_binary_exists (
96
- cli : & mut impl cli:: traits:: Cli ,
97
- mode : & Profile ,
98
- ) -> anyhow:: Result < PathBuf > {
99
- let cwd = current_dir ( ) . unwrap_or ( PathBuf :: from ( "./" ) ) ;
100
- let target_path = mode. target_directory ( & cwd) . join ( "wbuild" ) ;
101
- let mut project_path = get_runtime_path ( & cwd) ?;
102
-
103
- // If there is no TOML file exist, list all directories in the "runtime" folder and prompt the
104
- // user to select a runtime.
105
- if !project_path. join ( "Cargo.toml" ) . exists ( ) {
106
- let runtime = guide_user_to_select_runtime ( & project_path, cli) ?;
107
- project_path = project_path. join ( runtime) ;
108
- }
109
-
110
- match runtime_binary_path ( & target_path, & project_path) {
111
- Ok ( binary_path) => Ok ( binary_path) ,
112
- _ => {
113
- cli. info ( "Runtime binary was not found. The runtime will be built locally." ) ?;
114
- cli. warning ( "NOTE: this may take some time..." ) ?;
115
- build_project ( & project_path, None , mode, vec ! [ "runtime-benchmarks" ] , None ) ?;
116
- runtime_binary_path ( & target_path, & project_path) . map_err ( |e| e. into ( ) )
117
- } ,
118
- }
119
- }
120
-
121
- fn guide_user_to_configure_genesis (
122
- cmd : & mut PalletCmd ,
123
- cli : & mut impl cli:: traits:: Cli ,
124
- ) -> anyhow:: Result < ( ) > {
125
- let runtime_path = cmd. runtime . as_ref ( ) . expect ( "No runtime found." ) ;
126
- let preset_names = get_preset_names ( runtime_path) ?;
127
- let policy = match preset_names. is_empty ( ) {
128
- true => "none" ,
129
- false => guide_user_to_select_genesis_policy ( cli) ?,
130
- } ;
131
-
132
- let parsed_policy = parse_genesis_builder_policy ( policy) ?;
133
- cmd. genesis_builder = parsed_policy. genesis_builder ;
134
- if policy == GENESIS_BUILDER_RUNTIME_POLICY {
135
- cmd. genesis_builder_preset =
136
- guide_user_to_select_genesis_preset ( cli, runtime_path, & cmd. genesis_builder_preset ) ?;
137
- }
138
- Ok ( ( ) )
139
- }
140
-
141
- fn guide_user_to_select_runtime (
142
- project_path : & PathBuf ,
143
- cli : & mut impl cli:: traits:: Cli ,
144
- ) -> anyhow:: Result < PathBuf > {
145
- let runtimes = fs:: read_dir ( project_path) . unwrap ( ) ;
146
- let mut prompt = cli. select ( "Select the runtime:" ) ;
147
- for runtime in runtimes {
148
- let path = runtime. unwrap ( ) . path ( ) ;
149
- let manifest = from_path ( Some ( path. as_path ( ) ) ) ?;
150
- let package = manifest. package ( ) ;
151
- let name = package. clone ( ) . name ;
152
- let description = package. description ( ) . unwrap_or_default ( ) . to_string ( ) ;
153
- prompt = prompt. item ( path, & name, & description) ;
154
- }
155
- Ok ( prompt. interact ( ) ?)
156
- }
157
-
158
- fn guide_user_to_select_genesis_policy ( cli : & mut impl cli:: traits:: Cli ) -> anyhow:: Result < & str > {
159
- let mut prompt = cli. select ( "Select the genesis builder policy:" ) . initial_value ( "none" ) ;
160
- for ( policy, description) in [
161
- ( GENESIS_BUILDER_NO_POLICY , "Do not provide any genesis state" ) ,
162
- (
163
- GENESIS_BUILDER_RUNTIME_POLICY ,
164
- "Let the runtime build the genesis state through its `BuildGenesisConfig` runtime API" ,
165
- ) ,
166
- ] {
167
- prompt = prompt. item ( policy, policy, description) ;
168
- }
169
- Ok ( prompt. interact ( ) ?)
170
- }
171
-
172
- fn guide_user_to_select_genesis_preset (
173
- cli : & mut impl cli:: traits:: Cli ,
174
- runtime_path : & PathBuf ,
175
- default_value : & str ,
176
- ) -> anyhow:: Result < String > {
177
- let spinner = cliclack:: spinner ( ) ;
178
- spinner. start ( "Fetching available genesis builder presets of your runtime..." ) ;
179
- let mut prompt = cli
180
- . select ( "Select the genesis builder preset:" )
181
- . initial_value ( default_value. to_string ( ) ) ;
182
- let preset_names = get_preset_names ( runtime_path) ?;
183
- if preset_names. is_empty ( ) {
184
- return Err ( anyhow:: anyhow!( "No preset found for the runtime" ) )
185
- }
186
- spinner. stop ( format ! ( "Found {} genesis builder presets" , preset_names. len( ) ) ) ;
187
- for preset in preset_names {
188
- prompt = prompt. item ( preset. to_string ( ) , preset, "" ) ;
189
- }
190
- Ok ( prompt. interact ( ) ?)
191
- }
192
-
193
- #[ cfg( test) ]
194
- mod tests {
195
- use super :: * ;
196
- use crate :: cli:: MockCli ;
197
- use clap:: Parser ;
198
- use duct:: cmd;
199
- use std:: env;
200
- use tempfile:: tempdir;
201
-
202
- #[ test]
203
- fn benchmark_pallet_works ( ) -> anyhow:: Result < ( ) > {
204
- let mut cli =
205
- expect_select_genesis_policy ( expect_pallet_benchmarking_intro ( MockCli :: new ( ) ) , 0 )
206
- . expect_warning ( "NOTE: this may take some time..." )
207
- . expect_outro ( "Benchmark completed successfully!" ) ;
208
-
209
- let mut cmd = PalletCmd :: try_parse_from ( & [
210
- "" ,
211
- "--runtime" ,
212
- get_mock_runtime_path ( true ) . to_str ( ) . unwrap ( ) ,
213
- "--pallet" ,
214
- "pallet_timestamp" ,
215
- "--extrinsic" ,
216
- "" ,
217
- ] ) ?;
218
- Command :: bechmark_pallet ( & mut cmd, & mut cli) ?;
219
- cli. verify ( )
220
- }
221
-
222
- #[ test]
223
- fn benchmark_pallet_with_chainspec_fails ( ) -> anyhow:: Result < ( ) > {
224
- let spec = "path-to-chainspec" ;
225
- let mut cli =
226
- expect_pallet_benchmarking_intro ( MockCli :: new ( ) ) . expect_outro_cancel ( format ! (
227
- "Chain specs are not supported. Please remove `--chain={spec}` \
228
- and use `--runtime=<PATH>` instead"
229
- ) ) ;
230
-
231
- let mut cmd = PalletCmd :: try_parse_from ( & [
232
- "" ,
233
- "--chain" ,
234
- spec,
235
- "--pallet" ,
236
- "pallet_timestamp" ,
237
- "--extrinsic" ,
238
- "" ,
239
- ] ) ?;
240
-
241
- Command :: bechmark_pallet ( & mut cmd, & mut cli) ?;
242
- cli. verify ( )
243
- }
244
-
245
- #[ test]
246
- fn benchmark_pallet_without_runtime_benchmarks_feature_fails ( ) -> anyhow:: Result < ( ) > {
247
- let mut cli = expect_select_genesis_policy ( expect_pallet_benchmarking_intro ( MockCli :: new ( ) ) , 0 )
248
- . expect_outro_cancel (
249
- "Failed to run benchmarking: Invalid input: Could not call runtime API to Did not find the benchmarking metadata. \
250
- This could mean that you either did not build the node correctly with the `--features runtime-benchmarks` flag, \
251
- or the chain spec that you are using was not created by a node that was compiled with the flag: \
252
- Other: Exported method Benchmark_benchmark_metadata is not found"
253
- ) ;
254
- let mut cmd = PalletCmd :: try_parse_from ( & [
255
- "" ,
256
- "--runtime" ,
257
- get_mock_runtime_path ( false ) . to_str ( ) . unwrap ( ) ,
258
- "--pallet" ,
259
- "pallet_timestamp" ,
260
- "--extrinsic" ,
261
- "" ,
262
- ] ) ?;
263
- Command :: bechmark_pallet ( & mut cmd, & mut cli) ?;
264
- cli. verify ( )
265
- }
266
-
267
- #[ test]
268
- fn benchmark_pallet_fails_with_error ( ) -> anyhow:: Result < ( ) > {
269
- let mut cli = expect_select_genesis_policy ( expect_pallet_benchmarking_intro ( MockCli :: new ( ) ) , 0 )
270
- . expect_outro_cancel ( "Failed to run benchmarking: Invalid input: No benchmarks found which match your input." ) ;
271
- let mut cmd = PalletCmd :: try_parse_from ( & [
272
- "" ,
273
- "--runtime" ,
274
- get_mock_runtime_path ( true ) . to_str ( ) . unwrap ( ) ,
275
- "--pallet" ,
276
- "unknown-pallet-name" ,
277
- "--extrinsic" ,
278
- "" ,
279
- ] ) ?;
280
- Command :: bechmark_pallet ( & mut cmd, & mut cli) ?;
281
- cli. verify ( )
282
- }
283
-
284
- #[ test]
285
- fn guide_user_to_select_runtime_works ( ) -> anyhow:: Result < ( ) > {
286
- let temp_dir = tempdir ( ) ?;
287
- let runtime_path = temp_dir. path ( ) . join ( "runtime" ) ;
288
- let runtimes = [ "runtime-1" , "runtime-2" , "runtime-3" ] ;
289
- let mut cli = MockCli :: new ( ) . expect_select (
290
- "Select the runtime:" ,
291
- Some ( true ) ,
292
- true ,
293
- Some ( runtimes. map ( |runtime| ( runtime. to_string ( ) , "" . to_string ( ) ) ) . to_vec ( ) ) ,
294
- 0 ,
295
- ) ;
296
- fs:: create_dir ( & runtime_path) ?;
297
- for runtime in runtimes {
298
- cmd ( "cargo" , [ "new" , runtime, "--bin" ] ) . dir ( & runtime_path) . run ( ) ?;
34
+ Command :: Pallet ( mut cmd) => BenchmarkPallet . execute ( & mut cmd, & mut cli) ,
299
35
}
300
- guide_user_to_select_runtime ( & runtime_path, & mut cli) ?;
301
- cli. verify ( )
302
- }
303
-
304
- #[ test]
305
- fn guide_user_to_configure_genesis_works ( ) -> anyhow:: Result < ( ) > {
306
- let runtime_path = get_mock_runtime_path ( false ) ;
307
- let mut cli = expect_select_genesis_preset (
308
- expect_select_genesis_policy ( MockCli :: new ( ) , 1 ) ,
309
- & runtime_path,
310
- 0 ,
311
- ) ;
312
- let mut cmd = PalletCmd :: try_parse_from ( & [
313
- "" ,
314
- "--runtime" ,
315
- runtime_path. to_str ( ) . unwrap ( ) ,
316
- "--pallet" ,
317
- "" ,
318
- "--extrinsic" ,
319
- "" ,
320
- ] ) ?;
321
- guide_user_to_configure_genesis ( & mut cmd, & mut cli) ?;
322
- assert_eq ! ( cmd. genesis_builder, parse_genesis_builder_policy( "runtime" ) ?. genesis_builder) ;
323
- assert_eq ! (
324
- cmd. genesis_builder_preset,
325
- get_preset_names( & runtime_path) ?. first( ) . cloned( ) . unwrap_or_default( )
326
- ) ;
327
- cli. verify ( )
328
- }
329
-
330
- #[ test]
331
- fn guide_user_to_select_genesis_policy_works ( ) -> anyhow:: Result < ( ) > {
332
- // Select genesis builder policy `none`.
333
- let mut cli = expect_select_genesis_policy ( MockCli :: new ( ) , 0 ) ;
334
- guide_user_to_select_genesis_policy ( & mut cli) ?;
335
- cli. verify ( ) ?;
336
-
337
- // Select genesis builder policy `runtime`.
338
- let runtime_path = get_mock_runtime_path ( false ) ;
339
- cli = expect_select_genesis_preset (
340
- expect_select_genesis_policy ( MockCli :: new ( ) , 1 ) ,
341
- & runtime_path,
342
- 0 ,
343
- ) ;
344
- guide_user_to_select_genesis_policy ( & mut cli) ?;
345
- guide_user_to_select_genesis_preset ( & mut cli, & runtime_path, "development" ) ?;
346
- cli. verify ( )
347
- }
348
-
349
- #[ test]
350
- fn guide_user_to_select_genesis_preset_works ( ) -> anyhow:: Result < ( ) > {
351
- let runtime_path = get_mock_runtime_path ( false ) ;
352
- let mut cli = expect_select_genesis_preset ( MockCli :: new ( ) , & runtime_path, 0 ) ;
353
- guide_user_to_select_genesis_preset ( & mut cli, & runtime_path, "development" ) ?;
354
- cli. verify ( )
355
- }
356
-
357
- fn expect_pallet_benchmarking_intro ( cli : MockCli ) -> MockCli {
358
- cli. expect_intro ( "Benchmarking your pallets" ) . expect_warning (
359
- "NOTE: the `pop bench pallet` is not yet battle tested - double check the results." ,
360
- )
361
- }
362
-
363
- fn expect_select_genesis_policy ( cli : MockCli , item : usize ) -> MockCli {
364
- let policies = vec ! [
365
- ( GENESIS_BUILDER_NO_POLICY . to_string( ) , "Do not provide any genesis state" . to_string( ) ) ,
366
- ( GENESIS_BUILDER_RUNTIME_POLICY . to_string( ) , "Let the runtime build the genesis state through its `BuildGenesisConfig` runtime API" . to_string( ) )
367
- ] ;
368
- cli. expect_select (
369
- "Select the genesis builder policy:" ,
370
- Some ( true ) ,
371
- true ,
372
- Some ( policies) ,
373
- item,
374
- )
375
- }
376
-
377
- fn expect_select_genesis_preset ( cli : MockCli , runtime_path : & PathBuf , item : usize ) -> MockCli {
378
- let preset_names = get_preset_names ( runtime_path)
379
- . unwrap ( )
380
- . into_iter ( )
381
- . map ( |preset| ( preset, String :: default ( ) ) )
382
- . collect ( ) ;
383
- cli. expect_select (
384
- "Select the genesis builder preset:" ,
385
- Some ( true ) ,
386
- true ,
387
- Some ( preset_names) ,
388
- item,
389
- )
390
- }
391
-
392
- // Construct the path to the mock runtime WASM file.
393
- fn get_mock_runtime_path ( with_benchmark_features : bool ) -> std:: path:: PathBuf {
394
- let path = format ! (
395
- "../../tests/runtimes/{}.wasm" ,
396
- if with_benchmark_features { "base_parachain_benchmark" } else { "base_parachain" }
397
- ) ;
398
- env:: current_dir ( ) . unwrap ( ) . join ( path) . canonicalize ( ) . unwrap ( )
399
36
}
400
37
}
0 commit comments