Skip to content

Commit f2a3291

Browse files
committed
add --profile-path flag
1 parent e18191c commit f2a3291

File tree

3 files changed

+50
-16
lines changed

3 files changed

+50
-16
lines changed

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,20 @@ This will run a load test with a general BSC profile.
110110

111111

112112
### Parameters and Flags
113-
- `-p, --profile`: Specifies the profile to use for the benchmark. Available profiles can be found in the profile directory. Sample usage `-p bsc.general`
114-
- `-d` or `--profile-dir`: Specifies the base directory to use which contain profiles, supporting up to one-level of subdirectories.
115-
- `-s, --shape`: Specifies the shape of the load pattern. List available shapes with `chainbench list shapes`.
116-
- `-u, --users`: Sets the number of simulated users to use for the benchmark.
117-
- `-r, --spawn-rate`: Sets the spawn rate of users per second.
118-
- `-w, --workers`: Sets the number of worker threads to use for the benchmark.
119-
- `-t, --test-time`: Sets the duration of the test to run.
113+
- `-p`, `--profile`: Specifies the profile to use for the benchmark. Available profiles can be found in the profile directory. Sample usage `-p bsc.general`
114+
- `-d`, `--profile-dir`: Specifies the base directory to use which contain profiles, supporting up to one-level of subdirectories.
115+
- `--profile-path`: Specifies the full path to the profile file to use for the benchmark. Overrides `--profile` and `--profile-dir` options.
116+
- `-s`, `--shape`: Specifies the shape of the load pattern. List available shapes with `chainbench list shapes`.
117+
- `-u`, `--users`: Sets the number of simulated users to use for the benchmark.
118+
- `-r`, `--spawn-rate`: Sets the spawn rate of users per second.
119+
- `-w`, `--workers`: Sets the number of worker threads to use for the benchmark.
120+
- `-t`, `--test-time`: Sets the duration of the test to run.
120121
- `--target`: Specifies the target blockchain node URL that the benchmark will connect to.
121122
- `--headless`: Runs the benchmark in headless mode, meaning that no graphical user interface (GUI) will be displayed during the test. This is useful for running the test on a remote server or when the GUI is not needed.
122123
- `--autoquit`: Tells the Chainbench tool to automatically quit after the test has finished. This is useful for running the benchmark in an automated environment where manual intervention is not desired.
123124
- `--help`: Displays the help message.
124125
- `--debug-trace-methods`: Enables tasks tagged with debug or trace to be executed
125-
- `-E, --exclude-tags`: Exclude tasks tagged with custom tags from the test. You may specify this option multiple times.
126+
- `-E`, `--exclude-tags`: Exclude tasks tagged with custom tags from the test. You may specify this option multiple times.
126127
- `--use-latest-blocks`: Use latest blocks for test data generation and runs a background process to update the test data with latest blocks.
127128
- `--size`: Specifies the test data size. Available values are XS, S, M, L, XL. Default is S.
128129
- `--batch`: Runs the test using batch requests. This will send multiple requests in a single batch request. The number of requests in a batch can be specified using the `--batch-size` flag. Default batch size is 10.

chainbench/main.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ def validate_profile(ctx: Context, param: Parameter, value: str) -> str:
8484
return value
8585

8686

87+
def validate_profile_path(ctx: Context, param: Parameter, value: str) -> str:
88+
if value is not None:
89+
if "profile_dir" or "profile" in ctx.params:
90+
click.echo("WARNING: Profile and Profile Directory options are ignored when --profile-path flag is used.")
91+
if "method" in ctx.params:
92+
click.echo("WARNING: Profile and Profile Directory options are ignored when method argument is used.")
93+
"""Validate profile path."""
94+
abs_profile_path = Path(value).resolve()
95+
if not abs_profile_path.exists():
96+
raise FileNotFoundError(f"Profile path not found: {abs_profile_path}")
97+
profile_exists(abs_profile_path.name.removesuffix(".py"), abs_profile_path.parent)
98+
return value
99+
87100
@cli.command(
88101
help="Start a load test on the specified method. "
89102
"Alternatively, you can specify a profile to run using the --profile option instead. "
@@ -107,6 +120,14 @@ def validate_profile(ctx: Context, param: Parameter, value: str) -> str:
107120
help="Profile to run",
108121
show_default=True,
109122
)
123+
@click.option(
124+
"--profile-path",
125+
default=None,
126+
callback=validate_profile_path,
127+
type=click.Path(exists=True, dir_okay=False, file_okay=True, path_type=Path),
128+
help="Path to profile locustfile to be run. Overrides --profile and --profile-dir options.",
129+
show_default=True,
130+
)
110131
@click.option(
111132
"-s",
112133
"--shape",
@@ -193,6 +214,7 @@ def start(
193214
ctx: Context,
194215
profile: str,
195216
profile_dir: Path | None,
217+
profile_path: Path | None,
196218
shape: str | None,
197219
host: str,
198220
port: int,
@@ -243,18 +265,20 @@ def start(
243265
profile_dir = get_base_path(__file__) / "profile"
244266

245267
if method:
246-
profile_path = Path(all_methods[method])
268+
final_profile_path = Path(all_methods[method])
269+
elif profile_path:
270+
final_profile_path = profile_path
247271
elif profile:
248-
profile_path = get_profile_path(profile_dir, profile)
272+
final_profile_path = get_profile_path(profile_dir, profile)
249273
else:
250-
profile_path = profile_dir
274+
final_profile_path = profile_dir
251275

252-
if not profile_path.exists():
253-
click.echo(f"Profile path {profile_path} does not exist.")
276+
if not final_profile_path.exists():
277+
click.echo(f"Profile path {final_profile_path} does not exist.")
254278
sys.exit(1)
255279

256280
user_classes = {}
257-
for locustfile in parse_locustfile_paths([profile_path.__str__()]):
281+
for locustfile in parse_locustfile_paths([final_profile_path.__str__()]):
258282
_, _user_classes, _ = load_locustfile(locustfile)
259283
for key, value in _user_classes.items():
260284
user_classes[key] = value
@@ -273,7 +297,7 @@ def start(
273297
for test_data_type in test_data_types:
274298
click.echo(test_data_type)
275299
sys.exit(1)
276-
profile = profile_path.name
300+
profile = final_profile_path.name
277301
if not headless:
278302
enable_class_picker = True
279303

@@ -329,7 +353,7 @@ def start(
329353
custom_exclude_tags.extend(["batch", "batch_single"])
330354

331355
locust_options = LocustOptions(
332-
profile_path=profile_path,
356+
profile_path=final_profile_path,
333357
host=host,
334358
port=port,
335359
test_time=test_time,

chainbench/util/cli.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ def get_profiles(profile_dir: Path) -> list[str]:
4141
result.append(locustfile_path.parts[0][:-3])
4242
return result
4343

44+
def validate_profile_path(profile_path: Path) -> None:
45+
"""Validate profile path."""
46+
abs_profile_path = profile_path.resolve()
47+
if not profile_path.resolve().exists():
48+
raise FileNotFoundError(f"Profile file not found: {profile_path}")
49+
profiles = get_profiles(abs_profile_path.parent)
50+
if profile_path.stem.removesuffix(".py") not in profiles:
51+
raise ValueError(f"Profile file not found: {profile_path}")
52+
4453

4554
def generate_unique_dir_name() -> str:
4655
return datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

0 commit comments

Comments
 (0)