Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 128 additions & 39 deletions smart_tests/args4p/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,53 +264,54 @@ def error(msg: str) -> BadConfigException:
for c in self.commands:
c.check_consistency()

def _usage_line(self, program_name) -> str:
parts = ["Usage:"]

# Program name
parts.append(program_name)

# Build command path (for subcommands)
command_path: List[str] = []
current = self
while current.parent is not None:
command_path.insert(0, current.name)
current = current.parent
if len(command_path) > 0:
parts.append(" ".join(command_path))

# Add options placeholder if we have options
if self.options:
parts.append("[OPTIONS]")

# Add arguments
for a in self.arguments:
if a.required:
if a.multiple:
parts.append(f"<{a.metavar}>...")
else:
parts.append(f"<{a.metavar}>")
else:
if a.multiple:
parts.append(f"[{a.metavar}...]")
else:
parts.append(f"[{a.metavar}]")
if isinstance(self, Group):
if len(self.arguments) == 0:
# Add subcommand placeholder for groups
parts.append("COMMAND")
parts.append("...")

return " ".join(parts)

def format_help(self, program_name: str = os.path.basename(sys.argv[0])) -> str:
"""
Generate and return a formatted help message for this command.

:param program_name
Name of the program to display in the usage line. Defaults to the name of the running script.
"""
def usage_line() -> str:
parts = ["Usage:"]

# Program name
parts.append(program_name)

# Build command path (for subcommands)
command_path: List[str] = []
current = self
while current.parent is not None:
command_path.insert(0, current.name)
current = current.parent
if len(command_path) > 0:
parts.append(" ".join(command_path))

# Add options placeholder if we have options
if self.options:
parts.append("[OPTIONS]")

# Add arguments
for a in self.arguments:
if a.required:
if a.multiple:
parts.append(f"<{a.metavar}>...")
else:
parts.append(f"<{a.metavar}>")
else:
if a.multiple:
parts.append(f"[{a.metavar}...]")
else:
parts.append(f"[{a.metavar}]")
if isinstance(self, Group):
if len(self.arguments) == 0:
# Add subcommand placeholder for groups
parts.append("COMMAND")
parts.append("...")

return " ".join(parts)

lines = [usage_line()]
lines = [self._usage_line(program_name)]

# Description from docstring
if self.callback.__doc__:
Expand Down Expand Up @@ -416,6 +417,94 @@ def _format_options(self, caption: str, lines: list[str]):
if self.parent:
self.parent._format_options(f"Options (common to {self.parent.name})", lines)

def format_asciidoc_table(self, program_name: str) -> str:
"""
Generate an AsciiDoc table for the command's options and arguments.
Returns the table as a string.
"""
lines = [f"`{self._usage_line(program_name)}`", ""]

def _print_required(p: Parameter) -> str:
return "Yes" if p.required else "No"

if self.arguments:
lines.append("[cols=\"2,4,1\"]")
lines.append("|===")
lines.append("|Argument |Description |Required")
lines.append("")

for arg in self.arguments:
def _print_name() -> str:
if arg.multiple:
return f"`<{arg.metavar}>...`"
else:
return f"`<{arg.metavar}>`"

def _print_description() -> str:
desc_parts = []
if arg.help:
desc_parts.append(arg.help)

if arg.type != str:
type_name = getattr(arg.type, '__name__', str(arg.type))
desc_parts.append(f"(type: {type_name})")

if arg.default != NO_DEFAULT:
desc_parts.append(f"Default: `{arg.default}`")

return " ".join(desc_parts)

lines.append("// GENERATED. MODIFY IN CLI SOURCE CODE")
lines.append(f"|{_print_name()}")
lines.append(f"|{_print_description()}")
lines.append(f"|{_print_required(arg)}")
lines.append("")

lines.append("|===")

if self.options:
lines.append("[cols=\"2,4,1\"]")
lines.append("|===")
lines.append("|Option |Description |Required")
lines.append("")

for opt in sorted([opt for opt in self.options if not opt.hidden], key=lambda o: o.name):
def _print_name() -> str:
names = ", ".join([f"`{name}`" for name in opt.option_names])

# Add metavar for non-boolean options
if opt.type != bool:
if opt.metavar:
names += f" {opt.metavar}"
else:
type_name = getattr(opt.type, '__name__', str(opt.type))
names += f" {type_name.upper()}"

return names

def _print_description() -> str:
desc_parts = []
if opt.help:
desc_parts.append(opt.help)

if opt.default != NO_DEFAULT and opt.type != bool:
desc_parts.append(f"Default: `{opt.default}`")

if opt.multiple:
desc_parts.append("(can be specified multiple times)")

return " ".join(desc_parts)

lines.append("// GENERATED. MODIFY IN CLI SOURCE CODE")
lines.append(f"|{_print_name()}")
lines.append(f"|{_print_description()}")
lines.append(f"|{_print_required(opt)}")
lines.append("")

lines.append("|===")

return "\n".join(lines)

def __repr__(self):
return f"<Command name={self.name!r} options={self.options!r} arguments={self.arguments!r}>"

