diff --git a/README.rst b/README.rst index 44d84f5..f6c2b24 100644 --- a/README.rst +++ b/README.rst @@ -40,8 +40,8 @@ Usage $ robot2rst --help usage: robot2rst [-h] -i ROBOT_FILE -o RST_FILE [--only EXPRESSION] [-p PREFIX] - [-r [RELATIONSHIPS [RELATIONSHIPS ...]]] [-t [TAGS [TAGS ...]]] - [--type TYPE] [--trim-suffix] + [-r [RELATIONSHIPS ...]] [-t [TAGS ...]] [--include [INCLUDE ...]] + [-c [COVERAGE ...]] [--type TYPE] [--trim-suffix] Convert robot test cases to reStructuredText with traceable items. @@ -51,18 +51,23 @@ Usage Input robot file -o RST_FILE, --rst RST_FILE Output RST file, e.g. my_component_qtp.rst - --only EXPRESSION Expression of tags for Sphinx' `only` directive that surrounds all RST - content. By default, no `only` directive is generated. + --only EXPRESSION Expression of tags for Sphinx' `only` directive that surrounds all + RST content. By default, no `only` directive is generated. -p PREFIX, --prefix PREFIX Overrides the default 'QTEST-' prefix. -r [RELATIONSHIPS ...], --relationships [RELATIONSHIPS ...] Name(s) of the relationship(s) used to link to items in Tags section. The default value is 'validates'. -t [TAGS ...], --tags [TAGS ...] - Regex(es) for matching tags to add a relationship link for. All tags - get matched by default. + Zero or more Python regexes for matching tags to treat them as + traceable targets via a relationship. All tags get matched by + default. + --include [INCLUDE ...] + Zero or more Python regexes for matching tags to filter test cases. + If every regex matches at least one of a test case's tags, the test + case is included. -c [COVERAGE ...], --coverage [COVERAGE ...] - Minumum coverage percentages for the item-matrix(es); 1 value per tag + Minimum coverage percentages for the item-matrix(es); 1 value per tag in -t, --tags. --type TYPE Give value that starts with 'q' or 'i' (case-insensitive) to explicitly define the type of test: qualification/integration test. diff --git a/mlx/robot2rst.py b/mlx/robot2rst.py index 57bfa6b..b973cf4 100644 --- a/mlx/robot2rst.py +++ b/mlx/robot2rst.py @@ -95,10 +95,13 @@ def main(): help="Name(s) of the relationship(s) used to link to items in Tags section. The default value " "is 'validates'.") parser.add_argument("-t", "--tags", nargs='*', - help="Regex(es) for matching tags to add a relationship link for. All tags get matched by " - "default.") + help="Zero or more Python regexes for matching tags to treat them as traceable targets via a " + "relationship. All tags get matched by default.") + parser.add_argument("--include", nargs='*', default=[], + help="Zero or more Python regexes for matching tags to filter test cases. " + "If every regex matches at least one of a test case's tags, the test case is included.") parser.add_argument("-c", "--coverage", nargs='*', - help="Minumum coverage percentages for the item-matrix(es); 1 value per tag in -t, --tags.") + help="Minimum coverage percentages for the item-matrix(es); 1 value per tag in -t, --tags.") parser.add_argument("--type", default='q', help="Give value that starts with 'q' or 'i' (case-insensitive) to explicitly define " "the type of test: qualification/integration test. The default is 'qualification'.") @@ -137,10 +140,18 @@ def main(): f"percentages ({len(coverages)}).") relationship_config = [(relationships[i], tag_regexes[i], coverages[i]) for i in range(len(relationships))] - parser = ParserApplication(Path(args.robot_file)) + parser = ParserApplication(Path(args.robot_file), args.include) parser.run() - return generate_robot_2_rst(parser, Path(args.rst_file), prefix, relationship_config, - gen_matrix, test_type=test_type, only=args.expression, coverages=coverages) + if parser.tests: + exit_code = generate_robot_2_rst(parser, Path(args.rst_file), prefix, relationship_config, + gen_matrix, test_type=test_type, only=args.expression, coverages=coverages) + else: + msg = f"robot2rst did not generate {args.rst_file} because {args.robot_file} does not contain any test cases" + if args.include: + msg += f" with tags matching all regexes {args.include}" + LOGGER.info(msg) + exit_code = 0 + return exit_code def entrypoint(): diff --git a/mlx/robot_parser.py b/mlx/robot_parser.py index 2dc061a..5d49040 100644 --- a/mlx/robot_parser.py +++ b/mlx/robot_parser.py @@ -16,17 +16,20 @@ class ParserApplication(ModelVisitor): """ TestAttributes = namedtuple('TestAttributes', 'name doc tags') - def __init__(self, robot_file, *args, **kwargs): + def __init__(self, robot_file, tags_for_inclusion, *args, **kwargs): """ Constructor Args: robot_file (Path): Path to the .robot file. + tags_for_inclusion (list): Regexes for matching tags. A test case is included if every regex matches at + least one of its tags. """ super().__init__(*args, **kwargs) self.robot_file = str(robot_file.resolve(strict=True)) self.model = get_model(self.robot_file) self.tests = [] self.variables = {} + self.tags_for_inclusion = tags_for_inclusion def run(self): self.visit(self.model) @@ -63,5 +66,15 @@ def visit_TestCase(self, node): previous_token = token elif element_type == Token.TAGS: tags = [el.value for el in element.tokens if el.type == Token.ARGUMENT] + if self.evaluate_inclusion(tags): + self.tests.append(self.TestAttributes(node.name, doc, tags)) - self.tests.append(self.TestAttributes(node.name, doc, tags)) + def evaluate_inclusion(self, tags): + for pattern in self.tags_for_inclusion: + regexp = re.compile(pattern) + for tag in tags: + if regexp.match(tag): + break + else: + return False + return True