Beispiel #1
0
def template_tests(product=None):
    """
    Create a temporary directory with test cases parsed via jinja using
    product-specific context.
    """
    # Set up an empty temp directory
    tmpdir = tempfile.mkdtemp()

    # We want to remove the temporary directory on failure, but preserve
    # it on success. Wrap in a try/except block and reraise the original
    # exception after removing the temporary directory.
    try:
        # Load the product context we're executing under, if any.
        product_yaml = get_product_context(product)

        # Initialize a mock template_builder.
        empty = "/ssgts/empty/placeholder"
        template_builder = ssg.templates.Builder(product_yaml, empty,
                                                 _SHARED_TEMPLATES, empty,
                                                 empty)

        # Note that we're not exactly copying 1-for-1 the contents of the
        # directory structure into the temporary one. Instead we want a
        # flattened mapping with all rules in a single top-level directory
        # and all tests immediately contained within it. That is:
        #
        # /group_a/rule_a/tests/something.pass.sh -> /rule_a/something.pass.sh
        for dirpath, dirnames, _ in walk_through_benchmark_dirs(product):
            # Skip anything that isn't obviously a rule.
            if not is_rule_dir(dirpath):
                continue

            template_rule_tests(product, product_yaml, template_builder,
                                tmpdir, dirpath)
    except Exception as exp:
        shutil.rmtree(tmpdir, ignore_errors=True)
        raise exp

    return tmpdir
Beispiel #2
0
def iterate_over_rules(product=None):
    """Iterate over rule directories which have test scenarios".

    Returns:
        Named tuple Rule having these fields:
            directory -- absolute path to the rule "tests" subdirectory
                         containing the test scenarios in Bash
            id -- full rule id as it is present in datastream
            short_id -- short rule ID, the same as basename of the directory
                        containing the test scenarios in Bash
            files -- list of executable .sh files in the uploaded tarball
    """

    # Here we need to perform some magic to handle parsing the rule (from a
    # product perspective) and loading any templated tests. In particular,
    # identifying which tests to potentially run involves invoking the
    # templating engine.
    #
    # Begin by loading context about our execution environment, if any.
    product_yaml = get_product_context(product)

    # Initialize a mock template_builder.
    empty = "/ssgts/empty/placeholder"
    template_builder = ssg.templates.Builder(product_yaml, empty,
                                             _SHARED_TEMPLATES, empty, empty)

    for dirpath, dirnames, filenames in walk_through_benchmark_dirs(product):
        if is_rule_dir(dirpath):
            short_rule_id = os.path.basename(dirpath)

            # Load the rule itself to check for a template.
            rule, local_env_yaml = load_rule_and_env(dirpath, product_yaml,
                                                     product)
            template_name = None

            # Before we get too far, we wish to search the rule YAML to see if
            # it is applicable to the current product. If we have a product
            # and the rule isn't applicable for the product, there's no point
            # in continuing with the rest of the loading. This should speed up
            # the loading of the templated tests. Note that we've already
            # parsed the prodtype into local_env_yaml
            if product and local_env_yaml['products']:
                prodtypes = local_env_yaml['products']
                if "all" not in prodtypes and product not in prodtypes:
                    continue

            # All tests is a mapping from path (in the tarball) to contents
            # of the test case. This is necessary because later code (which
            # attempts to parse headers from the test case) don't have easy
            # access to templated content. By reading it and returning it
            # here, we can save later code from having to understand the
            # templating system.
            all_tests = dict()

            # Start by checking for templating tests and provision them if
            # present.
            if rule.template and rule.template['vars']:
                templated_tests = template_builder.get_all_tests(
                    rule.id_, rule.template, local_env_yaml)
                all_tests.update(templated_tests)
                template_name = rule.template['name']

            # Add additional tests from the local rule directory. Note that,
            # like the behavior in template_tests, this will overwrite any
            # templated tests with the same file name.
            tests_dir = os.path.join(dirpath, "tests")
            if os.path.exists(tests_dir):
                tests_dir_files = os.listdir(tests_dir)
                for test_case in tests_dir_files:
                    test_path = os.path.join(tests_dir, test_case)
                    if os.path.isdir(test_path):
                        continue

                    all_tests[test_case] = process_file_with_macros(
                        test_path, local_env_yaml)

            # Filter out everything except the shell test scenarios.
            # Other files in rule directories are editor swap files
            # or other content than a test case.
            allowed_scripts = filter(lambda x: x.endswith(".sh"), all_tests)
            content_mapping = {x: all_tests[x] for x in allowed_scripts}

            # Skip any rules that lack any content. This ensures that if we
            # end up with rules with a template lacking tests and without any
            # rule directory tests, we don't include the empty rule here.
            if not content_mapping:
                continue

            full_rule_id = OSCAP_RULE + short_rule_id
            result = Rule(directory=tests_dir,
                          id=full_rule_id,
                          short_id=short_rule_id,
                          files=content_mapping,
                          template=template_name)
            yield result
