Exemplo n.º 1
0
def full():
    """ More complex metadata structure with inheritance """
    data = """
        duration: 5m
        enabled: true
        require: [one]
        adjust:
          - enabled: false
            when: distro = centos
            because: the feature is not there yet
            continue: false
          - duration: 1m
            when: arch = ppc64
            because: they are super fast
            continue: true
          - require+: [two]
            when: distro = fedora

        /inherit:
            summary: This test just inherits all rules.

        /define:
            summary: This test defines its own rules.
            adjust:
                recommend: custom-package
                when: distro = fedora

        /extend:
            summary: This test extends parent rules.
            adjust+:
              - require+: [three]
                when: distro = fedora
        """
    return fmf.Tree(yaml.safe_load(data))
Exemplo n.º 2
0
def init(path, mini, full, force):
    """ Initialize the tree root. """

    # Initialize the FMF metadata tree root
    try:
        tree = fmf.Tree(path)
        echo("Tree root '{}' already exists.".format(tree.root))
    except fmf.utils.RootError:
        try:
            root = fmf.Tree.init(path)
        except fmf.utils.GeneralError as error:
            raise tmt.utils.GeneralError(
                "Failed to initialize tree root in '{}': {}".format(
                    path, error))
        echo("Tree root '{}' initialized.".format(root))

    # Populate the tree with example objects if requested
    if mini:
        tmt.Test.create('/tests/example', 'shell', force)
        tmt.Plan.create('/plans/example', 'mini', force)
        tmt.Story.create('/stories/example', 'mini', force)
    if full:
        tmt.Test.create('/tests/example', 'shell', force)
        tmt.Plan.create('/plans/example', 'full', force)
        tmt.Story.create('/stories/example', 'full', force)

    return 'init'
Exemplo n.º 3
0
Arquivo: base.py Projeto: msrb/tmt
    def __init__(self, id_=None, tree=None, context=None):
        """ Initialize tree, workdir and plans """
        # Use the last run id if requested
        self.config = tmt.utils.Config()
        if context.params.get('last'):
            id_ = self.config.last_run()
            if id_ is None:
                raise tmt.utils.GeneralError(
                    "No last run id found. Have you executed any run?")
        super().__init__(workdir=id_ or True, context=context)
        # Store workdir as the last run id
        self.config.last_run(self.workdir)
        default_plan = tmt.utils.yaml_to_dict(tmt.templates.DEFAULT_PLAN)
        # Save the tree
        try:
            self.tree = tree if tree else tmt.Tree('.')
            self.debug(f"Using tree '{self.tree.root}'.")
            # Insert default plan if no plan detected
            if not list(self.tree.tree.prune(keys=['execute'])):
                self.tree.tree.update(default_plan)
                self.debug(f"No plan found, adding the default plan.")
        # Create an empty default plan if no fmf metadata found
        except tmt.utils.MetadataError:
            # The default discover method for this case is 'shell'
            default_plan['/plans/default']['discover']['how'] = 'shell'
            self.tree = tmt.Tree(tree=fmf.Tree(default_plan))
            self.debug(f"No metadata found, using the default plan.")

        self._plans = None
        self._environment = dict()
        self.remove = self.opt('remove')
Exemplo n.º 4
0
Arquivo: cli.py Projeto: thrix/fmf
    def show(self, brief=False):
        """ Show metadata for each path given """
        output = []
        for path in self.options.paths or ["."]:
            if self.options.verbose:
                utils.info("Checking {0} for metadata.".format(path))
            tree = fmf.Tree(path)
            for node in tree.prune(self.options.whole, self.options.keys,
                                   self.options.names, self.options.filters,
                                   self.options.conditions):
                if brief:
                    show = node.show(brief=True)
                else:
                    show = node.show(brief=False,
                                     formatting=self.options.formatting,
                                     values=self.options.values)
                # List source files when in debug mode
                if self.options.debug:
                    for source in node.sources:
                        show += utils.color("{0}\n".format(source), "blue")
                if show is not None:
                    output.append(show)

        # Print output and summary
        if brief or self.options.formatting:
            joined = "".join(output)
        else:
            joined = "\n".join(output)
        print(joined, end="")
        if self.options.verbose:
            utils.info("Found {0}.".format(utils.listed(len(output),
                                                        "object")))
        self.output = joined
Exemplo n.º 5
0
def main(cmdline=None):
    """ Parse options, gather metadata, print requested data """

    # Parse command line arguments
    options, arguments = Options().parse(cmdline)
    if not arguments:
        arguments = ["."]
    output = ""

    # Enable debugging output
    if options.debug:
        utils.log.setLevel(utils.LOG_DEBUG)

    # Show metadata for each path given
    counter = 0
    for path in arguments:
        if options.verbose:
            utils.info("Checking {0} for metadata.".format(path))
        tree = fmf.Tree(path)
        for node in tree.prune(options.whole, options.keys, options.names,
                               options.filters):
            show = node.show(options.brief, options.formatting, options.values)
            if show is not None:
                print(show, end="")
                output += show
                counter += 1
    # Print summary
    if options.verbose:
        utils.info("Found {0}.".format(utils.listed(counter, "object")))
    return output