Expand Down
4 changes: 2 additions & 2 deletions smart_tests/commands/inspect/subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ def display(self):
def subset(
app: Application,
subset_id: Annotated[int, typer.Option(
help="subset id",
help="Subset id",
required=True
)],
json: Annotated[bool, typer.Option(
help="display JSON format"
help="Display JSON format"
)] = False,
):
is_json_format = json # Map parameter name
Expand Down
27 changes: 16 additions & 11 deletions smart_tests/commands/record/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,46 +35,51 @@ def build(
app: Application,
build_name: Annotated[str, typer.Option(
"--build",
help="build name",
metavar="BUILD_NAME",
help="Build name",
metavar="NAME",
required=True
)],
branch: Annotated[str | None, typer.Option(
"--branch",
help="Branch name. A branch is a set of test sessions grouped and this option value will be used for a lineage name."
help="Set branch name. A branch is a set of test sessions grouped and this option value will be used for a branch name.",
metavar="NAME"
)] = None,
repositories: Annotated[List[str], typer.Option(
"--repo-branch-map",
multiple=True,
help="Set repository name and branch name when you use --no-commit-collection option. "
"Please use the same repository name with a commit option"
"Please use the same repository name with a commit option",
metavar="REPO_NAME=BRANCH_NAME"
)] = [],
source: Annotated[List[str], typer.Option(
multiple=True,
help="path to local Git workspace, optionally prefixed by a label. "
help="Path to local Git workspace, optionally prefixed by a label. "
"like --source path/to/ws or --source main=path/to/ws",
metavar="REPO_NAME"
metavar="DIR"
)] = ["."],
max_days: Annotated[int, typer.Option(
help="the maximum number of days to collect commits retroactively"
help="The maximum number of days to collect commits retroactively",
metavar="DAYS"
)] = 30,
no_submodules: Annotated[bool, typer.Option(
help="stop collecting information from Git Submodules"
help="Stop collecting information from Git Submodules"
)] = False,
no_commit_collection: Annotated[bool, typer.Option(
help="do not collect commit data. "
help="Do not collect commit data. "
"This is useful if the repository is a shallow clone and the RevWalk is not "
"possible. The commit data must be collected with a separate fully-cloned "
"repository."
)] = False,
commits: Annotated[List[str], typer.Option(
"--commit",
multiple=True,
help="set repository name and commit hash when you use --no-commit-collection option"
help="Set repository name and commit hash when you use --no-commit-collection option",
metavar="REPO_NAME=COMMIT_HASH"
)] = [],
timestamp: Annotated[str | None, typer.Option(
help="Used to overwrite the build time when importing historical data. "
"Note: Format must be `YYYY-MM-DDThh:mm:ssTZD` or `YYYY-MM-DDThh:mm:ss` (local timezone applied)"
"Note: Format must be `YYYY-MM-DDThh:mm:ssTZD` or `YYYY-MM-DDThh:mm:ss` (local timezone applied)",
metavar="TIMESTAMP"
)] = None,
links: Annotated[List[KeyValue], typer.Option(
"--link",
Expand Down
12 changes: 8 additions & 4 deletions smart_tests/commands/record/commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,24 @@
def commit(
app: Application,
name: Annotated[str | None, typer.Option(
help="repository name"
help="Repository name",
metavar="NAME",
)] = None,
source: Annotated[str, typer.Option(
help="repository path"
help="Repository path",
metavar="DIR",
)] = os.getcwd(),
executable: Annotated[str, typer.Option(
help="[Obsolete] it was to specify how to perform commit collection but has been removed",
hidden=True
)] = "jar",
max_days: Annotated[int, typer.Option(
help="the maximum number of days to collect commits retroactively"
help="The maximum number of days to collect commits retroactively",
metavar="DAYS",
)] = 30,
import_git_log_output: Annotated[str | None, typer.Option(
help="import from the git-log output"
help="Import from the git-log output",
metavar="FILE",
)] = None,
):
if executable == 'docker':
Expand Down
14 changes: 9 additions & 5 deletions smart_tests/commands/record/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,32 @@ def session(
app: Application,
build_name: Annotated[str, typer.Option(
"--build",
help="build name",
help="Build name",
metavar="NAME",
required=True
)],
test_suite: Annotated[str, typer.Option(
"--test-suite",
help="Set test suite name. A test suite is a collection of test sessions. Setting a test suite allows you to "
"manage data over test sessions and lineages.",
metavar="NAME",
required=True
)],
flavors: Annotated[List[KeyValue], typer.Option(
"--flavor",
help="flavors",
help="Flavors",
multiple=True,
metavar="KEY=VALUE",
type=parse_key_value
)] = [],
is_observation: Annotated[bool, typer.Option(
"--observation",
help="enable observation mode"
help="Enable observation mode"
)] = False,
links: Annotated[List[KeyValue], typer.Option(
"--link",
help="Set external link of a title and url",
help="Set external link of title and url",
metavar="TITLE=URL",
multiple=True,
type=parse_key_value,
)] = [],
Expand All @@ -58,7 +61,8 @@ def session(
)] = False,
timestamp: Annotated[str | None, typer.Option(
help="Used to overwrite the session time when importing historical data. Note: Format must be "
"`YYYY-MM-DDThh:mm:ssTZD` or `YYYY-MM-DDThh:mm:ss` (local timezone applied)"
"`YYYY-MM-DDThh:mm:ssTZD` or `YYYY-MM-DDThh:mm:ss` (local timezone applied)",
metavar="TIMESTAMP"
)] = None,
):

Expand Down
3 changes: 2 additions & 1 deletion smart_tests/commands/record/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ def __init__(
hidden=True
)] = False,
group: Annotated[str | None, typer.Option(
help="Grouping name for test results"
help="Grouping name for test results",
metavar="NAME"
)] = "",
is_allow_test_before_build: Annotated[bool, typer.Option(
"--allow-test-before-build",
Expand Down
Loading
Loading