Beispiel #3
0
def template_tests(product=None):
    """
    Create a temporary directory with test cases parsed via jinja using
    product-specific context.
    """
    # Set up an empty temp directory
    tmpdir = tempfile.mkdtemp()

    # We want to remove the temporary directory on failure, but preserve
    # it on success. Wrap in a try/except block and reraise the original
    # exception after removing the temporary directory.
    try:
        # Load product's YAML file if present. This will allow us to parse
        # tests in the context of the product we're executing under.
        product_yaml = dict()
        if product:
            yaml_path = product_yaml_path(SSG_ROOT, product)
            product_yaml = load_product_yaml(yaml_path)

        # Below we could run into a DocumentationNotComplete error. However,
        # because the test suite isn't executed in the context of a particular
        # build (though, ideally it would be linked), we may not know exactly
        # whether the top-level rule/profile we're testing is actually
        # completed. Thus, forcibly set the required property to bypass this
        # error.
        product_yaml['cmake_build_type'] = 'Debug'

        # Note that we're not exactly copying 1-for-1 the contents of the
        # directory structure into the temporary one. Instead we want a
        # flattened mapping with all rules in a single top-level directory
        # and all tests immediately contained within it. That is:
        #
        # /group_a/rule_a/tests/something.pass.sh -> /rule_a/something.pass.sh
        for dirpath, dirnames, _ in walk_through_benchmark_dirs(product):
            # Skip anything that isn't obviously a rule.
            if "tests" not in dirnames or not is_rule_dir(dirpath):
                continue

            # Load rule content in our environment. We use this to satisfy
            # some implied properties that might be used in the test suite.
            rule_path = get_rule_dir_yaml(dirpath)
            rule = RuleYAML.from_yaml(rule_path, product_yaml)

            # Note that most places would check prodtype, but we don't care
            # about that here: if the rule is available to the product, we
            # load and parse it anyways as we have no knowledge of the
            # top-level profile or rule passed into the test suite.
            prodtypes = parse_prodtype(rule.prodtype)

            # Our local copy of env_yaml needs some properties from rule.yml
            # for completeness.
            local_env_yaml = dict()
            local_env_yaml.update(product_yaml)
            local_env_yaml['rule_id'] = rule.id_
            local_env_yaml['rule_title'] = rule.title
            local_env_yaml['products'] = prodtypes

            # Create the destination directory.
            dest_path = os.path.join(tmpdir, rule.id_)
            os.mkdir(dest_path)

            # Walk the test directory, writing all tests into the output
            # directory, recursively.
            tests_dir_path = os.path.join(dirpath, "tests")
            tests_dir_path = os.path.abspath(tests_dir_path)
            for dirpath, dirnames, filenames in os.walk(tests_dir_path):
                for dirname in dirnames:
                    # We want to recreate the correct path under the temporary
                    # directory. Resolve it to a relative path from the tests/
                    # directory.
                    dir_path = _rel_abs_path(os.path.join(dirpath, dirname),
                                             tests_dir_path)
                    assert '../' not in dir_path
                    tmp_dir_path = os.path.join(dest_path, dir_path)
                    os.mkdir(tmp_dir_path)

                for filename in filenames:
                    # We want to recreate the correct path under the temporary
                    # directory. Resolve it to a relative path from the tests/
                    # directory. Assumption: directories should be created
                    # prior to recursing into them, so we don't need to handle
                    # if a file's parent directory doesn't yet exist under the
                    # destination.
                    src_test_path = os.path.join(dirpath, filename)
                    rel_test_path = _rel_abs_path(src_test_path,
                                                  tests_dir_path)
                    dest_test_path = os.path.join(dest_path, rel_test_path)

                    # Rather than performing an OS-level copy, we need to
                    # first parse the test with jinja and then write it back
                    # out to the destination.
                    parsed_test = process_file(src_test_path, local_env_yaml)
                    with open(dest_test_path, 'w') as output_fp:
                        print(parsed_test, file=output_fp)

    except Exception as exp:
        shutil.rmtree(tmpdir, ignore_errors=True)
        raise exp

    return tmpdir