Exemplo n.º 6
0
def mini():
    """ Simple tree node with minimum set of attributes """
    data = """
        enabled: true
        adjust:
            enabled: false
            when: distro = centos
        """
    return fmf.Tree(yaml.safe_load(data))
Exemplo n.º 7
0
Arquivo: base.py Projeto: rasibley/tmt
 def tree(self):
     """ Initialize tree only when accessed """
     if self._tree is None:
         try:
             self._tree = fmf.Tree(self._path)
         except fmf.utils.RootError:
             raise tmt.utils.GeneralError(
                 "No metadata found in the '{0}' directory.".format(
                     self._path))
     return self._tree
Exemplo n.º 8
0
 def tree(self):
     """ Initialize tree only when accessed """
     if self._tree is None:
         try:
             self._tree = fmf.Tree(self._path)
         except fmf.utils.RootError:
             raise tmt.utils.MetadataError(
                 f"No metadata found in the '{self._path}' directory. "
                 f"Use 'tmt init' to get started.")
         except fmf.utils.FileError as error:
             raise tmt.utils.GeneralError(f"Invalid yaml syntax: {error}")
     return self._tree
Exemplo n.º 9
0
    def fetch(self):
        """ Fetch the library (unless already fetched) """
        # Initialize library cache (indexed by the repository name)
        if not hasattr(self.parent, '_library_cache'):
            self.parent._library_cache = dict()

        # Check if the library was already fetched
        try:
            library = self.parent._library_cache[self.repo]
            if library.url != self.url:
                raise tmt.utils.GeneralError(
                    f"Library '{self.repo}' with url '{self.url}' conflicts "
                    f"with already fetched library from '{library.url}'.")
            if library.ref != self.ref:
                raise tmt.utils.GeneralError(
                    f"Library '{self.repo}' using ref '{self.ref}' conflicts "
                    f"with already fetched library using ref '{library.ref}'.")
            self.parent.debug(f"Library '{self}' already fetched.", level=3)
            # Reuse the existing metadata tree
            self.tree = library.tree
        # Fetch the library and add it to the index
        except KeyError:
            self.parent.debug(f"Fetch library '{self}'.", level=3)
            # Prepare path, clone the repository, checkout ref
            directory = os.path.join(
                self.parent.workdir, self.dest, self.repo)
            # Clone repo with disabled prompt to ignore missing/private repos
            try:
                self.parent.run(
                    ['git', 'clone', self.url, directory],
                    shell=False, env={"GIT_TERMINAL_PROMPT": "0"})
            except tmt.utils.RunError as error:
                # Fallback to install during the prepare step if in rpm format
                if self.format == 'rpm':
                    raise LibraryError
                self.parent.info(
                    f"Failed to fetch library '{self}' from '{self.url}'.",
                    color='red')
                raise
            self.parent.run(
                ['git', 'checkout', self.ref], shell=False, cwd=directory)
            # Initialize metadata tree, add self into the library index
            self.tree = fmf.Tree(directory)
            self.parent._library_cache[self.repo] = self

        # Get the library node, check require and recommend
        library = self.tree.find(self.name)
        if not library:
            raise tmt.utils.GeneralError(
                f"Library '{self.name}' not found in '{self.repo}'.")
        self.require = tmt.utils.listify(library.get('require', []))
        self.recommend = tmt.utils.listify(library.get('recommend', []))
Exemplo n.º 10
0
def test_convert():
    """ Convert old metadata """
    tmp = tempfile.mkdtemp()
    os.system('cp -a {} {}'.format(CONVERT, tmp))
    path = os.path.join(tmp, 'convert')
    command = 'test convert --no-nitrate {}'.format(path)
    result = runner.invoke(tmt.cli.main, command.split())
    assert result.exit_code == 0
    assert 'Metadata successfully stored' in result.output
    assert 'This is really that simple' in result.output
    node = fmf.Tree(path).find('/')
    assert node.get('summary') == 'Simple smoke test'
    assert node.get('component') == ['tmt']
    assert 'This is really that simple.' in node.get('description')
    shutil.rmtree(tmp)
Exemplo n.º 11
0
Arquivo: base.py Projeto: jscotka/tmt
 def _save_tree(self, tree):
     """ Save metadata tree, handle the default plan """
     default_plan = tmt.utils.yaml_to_dict(tmt.templates.DEFAULT_PLAN)
     try:
         self.tree = tree if tree else tmt.Tree('.')
         self.debug(f"Using tree '{self.tree.root}'.")
         # Insert default plan if no plan detected
         if not list(self.tree.tree.prune(keys=['execute'])):
             self.tree.tree.update(default_plan)
             self.debug(f"No plan found, adding the default plan.")
     # Create an empty default plan if no fmf metadata found
     except tmt.utils.MetadataError:
         # The default discover method for this case is 'shell'
         default_plan['/plans/default']['discover']['how'] = 'shell'
         self.tree = tmt.Tree(tree=fmf.Tree(default_plan))
         self.debug(f"No metadata found, using the default plan.")
