-
Notifications
You must be signed in to change notification settings - Fork 0
Remove BDD process-global state (#492) #580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,14 @@ use test_support::locale_stubs::{StubEnv, StubSystemLocale}; | |
| /// Tests that do not explicitly set up configuration or environment variables | ||
| /// may be affected by ambient host configuration. | ||
| pub(super) fn apply_cli(world: &TestWorld, args: &CliArgs) { | ||
| apply_cli_tokens(world, build_tokens(args.as_str())); | ||
| } | ||
|
|
||
| /// Apply parsed CLI argument tokens, storing the result or error in world state. | ||
| /// | ||
| /// This accepts fully formed arguments for scenarios whose temporary-resource | ||
| /// paths cannot be represented as static feature text. | ||
| pub(super) fn apply_cli_tokens(world: &TestWorld, mut tokens: Vec<std::ffi::OsString>) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '1,180p' tests/bdd/steps/cli.rs
printf '\n--- related directory-option uses ---\n'
rg -n -C 5 'directory|temp_dir|apply_cli_tokens|to_str' tests/bdd/steps/cli.rs tests/bddRepository: leynos/netsuke Length of output: 50370 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- CLI directory option definitions and parser entry points ---'
rg -n -C 8 'directory|parse_with_localizer_from|Arg::new\("C"|short\('\''C'\''' src tests/bdd --glob '*.rs'
printf '%s\n' '--- configuration-discovery scenarios and attached-option coverage ---'
rg -n -C 8 --glob '*.feature' --glob '*.rs' -- '--directory=|-C[^[:space:]]|--directory| -C' features tests src
printf '%s\n' '--- repository platform and dependency context ---'
rg -n -C 3 'edition\s*=|target_os|clap' Cargo.toml Cargo.lock .github 2>/dev/null | head -160Repository: leynos/netsuke Length of output: 50373 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- parser directory definition ---'
sed -n '75,135p' src/cli/parser.rs
printf '%s\n' '--- exact attached and separate directory references ---'
rg -n -C 4 --glob '*.rs' --glob '*.feature' --glob '*.md' -- '--directory=|-C[[:alnum:]_/.-]+|--directory([[:space:]]|$)|[[:space:]]-C([[:space:]]|$)' . | head -240
printf '%s\n' '--- configuration discovery BDD scenarios ---'
sed -n '1,230p' tests/bdd/steps/configuration_discovery.rsRepository: leynos/netsuke Length of output: 25874 🌐 Web query:
💡 Result: In the clap Rust crate, the handling of duplicate options, argument values, and non-UTF-8 OsString types works as follows: Duplicate Option Behaviour: If you define multiple arguments with the same short or long option flag using the derive interface, clap will panic at runtime in debug mode to warn you of the conflict [1]. The derive macros do not currently produce a compile-time error for duplicate flags [1]. It is recommended to run a test that calls Command::debug_assert to catch these issues in your CI/CD pipeline [2]. Parsing OsString and Non-UTF-8 Values: By default, clap often attempts to parse arguments as standard strings [3]. To handle paths or values that may contain non-UTF-8 sequences, you must explicitly tell clap to use OsString parsing [3]. In the derive interface, you can achieve this by using the value_parser attribute with the from_os_str helper [3]: #[arg(short = 'C', long = "directory", value_parser = clap::value_parser!(std::ffi::OsString))] path: std::ffi::OsString, This ensures that the input is captured as an OsString, which is natively capable of representing non-UTF-8 sequences as they exist on the host operating system [4][5]. General Usage Note: When using attributes like #[arg(short = 'C', long = "directory")] on a field, clap maps these to the underlying Command builder configuration [2][6]. If you provide both an attached value (e.g., -Cpath) and a long option (e.g., --directory=path), these are treated as defined by the argument's configuration. If the same argument is intended to support both forms, simply specifying both short and long in the #[arg(...)] attribute is sufficient [6]. Clap will handle the parsing of both -C value and --directory=value automatically [6]. Citations:
🌐 Web query:
💡 Result: In the clap crate, using ArgAction::Set (or other default actions) on an argument that is provided multiple times results in an ArgumentConflict error because clap, by default, expects an argument to be used only once [1][2]. To resolve this, you have two primary options depending on your desired behavior: 1. Allow the argument to be used multiple times and override previous values (the last one wins): You can enable this behavior by setting.args_override_self(true) on your Command definition [3]. Example using the derive API: #[derive(Parser)] #[command(args_override_self = true)] struct Args { #[arg(short, long)] flag: String, } 2. Allow the argument to be used multiple times and collect all provided values: If you want to keep all occurrences rather than overriding, use ArgAction::Append instead of ArgAction::Set [2][4]. Example using the builder API: Arg::new("flag").long("flag").action(clap::ArgAction::Append) When using ArgAction::Append, clap will store all provided values for that argument in a list, which you can retrieve using get_many [2][4]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
source = Path("tests/bdd/steps/cli.rs").read_text()
assert 't.to_str().is_some_and' in source
assert 't.starts_with("-C")' in source
assert 't.starts_with("--directory=")' in source
assert 'args_override_self' not in Path("src/cli/parser.rs").read_text()
def current_is_directory_flag(token: bytes) -> bool:
try:
value = token.decode("utf-8")
except UnicodeDecodeError:
return False
return (
value == "-C"
or value.startswith("-C")
or value == "--directory"
or value.startswith("--directory=")
)
cases = {
b"-Cworkspace": True,
b"--directory=workspace": True,
b"-C\xff": False,
b"--directory=\xff": False,
}
for token, expected in cases.items():
actual = current_is_directory_flag(token)
print(f"{token!r}: {actual}")
assert actual == expected
for token in (b"-C\xff", b"--directory=\xff"):
tokens = [b"netsuke", token]
if not any(current_is_directory_flag(item) for item in tokens):
tokens[1:1] = [b"-C", b"/tmp/injected"]
print("injected:", tokens)
assert tokens[1:3] == [b"-C", b"/tmp/injected"]
assert token in tokens[3:]
print("current scan reproduces the duplicate-directory setup for both attached forms")
PYRepository: leynos/netsuke Length of output: 240 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
source = Path("tests/bdd/steps/cli.rs").read_text()
parser = Path("src/cli/parser.rs").read_text()
assert 't.to_str().is_some_and' in source
assert 's.starts_with("-C")' in source
assert 's.starts_with("--directory=")' in source
assert 'args_override_self' not in parser
def current_is_directory_flag(token: bytes) -> bool:
try:
value = token.decode("utf-8")
except UnicodeDecodeError:
return False
return (
value == "-C"
or value.startswith("-C")
or value == "--directory"
or value.startswith("--directory=")
)
cases = {
b"-Cworkspace": True,
b"--directory=workspace": True,
b"-C\xff": False,
b"--directory=\xff": False,
}
for token, expected in cases.items():
actual = current_is_directory_flag(token)
print(f"{token!r}: {actual}")
assert actual == expected
for token in (b"-C\xff", b"--directory=\xff"):
tokens = [b"netsuke", token]
if not any(current_is_directory_flag(item) for item in tokens):
tokens[1:1] = [b"-C", b"/tmp/injected"]
print("injected:", tokens)
assert tokens[1:3] == [b"-C", b"/tmp/injected"]
assert token in tokens[3:]
print("current scan reproduces the duplicate-directory setup for both attached forms")
PYRepository: leynos/netsuke Length of output: 454 Detect directory flags without requiring UTF-8.
🤖 Prompt for AI Agents |
||
| let env = world | ||
| .locale_env | ||
| .get() | ||
|
|
@@ -44,7 +52,6 @@ pub(super) fn apply_cli(world: &TestWorld, args: &CliArgs) { | |
|
|
||
| // If there's a temp_dir set and the args don't already contain an | ||
| // explicit -C or --directory flag, prepend -C <temp_dir> for config discovery. | ||
| let mut tokens = build_tokens(args.as_str()); | ||
| if let Some(temp_dir) = world.temp_dir.borrow().as_ref() { | ||
| let is_directory_flag = |t: &std::ffi::OsString| { | ||
| t.to_str().is_some_and(|s| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Make the missing selector unique and assert the missing-file failure.
Generate the selector name from the temporary directory name. Assert the expected
missing-file error. The fixed filename can exist in the Cargo process working
directory. A malformed colliding file makes
first_error().is_some()pass evenwhen discovery incorrectly loads from the process working directory.
🤖 Prompt for AI Agents
Source: Coding guidelines