Exemplo n.º 12
0
    def __init__(self, data, name=None):
        """
        Initialize test data from an fmf node or a dictionary

        The following two methods are supported:

            Test(node)
            Test(dictionary, name)

        Test name is required when initializing from a dictionary.
        """

        # Create a simple test node if dictionary given
        if isinstance(data, dict):
            if name is None:
                raise tmt.utils.GeneralError(
                    'Name required to initialize test.')
            elif not name.startswith('/'):
                raise tmt.utils.SpecificationError(
                    "Test name should start with a '/'.")
            node = fmf.Tree(data)
            node.name = name
        else:
            node = data
        super().__init__(node)

        # Set all supported attributes
        for key in self._keys:
            setattr(self, key, self.node.get(key))

        # Path defaults to the node name
        self._check('path', expected=str, default=self.name)

        # Check that lists are lists or strings, listify if needed
        for key in ['component', 'contact', 'require', 'recommend', 'tag']:
            self._check(key, expected=(list, str), default=[], listify=True)

        # Check that environment is a dictionary
        self._check('environment', expected=dict, default={})
        self.environment = dict([(key, str(value))
                                 for key, value in self.environment.items()])

        # Default duration, manual, enabled and result
        self._check('duration', expected=str, default=DEFAULT_TEST_DURATION)
        self._check('manual', expected=bool, default=False)
        self._check('enabled', expected=bool, default=True)
        self._check('result', expected=str, default='respect')
Exemplo n.º 13
0
    def go(self):
        """ Discover available tests """
        super(DiscoverShell, self).go()
        tests = fmf.Tree(dict(summary='tests'))

        # Check and process each defined shell test
        for data in self.data['tests']:
            # Create data copy (we want to keep original data for save()
            data = copy.deepcopy(data)
            # Extract name, make sure it is present
            try:
                name = data.pop('name')
            except KeyError:
                raise tmt.utils.SpecificationError(
                    f"Missing test name in '{self.step.plan.name}'.")
            # Make sure that the test script is defined
            if 'test' not in data:
                raise tmt.utils.SpecificationError(
                    f"Missing test script in '{self.step.plan.name}'.")
            # Prepare path to the test working directory (tree root by default)
            try:
                data['path'] = f"/tests{data['path']}"
            except KeyError:
                data['path'] = f"/tests"
            # Apply default test duration unless provided
            if 'duration' not in data:
                data['duration'] = tmt.base.DEFAULT_TEST_DURATION_L2

            # Create a simple fmf node, adjust its name
            tests.child(name, data)

        # Copy directory tree (if defined) to the workdir
        directory = self.step.plan.my_run.tree.root
        testdir = os.path.join(self.workdir, 'tests')
        if directory:
            self.info('directory', directory, 'green')
            self.debug("Copy '{}' to '{}'.".format(directory, testdir))
            shutil.copytree(directory, testdir, symlinks=True)
        else:
            os.makedirs(testdir)

        # Use a tmt.Tree to apply possible command line filters
        tests = tmt.Tree(tree=tests).tests(conditions=["manual is False"])
        self._tests = tests
Exemplo n.º 14
0
Arquivo: cli.py Projeto: jscotka/fmf
def main(cmdline=None):
    """ Parse options, gather metadata, print requested data """

    # Parse command line arguments
    options, arguments = Options().parse(cmdline)
    if not arguments:
        arguments = ["."]
    output = ""

    # Enable debugging output
    if options.debug:
        utils.log.setLevel(utils.LOG_DEBUG)

    # Show metadata for each path given
    counter = 0
    for path in arguments:
        if options.verbose:
            utils.info("Checking {0} for metadata.".format(path))
        tree = fmf.Tree(path)
        for node in tree.climb(options.whole):
            # Select only nodes with key content
            if not all([key in node.data for key in options.keys]):
                continue
            # Select nodes with name matching regular expression
            if options.names and not any(
                    [re.search(name, node.name) for name in options.names]):
                continue
            # Apply advanced filters if given
            try:
                if not all([utils.filter(filter, node.data)
                        for filter in options.filters]):
                    continue
            # Handle missing attribute as if filter failed
            except utils.FilterError:
                continue
            show = node.show(brief=options.brief)
            print(show)
            output += show + "\n"
            counter += 1
    # Print summary
    if options.verbose:
        utils.info("Found {0}.".format(utils.listed(counter, "object")))
    return output
Exemplo n.º 15
0
Arquivo: shell.py Projeto: psss/tmt
    def go(self):
        """ Discover available tests """
        super(DiscoverShell, self).go()
        tests = fmf.Tree(dict(summary='tests'))

        # Check and process each defined shell test
        for data in self.data['tests']:
            # Create data copy (we want to keep original data for save()
            data = copy.deepcopy(data)
            # Extract name, make sure it is present
            try:
                name = data.pop('name')
            except KeyError:
                raise tmt.utils.SpecificationError(
                    f"Missing test name in '{self.step.plan.name}'.")
            # Make sure that the test script is defined
            if 'test' not in data:
                raise tmt.utils.SpecificationError(
                    f"Missing test script in '{self.step.plan.name}'.")
            # Prepare path to the test working directory (tree root by default)
            try:
                data['path'] = f"/tests{data['path']}"
            except KeyError:
                data['path'] = f"/tests"
            # Apply default test duration unless provided
            if 'duration' not in data:
                data['duration'] = tmt.base.DEFAULT_TEST_DURATION_L2

            # Create a simple fmf node, adjust its name
            tests.child(name, data)

        # Symlink tests directory to the plan work tree
        testdir = os.path.join(self.workdir, "tests")
        relative_path = os.path.relpath(self.step.plan.worktree, self.workdir)
        os.symlink(relative_path, testdir)

        # Use a tmt.Tree to apply possible command line filters
        tests = tmt.Tree(tree=tests).tests(conditions=["manual is False"])
        self._tests = tests
Exemplo n.º 16
0
 def go(self):
     """ Discover available tests """
     super(DiscoverShell, self).go()
     tests = fmf.Tree(dict(summary='tests'))
     path = False
     for data in self.data['tests']:
         # Extract name, make sure it is present
         try:
             name = data.pop('name')
         except KeyError:
             raise tmt.utils.SpecificationError(
                 f"Missing test name in '{self.step.plan.name}'.")
         # Make sure that the test script is defined
         if 'test' not in data:
             raise tmt.utils.SpecificationError(
                 f"Missing test script in '{self.step.plan.name}'.")
         # Adjust path if necessary (defaults to '.')
         try:
             data['path'] = f"/{self.name}/tests{data['path']}"
             path = True
         except KeyError:
             data['path'] = '.'
         # Create a simple fmf node, adjust its name
         tests.child(name, data)
     # Copy current directory to workdir only if any path specified
     if path:
         directory = self.step.plan.run.tree.root
         testdir = os.path.join(self.workdir, 'tests')
         self.info('directory', directory, 'green')
         self.debug("Copy '{}' to '{}'.".format(directory, testdir))
         shutil.copytree(directory, testdir)
     # Use a tmt.Tree to apply possible command line filters
     tests = tmt.Tree(tree=tests).tests()
     # Summary of selected tests, test list in verbose mode
     summary = fmf.utils.listed(len(tests), 'test') + ' selected'
     self.info('tests', summary, 'green')
     for test in tests:
         self.verbose(test.name, color='red', shift=1)
     self._tests = tests
Exemplo n.º 17
0
Arquivo: cli.py Projeto: happz/tmt
def import_(context, paths, makefile, nitrate, purpose, disabled, **kwargs):
    """
    Import old test metadata into the new fmf format.

    Accepts one or more directories where old metadata are stored.
    By default all available sources and current directory are used.
    The following test metadata are converted for each source:

    \b
    makefile ..... summary, component, duration, require
    purpose ...... description
    nitrate ...... contact, component, tag,
                   environment, relevancy, enabled
    """
    tmt.Test._save_context(context)
    if not paths:
        paths = ['.']
    for path in paths:
        # Make sure we've got a real directory
        path = os.path.realpath(path)
        if not os.path.isdir(path):
            raise tmt.utils.GeneralError(
                "Path '{0}' is not a directory.".format(path))
        # Gather old metadata and store them as fmf
        common, individual = tmt.convert.read(path, makefile, nitrate, purpose,
                                              disabled)
        # Add path to common metadata if there are virtual test cases
        if individual:
            root = fmf.Tree(path).root
            common['path'] = os.path.join('/', os.path.relpath(path, root))
        # Store common metadata
        common_path = os.path.join(path, 'main.fmf')
        tmt.convert.write(common_path, common)
        # Store individual data (as virtual tests)
        for testcase in individual:
            testcase_path = os.path.join(
                path,
                str(testcase['extra-nitrate']) + '.fmf')
            tmt.convert.write(testcase_path, testcase)
Exemplo n.º 18
0
#!/usr/bin/python

import fmf

# Browse directory with wget example metadata
for node in fmf.Tree("../wget").climb():
    try:
        # List nodes with "test" attribute defined and "Tier2" in tags
        if "test" in node.data and "Tier2" in node.data["tags"]:
            print(node.show())
    except KeyError:
        pass
Exemplo n.º 19
0
Arquivo: base.py Projeto: jscotka/tmt
    def __init__(self, data, name=None):
        """
        Initialize test data from an fmf node or a dictionary

        The following two methods are supported:

            Test(node)
            Test(dictionary, name)

        Test name is required when initializing from a dictionary.
        """

        # Create a simple test node if dictionary given
        if isinstance(data, dict):
            if name is None:
                raise tmt.utils.GeneralError(
                    'Name required to initialize test.')
            elif not name.startswith('/'):
                raise tmt.utils.SpecificationError(
                    "Test name should start with a '/'.")
            node = fmf.Tree(data)
            node.name = name
        else:
            node = data
        super().__init__(node)

        # Set all supported attributes
        for key in self._keys:
            setattr(self, key, self.node.get(key))

        # Path defaults to the directory where metadata are stored or to
        # the root '/' if fmf metadata were not stored on the filesystem
        try:
            directory = os.path.dirname(self.node.sources[-1])
            relative_path = os.path.relpath(directory, self.node.root)
            if relative_path == '.':
                default_path = '/'
            else:
                default_path = os.path.join('/', relative_path)
        except (AttributeError, IndexError):
            default_path = '/'
        self._check('path', expected=str, default=default_path)

        # Check that lists are lists or strings, listify if needed
        for key in ['component', 'contact', 'require', 'recommend', 'tag']:
            self._check(key, expected=(list, str), default=[], listify=True)

        # FIXME Framework should default to 'shell' in the future. For
        # backward-compatibility with the old execute methods we need to be
        # able to detect if the test has explicitly set the framework.
        self._check('framework', expected=str, default=None)
        if self.framework == 'beakerlib':
            self.require.append('beakerlib')

        # Check that environment is a dictionary
        self._check('environment', expected=dict, default={})
        self.environment = dict([(key, str(value))
                                 for key, value in self.environment.items()])

        # Default duration, manual, enabled and result
        self._check('duration', expected=str, default=DEFAULT_TEST_DURATION_L1)
        self._check('manual', expected=bool, default=False)
        self._check('enabled', expected=bool, default=True)
        self._check('result', expected=str, default='respect')

        # Store original metadata with applied defaults and including
        # keys which are not defined in the L1 metadata specification
        self._metadata = self.node.data.copy()
        self._metadata.update(self.export(format_='dict'))
        self._metadata['name'] = self.name
Exemplo n.º 20
0
Arquivo: base.py Projeto: hroncok/tmt
 def __init__(self, path='.'):
     """ Initialize testsets for given directory path """
     self.tree = fmf.Tree(path)
     self.testsets = [Testset(testset) for testset in self.tree.climb()]
Exemplo n.º 21
0
    def go(self):
        """ Discover available tests """
        super(DiscoverFmf, self).go()

        # Check url and path, prepare test directory
        url = self.get('url')
        path = self.get('path')
        testdir = os.path.join(self.workdir, 'tests')
        dist_git_source = self.get('dist-git-source', False)

        # Raise an exception if --fmf-id uses w/o url and git root
        # doesn't exist for discovered plan
        if self.opt('fmf_id'):

            def assert_git_url(plan_name=None):
                try:
                    subprocess.run('git rev-parse --show-toplevel'.split(),
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.DEVNULL,
                                   check=True)
                except subprocess.CalledProcessError:
                    raise tmt.utils.DiscoverError(
                        f"`tmt run discover --fmf-id` without `url` option in "
                        f"plan `{plan_name}` can be used only within"
                        f" git repo.")

            # It covers only one case, when there is:
            # 1) no --url on CLI
            # 2) plan w/o url exists in test run
            if not self.opt('url'):
                try:
                    fmf_tree = fmf.Tree(os.getcwd())
                except fmf.utils.RootError:
                    raise tmt.utils.DiscoverError(
                        f"No metadata found in the current directory. "
                        f"Use 'tmt init' to get started.")
                for i, attr in enumerate(fmf_tree.climb()):
                    try:
                        plan_url = attr.data.get('discover').get('url')
                        plan_name = attr.name
                        if not plan_url:
                            assert_git_url(plan_name)
                    except AttributeError:
                        pass
            # All other cases are covered by this condition
            if not url:
                assert_git_url(self.step.plan.name)

        # Clone provided git repository (if url given) with disabled
        # prompt to ignore possibly missing or private repositories
        if url:
            self.info('url', url, 'green')
            self.debug(f"Clone '{url}' to '{testdir}'.")
            self.run(['git', 'clone', url, testdir],
                     env={"GIT_ASKPASS": "******"})
        # Copy git repository root to workdir
        else:
            # Path for distgit sources cannot be checked until the
            # tarball is extracted
            if path and not os.path.isdir(path) and not dist_git_source:
                raise tmt.utils.DiscoverError(
                    f"Provided path '{path}' is not a directory.")
            if dist_git_source:
                git_root = self.step.plan.my_run.tree.root
            else:
                fmf_root = path or self.step.plan.my_run.tree.root
                if fmf_root is None:
                    raise tmt.utils.DiscoverError(
                        f"No metadata found in the current directory.")
                # Check git repository root (use fmf root if not found)
                try:
                    output = self.run(["git", "rev-parse", "--show-toplevel"],
                                      cwd=fmf_root,
                                      dry=True)
                    git_root = output[0].strip('\n')
                except tmt.utils.RunError:
                    self.debug(f"Git root not found, using '{fmf_root}.'")
                    git_root = fmf_root
                # Set path to relative path from the git root to fmf root
                path = os.path.relpath(fmf_root, git_root)
            self.info('directory', git_root, 'green')
            self.debug(f"Copy '{git_root}' to '{testdir}'.")
            if not self.opt('dry'):
                shutil.copytree(git_root, testdir, symlinks=True)

        # Checkout revision if requested
        ref = self.get('ref')
        if ref:
            self.info('ref', ref, 'green')
            self.debug(f"Checkout ref '{ref}'.")
            self.run(['git', 'checkout', '-f', str(ref)], cwd=testdir)

        # Show current commit hash if inside a git repository
        try:
            hash_, _ = self.run(["git", "rev-parse", "--short", "HEAD"],
                                cwd=testdir)
            self.verbose('hash', hash_.strip(), 'green')
        except (tmt.utils.RunError, AttributeError):
            pass

        # Fetch and extract distgit sources
        if dist_git_source:
            try:
                self.extract_distgit_source(testdir, testdir,
                                            self.get('dist-git-type'))
            except Exception as error:
                raise tmt.utils.DiscoverError(
                    f"Failed to process 'dist-git-source'.", original=error)

        # Adjust path and optionally show
        if path is None or path == '.':
            path = ''
        else:
            self.info('path', path, 'green')

        # Prepare the whole tree path and test path prefix
        tree_path = os.path.join(testdir, path.lstrip('/'))
        if not os.path.isdir(tree_path) and not self.opt('dry'):
            raise tmt.utils.DiscoverError(
                f"Metadata tree path '{path}' not found.")
        prefix_path = os.path.join('/tests', path.lstrip('/'))

        # Show filters and test names if provided
        # Check the 'test --filter' option first, then from discover
        filters = list(tmt.base.Test._opt('filters') or self.get('filter', []))
        for filter_ in filters:
            self.info('filter', filter_, 'green')
        # Names of tests selected by --test option
        names = self.get('test', [])
        if names:
            self.info('tests', fmf.utils.listed(names), 'green')
        # Check the 'test --link' option first, then from discover
        links = list(tmt.base.Test._opt('link') or self.get('link', []))
        for link_ in links:
            self.info('link', link_, 'green')

        excludes = list(
            tmt.base.Test._opt('exclude') or self.get('exclude', []))

        # Filter only modified tests if requested
        modified_only = self.get('modified-only')
        modified_url = self.get('modified-url')
        if modified_url:
            self.info('modified-url', modified_url, 'green')
            self.debug(f"Fetch also '{modified_url}' as 'reference'.")
            self.run(['git', 'remote', 'add', 'reference', modified_url],
                     cwd=testdir)
            self.run(['git', 'fetch', 'reference'], cwd=testdir)
        if modified_only:
            modified_ref = self.get('modified-ref',
                                    tmt.utils.default_branch(testdir))
            self.info('modified-ref', modified_ref, 'green')
            output = self.run([
                'git', 'log', '--format=', '--stat', '--name-only',
                f"{modified_ref}..HEAD"
            ],
                              cwd=testdir)[0]
            modified = set(f"^/{re.escape(name)}"
                           for name in map(os.path.dirname, output.split('\n'))
                           if name)
            self.debug(f"Limit to modified test dirs: {modified}", level=3)
            names.extend(modified)

        # Initialize the metadata tree, search for available tests
        self.debug(f"Check metadata tree in '{tree_path}'.")
        if self.opt('dry'):
            self._tests = []
            return
        tree = tmt.Tree(path=tree_path, context=self.step.plan._fmf_context())
        self._tests = tree.tests(filters=filters,
                                 names=names,
                                 conditions=["manual is False"],
                                 unique=False,
                                 links=links,
                                 excludes=excludes)

        # Prefix tests and handle library requires
        for test in self._tests:
            # Prefix test path with 'tests' and possible 'path' prefix
            test.path = os.path.join(prefix_path, test.path.lstrip('/'))
            # Check for possible required beakerlib libraries
            if test.require or test.recommend:
                test.require, test.recommend, _ = tmt.beakerlib.dependencies(
                    test.require, test.recommend, parent=self)
Exemplo n.º 22
0
#!/usr/bin/python

import fmf
from pathlib import Path
import subprocess

tree_root = Path.cwd().absolute()
node = fmf.Tree(tree_root).find("/plans")
with node as data:
    data["discover"]["url"] = "https://github.com/packit/packit.git"
    data["discover"]["ref"] = (subprocess.check_output(
        ["git", "rev-parse", "HEAD"]).decode().strip())
Exemplo n.º 23
0
    def __init__(self, args, unknown):
        # choose between TESTS and ADDITIONAL ENVIRONMENT from options
        if args.linter:
            self.tests += glob.glob("{MTF_TOOLS}/{GENERIC_TEST}/*.py".format(
                MTF_TOOLS=metadata_common.MetadataLoaderMTF.MTF_LINTER_PATH,
                GENERIC_TEST=common.conf["generic"]["generic_tests"]))
            self.tests += glob.glob("{MTF_TOOLS}/{STATIC_LINTERS}/*.py".format(
                MTF_TOOLS=metadata_common.MetadataLoaderMTF.MTF_LINTER_PATH,
                STATIC_LINTERS=common.conf["generic"]["static_tests"]))
        self.args = args

        # parse unknow options and try to find what parameter is test
        while unknown:
            if unknown[0] in self.A_KNOWN_PARAMS_SIMPLE:
                self.additionalAvocadoArg.append(unknown[0])
                unknown = unknown[1:]
            elif unknown[0].startswith("-"):
                if "=" in unknown[0] or len(unknown) < 2:
                    self.additionalAvocadoArg.append(unknown[0])
                    unknown = unknown[1:]
                else:
                    self.additionalAvocadoArg += unknown[0:2]
                    unknown = unknown[2:]
            elif glob.glob(unknown[0]):
                # dereference filename via globs
                testlist = glob.glob(unknown[0])
                self.tests += testlist
                unknown = unknown[1:]
            else:
                self.tests.append(unknown[0])
                unknown = unknown[1:]

        if self.args.metadata:
            core.print_info("Using Metadata loader for tests and filtering")
            metadata_tests = filtertests(backend="mtf",
                                         location=os.getcwd(),
                                         linters=False,
                                         tests=[],
                                         tags=[],
                                         relevancy="")
            tests_dict = [x[metadata_common.SOURCE] for x in metadata_tests]
            self.tests += tests_dict
            core.print_debug("Loaded tests via metadata file: %s" % tests_dict)
        core.print_debug("tests = {0}".format(self.tests))
        core.print_debug("additionalAvocadoArg = {0}".format(
            self.additionalAvocadoArg))

        # Advanced filtering and testcases adding based on FMF metadata, see help
        if self.args.fmffilter or self.args.fmfpath:
            # if fmf path is empty, use actual directory
            if not self.args.fmfpath:
                self.args.fmfpath = ["."]
            try:
                import fmf
            except ImportError:
                mtfexceptions.ModuleFrameworkException(
                    "FMF metadata format not installed on your system,"
                    " see fmf.readthedocs.io/en/latest/"
                    "for more info (how to install)")
            core.print_debug(
                "Using FMF metadata: path - {} and filters - {}".format(
                    self.args.fmfpath,
                    common.conf["fmf"]["filters"] + self.args.fmffilter))
            for onepath in self.args.fmfpath:
                tree = fmf.Tree(onepath)
                for node in tree.prune(
                        False, common.conf["fmf"]["keys"],
                        common.conf["fmf"]["names"],
                        common.conf["fmf"]["filters"] + self.args.fmffilter):
                    testcase = node.show(False, common.conf["fmf"]["format"],
                                         common.conf["fmf"]["values"])
                    core.print_debug("added test by FMF: {}".format(testcase))
                    self.tests.append(testcase)
Exemplo n.º 24
0
            return 0

    if not paths:
        paths = ['.']
    for path in paths:
        # Make sure we've got a real directory
        path = os.path.realpath(path)
        if not os.path.isdir(path):
            raise tmt.utils.GeneralError(
                "Path '{0}' is not a directory.".format(path))
        # Gather old metadata and store them as fmf
        common, individual = tmt.convert.read(path, makefile, nitrate, purpose,
                                              disabled)
        # Add path to common metadata if there are virtual test cases
        if individual:
            root = fmf.Tree(path).root
            common['path'] = os.path.join('/', os.path.relpath(path, root))
        # Store common metadata
        common_path = os.path.join(path, 'main.fmf')
        tmt.convert.write(common_path, common)
        # Store individual data (as virtual tests)
        for testcase in individual:
            testcase_path = os.path.join(
                path,
                str(testcase['extra-nitrate']) + '.fmf')
            tmt.convert.write(testcase_path, testcase)
        # Adjust runtest.sh content and permission if needed
        tmt.convert.adjust_runtest(os.path.join(path, 'runtest.sh'))


@tests.command()
Exemplo n.º 25
0
import pytest

import fmf
import tmt
import tmt.plugins

# Load all plugins
tmt.plugins.explore()

# Ignore loading/saving from/to workdir
tmt.steps.execute.Execute.load = lambda self: None
tmt.steps.execute.Execute.save = lambda self: None


# Smoke plan
smoke = tmt.Plan(fmf.Tree({'execute': {'script': 'tmt --help'}}))
smoke.execute.wake()

def test_smoke_method():
    assert smoke.execute.data[0]['how'] == 'shell'

def test_smoke_plugin():
    assert isinstance(
        smoke.execute.plugins()[0], tmt.steps.execute.ExecutePlugin)

def test_requires():
    assert smoke.execute.requires() == []


# Basic plan
basic = tmt.Plan(fmf.Tree({'execute': {'how': 'beakerlib'}}))
Exemplo n.º 26
0
    def fetch(self):
        """ Fetch the library (unless already fetched) """
        # Initialize library cache (indexed by the repository name)
        if not hasattr(self.parent, '_library_cache'):
            self.parent._library_cache = dict()

        # Check if the library was already fetched
        try:
            library = self.parent._library_cache[self.repo]
            # The url must be identical
            if library.url != self.url:
                raise tmt.utils.GeneralError(
                    f"Library '{self}' with url '{self.url}' conflicts "
                    f"with already fetched library from '{library.url}'.")
            # Use the default branch if no ref provided
            if self.ref is None:
                self.ref = library.default_branch
            # The same ref has to be used
            if library.ref != self.ref:
                raise tmt.utils.GeneralError(
                    f"Library '{self}' using ref '{self.ref}' conflicts "
                    f"with already fetched library '{library}' "
                    f"using ref '{library.ref}'.")
            self.parent.debug(f"Library '{self}' already fetched.", level=3)
            # Reuse the existing metadata tree
            self.tree = library.tree
        # Fetch the library and add it to the index
        except KeyError:
            self.parent.debug(f"Fetch library '{self}'.", level=3)
            # Prepare path, clone the repository, checkout ref
            directory = os.path.join(self.parent.workdir, self.dest, self.repo)
            # Clone repo with disabled prompt to ignore missing/private repos
            try:
                if self.url:
                    self.parent.run(['git', 'clone', self.url, directory],
                                    env={"GIT_ASKPASS": "******"})
                else:
                    self.parent.debug(
                        f"Copy local library '{self.path}' to '{directory}'.",
                        level=3)
                    shutil.copytree(self.path, directory, symlinks=True)
                # Detect the default branch from the origin
                try:
                    self.default_branch = tmt.utils.default_branch(directory)
                except OSError as error:
                    raise tmt.utils.GeneralError(
                        f"Unable to detect default branch for '{directory}'. "
                        f"Is the git repository '{self.url}' empty?")
                # Use the default branch if no ref provided
                if self.ref is None:
                    self.ref = self.default_branch
            except tmt.utils.RunError as error:
                # Fallback to install during the prepare step if in rpm format
                if self.format == 'rpm':
                    self.parent.debug(f"Repository '{self.url}' not found.")
                    raise LibraryError
                self.parent.fail(
                    f"Failed to fetch library '{self}' from '{self.url}'.")
                raise
            # Check out the requested branch
            try:
                self.parent.run(['git', 'checkout', self.ref], cwd=directory)
            except tmt.utils.RunError as error:
                # Fallback to install during the prepare step if in rpm format
                if self.format == 'rpm':
                    self.parent.debug(f"Invalid reference '{self.ref}'.")
                    raise LibraryError
                self.parent.fail(
                    f"Reference '{self.ref}' for library '{self}' not found.")
                raise
            # Initialize metadata tree, add self into the library index
            self.tree = fmf.Tree(directory)
            self.parent._library_cache[self.repo] = self

        # Get the library node, check require and recommend
        library = self.tree.find(self.name)
        if not library:
            # Fallback to install during the prepare step if in rpm format
            if self.format == 'rpm':
                self.parent.debug(
                    f"Library '{self.name.lstrip('/')}' not found "
                    f"in the '{self.url}' repo.")
                raise LibraryError
            raise tmt.utils.GeneralError(
                f"Library '{self.name}' not found in '{self.repo}'.")
        self.require = tmt.utils.listify(library.get('require', []))
        self.recommend = tmt.utils.listify(library.get('recommend', []))

        # Create a symlink if the library is deep in the structure
        # FIXME: hot fix for https://github.com/beakerlib/beakerlib/pull/72
        # Covers also cases when library is stored more than 2 levels deep
        if os.path.dirname(self.name).lstrip('/'):
            link = self.name.lstrip('/')
            path = os.path.join(self.tree.root, os.path.basename(self.name))
            self.parent.debug(
                f"Create a '{link}' symlink as the library is stored "
                f"deep in the directory structure.")
            try:
                os.symlink(link, path)
            except OSError as error:
                self.parent.warn(f"Unable to create a '{link}' symlink "
                                 f"for a deep library ({error}).")
Exemplo n.º 27
0
 def __init__(self, fmf_tree_path: str = '.'):
     self._cur_path = os.path.abspath(fmf_tree_path)
     self._tree = fmf.Tree(self._cur_path)