We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
avocado list --resolver doesn't work with hint files
Sample setup with QEMU's source code, plus `avocado.hint`:

``` 
[kinds]
tap = ./tests/test-*

[tap]
uri = $testpath
args = 
kwargs =
```

Then, running:

```
$ avocado list --resolver ./tests/test-iov 
exec-test ./tests/test-iov
```

Where `tap` would be expected.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `avocado/core/suite.py`
Content:
```
1 from enum import Enum
2 from uuid import uuid4
3 
4 from .dispatcher import RunnerDispatcher
5 from .exceptions import (JobTestSuiteReferenceResolutionError,
6                          OptionValidationError)
7 from .loader import (DiscoverMode, LoaderError, LoaderUnhandledReferenceError,
8                      loader)
9 from .resolver import resolve
10 from .settings import settings
11 from .tags import filter_test_tags
12 from .test import DryRunTest, Test
13 from .utils import resolutions_to_tasks
14 from .varianter import Varianter
15 
16 
17 class TestSuiteError(Exception):
18     pass
19 
20 
21 class TestSuiteStatus(Enum):
22     RESOLUTION_NOT_STARTED = object()
23     TESTS_NOT_FOUND = object()
24     TESTS_FOUND = object()
25     UNKNOWN = object()
26 
27 
28 class TestSuite:
29     def __init__(self, name, config=None, tests=None, job_config=None,
30                  resolutions=None):
31         self.name = name
32         self.tests = tests
33         self.resolutions = resolutions
34 
35         # Create a complete config dict with all registered options + custom
36         # config
37         self.config = settings.as_dict()
38         if job_config:
39             self.config.update(job_config)
40         if config:
41             self.config.update(config)
42 
43         self._variants = None
44         self._references = None
45         self._runner = None
46         self._test_parameters = None
47 
48         if (config.get('run.dry_run.enabled') and
49                 self.config.get('run.test_runner') == 'runner'):
50             self._convert_to_dry_run()
51 
52     def __len__(self):
53         """This is a convenient method to run `len()` over this object.
54 
55         With this you can run: len(a_suite) and will return the same as
56         `len(a_suite.tests)`.
57         """
58         return self.size
59 
60     def _convert_to_dry_run(self):
61         for i in range(self.size):
62             self.tests[i] = [DryRunTest, self.tests[i][1]]
63 
64     @classmethod
65     def _from_config_with_loader(cls, config, name=None):
66         references = config.get('run.references')
67         ignore_missing = config.get('run.ignore_missing_references')
68         verbose = config.get('core.verbose')
69         subcommand = config.get('subcommand')
70 
71         # To-be-removed: For some reason, avocado list will display more tests
72         # if in verbose mode. IMO, this is a little inconsistent with the 'run'
73         # command.  This hack was needed to make one specific test happy.
74         tests_mode = DiscoverMode.DEFAULT
75         if subcommand == 'list':
76             if verbose:
77                 tests_mode = DiscoverMode.ALL
78             else:
79                 tests_mode = DiscoverMode.AVAILABLE
80 
81         try:
82             loader.load_plugins(config)
83             tests = loader.discover(references,
84                                     force=ignore_missing,
85                                     which_tests=tests_mode)
86             if config.get("filter.by_tags.tags"):
87                 tests = filter_test_tags(
88                     tests,
89                     config.get("filter.by_tags.tags"),
90                     config.get("filter.by_tags.include_empty"),
91                     config.get('filter.by_tags.include_empty_key'))
92         except (LoaderUnhandledReferenceError, LoaderError) as details:
93             raise TestSuiteError(details)
94 
95         return cls(name=name or str(uuid4()),
96                    config=config,
97                    tests=tests)
98 
99     @classmethod
100     def _from_config_with_resolver(cls, config, name=None):
101         ignore_missing = config.get('run.ignore_missing_references')
102         references = config.get('run.references')
103         try:
104             resolutions = resolve(references, ignore_missing=ignore_missing)
105         except JobTestSuiteReferenceResolutionError as details:
106             raise TestSuiteError(details)
107         tasks = resolutions_to_tasks(resolutions, config)
108 
109         return cls(name=name or str(uuid4()),
110                    config=config,
111                    tests=tasks,
112                    resolutions=resolutions)
113 
114     def _get_stats_from_nrunner(self):
115         stats = {}
116         for test in self.tests:
117             stats = self._increment_dict_key_counter(stats, test.runnable.kind)
118         return stats
119 
120     def _get_stats_from_runner(self):
121         stats = {}
122         mapping = loader.get_type_label_mapping()
123 
124         for cls, _ in self.tests:
125             if isinstance(cls, str):
126                 cls = Test
127             stats = self._increment_dict_key_counter(stats, mapping[cls])
128         return stats
129 
130     def _get_tags_stats_from_nrunner(self):
131         stats = {}
132         for test in self.tests:
133             if test.runnable is None:
134                 continue
135             tags = test.runnable.tags or {}
136             for tag in tags:
137                 stats = self._increment_dict_key_counter(stats, tag)
138         return stats
139 
140     def _get_tags_stats_from_runner(self):
141         stats = {}
142         for test in self.tests:
143             params = test[1]
144             for tag in params.get('tags', {}):
145                 stats = self._increment_dict_key_counter(stats, tag)
146         return stats
147 
148     @staticmethod
149     def _increment_dict_key_counter(dict_object, key):
150         try:
151             dict_object[key.lower()] += 1
152         except KeyError:
153             dict_object[key.lower()] = 1
154         return dict_object
155 
156     @property
157     def references(self):
158         if self._references is None:
159             self._references = self.config.get('run.references')
160         return self._references
161 
162     @property
163     def runner(self):
164         if self._runner is None:
165             runner_name = self.config.get('run.test_runner') or 'runner'
166             try:
167                 runner_extension = RunnerDispatcher()[runner_name]
168                 self._runner = runner_extension.obj
169             except KeyError:
170                 raise TestSuiteError("Runner not implemented.")
171         return self._runner
172 
173     @property
174     def size(self):
175         """The overall length/size of this test suite."""
176         if self.tests is None:
177             return 0
178         return len(self.tests)
179 
180     @property
181     def stats(self):
182         """Return a statistics dict with the current tests."""
183         runner_name = self.config.get('run.test_runner') or 'runner'
184         if runner_name == 'runner':
185             return self._get_stats_from_runner()
186         elif runner_name == 'nrunner':
187             return self._get_stats_from_nrunner()
188         return {}
189 
190     @property
191     def status(self):
192         if self.tests is None:
193             return TestSuiteStatus.RESOLUTION_NOT_STARTED
194         elif self.size == 0:
195             return TestSuiteStatus.TESTS_NOT_FOUND
196         elif self.size > 0:
197             return TestSuiteStatus.TESTS_FOUND
198         else:
199             return TestSuiteStatus.UNKNOWN
200 
201     @property
202     def tags_stats(self):
203         """Return a statistics dict with the current tests tags."""
204         runner_name = self.config.get('run.test_runner') or 'runner'
205         if runner_name == 'runner':
206             return self._get_tags_stats_from_runner()
207         elif runner_name == 'nrunner':
208             return self._get_tags_stats_from_nrunner()
209         return {}
210 
211     @property
212     def test_parameters(self):
213         """Placeholder for test parameters.
214 
215         This is related to --test-parameters command line option or
216         (run.test_parameters).
217         """
218         if self._test_parameters is None:
219             self._test_parameters = {name: value for name, value
220                                      in self.config.get('run.test_parameters',
221                                                         [])}
222         return self._test_parameters
223 
224     @property
225     def variants(self):
226         if self._variants is None:
227             variants = Varianter()
228             if not variants.is_parsed():
229                 try:
230                     variants.parse(self.config)
231                 except (IOError, ValueError) as details:
232                     raise OptionValidationError("Unable to parse "
233                                                 "variant: %s" % details)
234             self._variants = variants
235         return self._variants
236 
237     def run(self, job):
238         """Run this test suite with the job context in mind.
239 
240         :param job: A :class:`avocado.core.job.Job` instance.
241         :rtype: set
242         """
243         return self.runner.run_suite(job, self)
244 
245     @classmethod
246     def from_config(cls, config, name=None, job_config=None):
247         """Helper method to create a TestSuite from config dicts.
248 
249         This is different from the TestSuite() initialization because here we
250         are assuming that you need some help to build the test suite. Avocado
251         will try to resolve tests based on the configuration information insead
252         of assuming pre populated tests.
253 
254         If you need to create a custom TestSuite, please use the TestSuite()
255         constructor instead of this method.
256 
257         :param config: A config dict to be used on the desired test suite.
258         :type config: dict
259         :param name: The name of the test suite. This is optional and default
260                      is a random uuid.
261         :type name: str
262         :param job_config: The job config dict (a global config). Use this to
263                            avoid huge configs per test suite. This is also
264                            optional.
265         :type job_config: dict
266         """
267         if job_config:
268             config.update(job_config)
269         runner = config.get('run.test_runner') or 'runner'
270         if runner == 'nrunner':
271             suite = cls._from_config_with_resolver(config, name)
272         else:
273             suite = cls._from_config_with_loader(config, name)
274 
275         if not config.get('run.ignore_missing_references'):
276             if not suite.tests:
277                 msg = ("Test Suite could not be create. No test references "
278                        "provided nor any other arguments resolved into tests")
279                 raise TestSuiteError(msg)
280 
281         return suite
282
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/avocado/core/suite.py b/avocado/core/suite.py
--- a/avocado/core/suite.py
+++ b/avocado/core/suite.py
@@ -1,3 +1,18 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# See LICENSE for more details.
+#
+# Copyright: Red Hat Inc. 2020
+# Author: Beraldo Leal <bleal@redhat.com>
+
+import os
 from enum import Enum
 from uuid import uuid4
 
@@ -6,6 +21,7 @@
                          OptionValidationError)
 from .loader import (DiscoverMode, LoaderError, LoaderUnhandledReferenceError,
                      loader)
+from .parser import HintParser
 from .resolver import resolve
 from .settings import settings
 from .tags import filter_test_tags
@@ -101,7 +117,13 @@
         ignore_missing = config.get('run.ignore_missing_references')
         references = config.get('run.references')
         try:
-            resolutions = resolve(references, ignore_missing=ignore_missing)
+            hint = None
+            hint_filepath = '.avocado.hint'
+            if os.path.exists(hint_filepath):
+                hint = HintParser(hint_filepath)
+            resolutions = resolve(references,
+                                  hint=hint,
+                                  ignore_missing=ignore_missing)
         except JobTestSuiteReferenceResolutionError as details:
             raise TestSuiteError(details)
         tasks = resolutions_to_tasks(resolutions, config)

{"golden_diff": "diff --git a/avocado/core/suite.py b/avocado/core/suite.py\n--- a/avocado/core/suite.py\n+++ b/avocado/core/suite.py\n@@ -1,3 +1,18 @@\n+# This program is free software; you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation; either version 2 of the License, or\n+# (at your option) any later version.\n+#\n+# This program is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n+#\n+# See LICENSE for more details.\n+#\n+# Copyright: Red Hat Inc. 2020\n+# Author: Beraldo Leal <bleal@redhat.com>\n+\n+import os\n from enum import Enum\n from uuid import uuid4\n \n@@ -6,6 +21,7 @@\n                          OptionValidationError)\n from .loader import (DiscoverMode, LoaderError, LoaderUnhandledReferenceError,\n                      loader)\n+from .parser import HintParser\n from .resolver import resolve\n from .settings import settings\n from .tags import filter_test_tags\n@@ -101,7 +117,13 @@\n         ignore_missing = config.get('run.ignore_missing_references')\n         references = config.get('run.references')\n         try:\n-            resolutions = resolve(references, ignore_missing=ignore_missing)\n+            hint = None\n+            hint_filepath = '.avocado.hint'\n+            if os.path.exists(hint_filepath):\n+                hint = HintParser(hint_filepath)\n+            resolutions = resolve(references,\n+                                  hint=hint,\n+                                  ignore_missing=ignore_missing)\n         except JobTestSuiteReferenceResolutionError as details:\n             raise TestSuiteError(details)\n         tasks = resolutions_to_tasks(resolutions, config)\n", "issue": "avocado list --resolver doesn't work with hint files\nSample setup with QEMU's source code, plus `avocado.hint`:\r\n\r\n``` \r\n[kinds]\r\ntap = ./tests/test-*\r\n\r\n[tap]\r\nuri = $testpath\r\nargs = \r\nkwargs =\r\n```\r\n\r\nThen, running:\r\n\r\n```\r\n$ avocado list --resolver ./tests/test-iov \r\nexec-test ./tests/test-iov\r\n```\r\n\r\nWhere `tap` would be expected.\r\n\n", "before_files": [{"content": "from enum import Enum\nfrom uuid import uuid4\n\nfrom .dispatcher import RunnerDispatcher\nfrom .exceptions import (JobTestSuiteReferenceResolutionError,\n                         OptionValidationError)\nfrom .loader import (DiscoverMode, LoaderError, LoaderUnhandledReferenceError,\n                     loader)\nfrom .resolver import resolve\nfrom .settings import settings\nfrom .tags import filter_test_tags\nfrom .test import DryRunTest, Test\nfrom .utils import resolutions_to_tasks\nfrom .varianter import Varianter\n\n\nclass TestSuiteError(Exception):\n    pass\n\n\nclass TestSuiteStatus(Enum):\n    RESOLUTION_NOT_STARTED = object()\n    TESTS_NOT_FOUND = object()\n    TESTS_FOUND = object()\n    UNKNOWN = object()\n\n\nclass TestSuite:\n    def __init__(self, name, config=None, tests=None, job_config=None,\n                 resolutions=None):\n        self.name = name\n        self.tests = tests\n        self.resolutions = resolutions\n\n        # Create a complete config dict with all registered options + custom\n        # config\n        self.config = settings.as_dict()\n        if job_config:\n            self.config.update(job_config)\n        if config:\n            self.config.update(config)\n\n        self._variants = None\n        self._references = None\n        self._runner = None\n        self._test_parameters = None\n\n        if (config.get('run.dry_run.enabled') and\n                self.config.get('run.test_runner') == 'runner'):\n            self._convert_to_dry_run()\n\n    def __len__(self):\n        \"\"\"This is a convenient method to run `len()` over this object.\n\n        With this you can run: len(a_suite) and will return the same as\n        `len(a_suite.tests)`.\n        \"\"\"\n        return self.size\n\n    def _convert_to_dry_run(self):\n        for i in range(self.size):\n            self.tests[i] = [DryRunTest, self.tests[i][1]]\n\n    @classmethod\n    def _from_config_with_loader(cls, config, name=None):\n        references = config.get('run.references')\n        ignore_missing = config.get('run.ignore_missing_references')\n        verbose = config.get('core.verbose')\n        subcommand = config.get('subcommand')\n\n        # To-be-removed: For some reason, avocado list will display more tests\n        # if in verbose mode. IMO, this is a little inconsistent with the 'run'\n        # command.  This hack was needed to make one specific test happy.\n        tests_mode = DiscoverMode.DEFAULT\n        if subcommand == 'list':\n            if verbose:\n                tests_mode = DiscoverMode.ALL\n            else:\n                tests_mode = DiscoverMode.AVAILABLE\n\n        try:\n            loader.load_plugins(config)\n            tests = loader.discover(references,\n                                    force=ignore_missing,\n                                    which_tests=tests_mode)\n            if config.get(\"filter.by_tags.tags\"):\n                tests = filter_test_tags(\n                    tests,\n                    config.get(\"filter.by_tags.tags\"),\n                    config.get(\"filter.by_tags.include_empty\"),\n                    config.get('filter.by_tags.include_empty_key'))\n        except (LoaderUnhandledReferenceError, LoaderError) as details:\n            raise TestSuiteError(details)\n\n        return cls(name=name or str(uuid4()),\n                   config=config,\n                   tests=tests)\n\n    @classmethod\n    def _from_config_with_resolver(cls, config, name=None):\n        ignore_missing = config.get('run.ignore_missing_references')\n        references = config.get('run.references')\n        try:\n            resolutions = resolve(references, ignore_missing=ignore_missing)\n        except JobTestSuiteReferenceResolutionError as details:\n            raise TestSuiteError(details)\n        tasks = resolutions_to_tasks(resolutions, config)\n\n        return cls(name=name or str(uuid4()),\n                   config=config,\n                   tests=tasks,\n                   resolutions=resolutions)\n\n    def _get_stats_from_nrunner(self):\n        stats = {}\n        for test in self.tests:\n            stats = self._increment_dict_key_counter(stats, test.runnable.kind)\n        return stats\n\n    def _get_stats_from_runner(self):\n        stats = {}\n        mapping = loader.get_type_label_mapping()\n\n        for cls, _ in self.tests:\n            if isinstance(cls, str):\n                cls = Test\n            stats = self._increment_dict_key_counter(stats, mapping[cls])\n        return stats\n\n    def _get_tags_stats_from_nrunner(self):\n        stats = {}\n        for test in self.tests:\n            if test.runnable is None:\n                continue\n            tags = test.runnable.tags or {}\n            for tag in tags:\n                stats = self._increment_dict_key_counter(stats, tag)\n        return stats\n\n    def _get_tags_stats_from_runner(self):\n        stats = {}\n        for test in self.tests:\n            params = test[1]\n            for tag in params.get('tags', {}):\n                stats = self._increment_dict_key_counter(stats, tag)\n        return stats\n\n    @staticmethod\n    def _increment_dict_key_counter(dict_object, key):\n        try:\n            dict_object[key.lower()] += 1\n        except KeyError:\n            dict_object[key.lower()] = 1\n        return dict_object\n\n    @property\n    def references(self):\n        if self._references is None:\n            self._references = self.config.get('run.references')\n        return self._references\n\n    @property\n    def runner(self):\n        if self._runner is None:\n            runner_name = self.config.get('run.test_runner') or 'runner'\n            try:\n                runner_extension = RunnerDispatcher()[runner_name]\n                self._runner = runner_extension.obj\n            except KeyError:\n                raise TestSuiteError(\"Runner not implemented.\")\n        return self._runner\n\n    @property\n    def size(self):\n        \"\"\"The overall length/size of this test suite.\"\"\"\n        if self.tests is None:\n            return 0\n        return len(self.tests)\n\n    @property\n    def stats(self):\n        \"\"\"Return a statistics dict with the current tests.\"\"\"\n        runner_name = self.config.get('run.test_runner') or 'runner'\n        if runner_name == 'runner':\n            return self._get_stats_from_runner()\n        elif runner_name == 'nrunner':\n            return self._get_stats_from_nrunner()\n        return {}\n\n    @property\n    def status(self):\n        if self.tests is None:\n            return TestSuiteStatus.RESOLUTION_NOT_STARTED\n        elif self.size == 0:\n            return TestSuiteStatus.TESTS_NOT_FOUND\n        elif self.size > 0:\n            return TestSuiteStatus.TESTS_FOUND\n        else:\n            return TestSuiteStatus.UNKNOWN\n\n    @property\n    def tags_stats(self):\n        \"\"\"Return a statistics dict with the current tests tags.\"\"\"\n        runner_name = self.config.get('run.test_runner') or 'runner'\n        if runner_name == 'runner':\n            return self._get_tags_stats_from_runner()\n        elif runner_name == 'nrunner':\n            return self._get_tags_stats_from_nrunner()\n        return {}\n\n    @property\n    def test_parameters(self):\n        \"\"\"Placeholder for test parameters.\n\n        This is related to --test-parameters command line option or\n        (run.test_parameters).\n        \"\"\"\n        if self._test_parameters is None:\n            self._test_parameters = {name: value for name, value\n                                     in self.config.get('run.test_parameters',\n                                                        [])}\n        return self._test_parameters\n\n    @property\n    def variants(self):\n        if self._variants is None:\n            variants = Varianter()\n            if not variants.is_parsed():\n                try:\n                    variants.parse(self.config)\n                except (IOError, ValueError) as details:\n                    raise OptionValidationError(\"Unable to parse \"\n                                                \"variant: %s\" % details)\n            self._variants = variants\n        return self._variants\n\n    def run(self, job):\n        \"\"\"Run this test suite with the job context in mind.\n\n        :param job: A :class:`avocado.core.job.Job` instance.\n        :rtype: set\n        \"\"\"\n        return self.runner.run_suite(job, self)\n\n    @classmethod\n    def from_config(cls, config, name=None, job_config=None):\n        \"\"\"Helper method to create a TestSuite from config dicts.\n\n        This is different from the TestSuite() initialization because here we\n        are assuming that you need some help to build the test suite. Avocado\n        will try to resolve tests based on the configuration information insead\n        of assuming pre populated tests.\n\n        If you need to create a custom TestSuite, please use the TestSuite()\n        constructor instead of this method.\n\n        :param config: A config dict to be used on the desired test suite.\n        :type config: dict\n        :param name: The name of the test suite. This is optional and default\n                     is a random uuid.\n        :type name: str\n        :param job_config: The job config dict (a global config). Use this to\n                           avoid huge configs per test suite. This is also\n                           optional.\n        :type job_config: dict\n        \"\"\"\n        if job_config:\n            config.update(job_config)\n        runner = config.get('run.test_runner') or 'runner'\n        if runner == 'nrunner':\n            suite = cls._from_config_with_resolver(config, name)\n        else:\n            suite = cls._from_config_with_loader(config, name)\n\n        if not config.get('run.ignore_missing_references'):\n            if not suite.tests:\n                msg = (\"Test Suite could not be create. No test references \"\n                       \"provided nor any other arguments resolved into tests\")\n                raise TestSuiteError(msg)\n\n        return suite\n", "path": "avocado/core/suite.py"}], "after_files": [{"content": "# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n#\n# See LICENSE for more details.\n#\n# Copyright: Red Hat Inc. 2020\n# Author: Beraldo Leal <bleal@redhat.com>\n\nimport os\nfrom enum import Enum\nfrom uuid import uuid4\n\nfrom .dispatcher import RunnerDispatcher\nfrom .exceptions import (JobTestSuiteReferenceResolutionError,\n                         OptionValidationError)\nfrom .loader import (DiscoverMode, LoaderError, LoaderUnhandledReferenceError,\n                     loader)\nfrom .parser import HintParser\nfrom .resolver import resolve\nfrom .settings import settings\nfrom .tags import filter_test_tags\nfrom .test import DryRunTest, Test\nfrom .utils import resolutions_to_tasks\nfrom .varianter import Varianter\n\n\nclass TestSuiteError(Exception):\n    pass\n\n\nclass TestSuiteStatus(Enum):\n    RESOLUTION_NOT_STARTED = object()\n    TESTS_NOT_FOUND = object()\n    TESTS_FOUND = object()\n    UNKNOWN = object()\n\n\nclass TestSuite:\n    def __init__(self, name, config=None, tests=None, job_config=None,\n                 resolutions=None):\n        self.name = name\n        self.tests = tests\n        self.resolutions = resolutions\n\n        # Create a complete config dict with all registered options + custom\n        # config\n        self.config = settings.as_dict()\n        if job_config:\n            self.config.update(job_config)\n        if config:\n            self.config.update(config)\n\n        self._variants = None\n        self._references = None\n        self._runner = None\n        self._test_parameters = None\n\n        if (config.get('run.dry_run.enabled') and\n                self.config.get('run.test_runner') == 'runner'):\n            self._convert_to_dry_run()\n\n    def __len__(self):\n        \"\"\"This is a convenient method to run `len()` over this object.\n\n        With this you can run: len(a_suite) and will return the same as\n        `len(a_suite.tests)`.\n        \"\"\"\n        return self.size\n\n    def _convert_to_dry_run(self):\n        for i in range(self.size):\n            self.tests[i] = [DryRunTest, self.tests[i][1]]\n\n    @classmethod\n    def _from_config_with_loader(cls, config, name=None):\n        references = config.get('run.references')\n        ignore_missing = config.get('run.ignore_missing_references')\n        verbose = config.get('core.verbose')\n        subcommand = config.get('subcommand')\n\n        # To-be-removed: For some reason, avocado list will display more tests\n        # if in verbose mode. IMO, this is a little inconsistent with the 'run'\n        # command.  This hack was needed to make one specific test happy.\n        tests_mode = DiscoverMode.DEFAULT\n        if subcommand == 'list':\n            if verbose:\n                tests_mode = DiscoverMode.ALL\n            else:\n                tests_mode = DiscoverMode.AVAILABLE\n\n        try:\n            loader.load_plugins(config)\n            tests = loader.discover(references,\n                                    force=ignore_missing,\n                                    which_tests=tests_mode)\n            if config.get(\"filter.by_tags.tags\"):\n                tests = filter_test_tags(\n                    tests,\n                    config.get(\"filter.by_tags.tags\"),\n                    config.get(\"filter.by_tags.include_empty\"),\n                    config.get('filter.by_tags.include_empty_key'))\n        except (LoaderUnhandledReferenceError, LoaderError) as details:\n            raise TestSuiteError(details)\n\n        return cls(name=name or str(uuid4()),\n                   config=config,\n                   tests=tests)\n\n    @classmethod\n    def _from_config_with_resolver(cls, config, name=None):\n        ignore_missing = config.get('run.ignore_missing_references')\n        references = config.get('run.references')\n        try:\n            hint = None\n            hint_filepath = '.avocado.hint'\n            if os.path.exists(hint_filepath):\n                hint = HintParser(hint_filepath)\n            resolutions = resolve(references,\n                                  hint=hint,\n                                  ignore_missing=ignore_missing)\n        except JobTestSuiteReferenceResolutionError as details:\n            raise TestSuiteError(details)\n        tasks = resolutions_to_tasks(resolutions, config)\n\n        return cls(name=name or str(uuid4()),\n                   config=config,\n                   tests=tasks,\n                   resolutions=resolutions)\n\n    def _get_stats_from_nrunner(self):\n        stats = {}\n        for test in self.tests:\n            stats = self._increment_dict_key_counter(stats, test.runnable.kind)\n        return stats\n\n    def _get_stats_from_runner(self):\n        stats = {}\n        mapping = loader.get_type_label_mapping()\n\n        for cls, _ in self.tests:\n            if isinstance(cls, str):\n                cls = Test\n            stats = self._increment_dict_key_counter(stats, mapping[cls])\n        return stats\n\n    def _get_tags_stats_from_nrunner(self):\n        stats = {}\n        for test in self.tests:\n            if test.runnable is None:\n                continue\n            tags = test.runnable.tags or {}\n            for tag in tags:\n                stats = self._increment_dict_key_counter(stats, tag)\n        return stats\n\n    def _get_tags_stats_from_runner(self):\n        stats = {}\n        for test in self.tests:\n            params = test[1]\n            for tag in params.get('tags', {}):\n                stats = self._increment_dict_key_counter(stats, tag)\n        return stats\n\n    @staticmethod\n    def _increment_dict_key_counter(dict_object, key):\n        try:\n            dict_object[key.lower()] += 1\n        except KeyError:\n            dict_object[key.lower()] = 1\n        return dict_object\n\n    @property\n    def references(self):\n        if self._references is None:\n            self._references = self.config.get('run.references')\n        return self._references\n\n    @property\n    def runner(self):\n        if self._runner is None:\n            runner_name = self.config.get('run.test_runner') or 'runner'\n            try:\n                runner_extension = RunnerDispatcher()[runner_name]\n                self._runner = runner_extension.obj\n            except KeyError:\n                raise TestSuiteError(\"Runner not implemented.\")\n        return self._runner\n\n    @property\n    def size(self):\n        \"\"\"The overall length/size of this test suite.\"\"\"\n        if self.tests is None:\n            return 0\n        return len(self.tests)\n\n    @property\n    def stats(self):\n        \"\"\"Return a statistics dict with the current tests.\"\"\"\n        runner_name = self.config.get('run.test_runner') or 'runner'\n        if runner_name == 'runner':\n            return self._get_stats_from_runner()\n        elif runner_name == 'nrunner':\n            return self._get_stats_from_nrunner()\n        return {}\n\n    @property\n    def status(self):\n        if self.tests is None:\n            return TestSuiteStatus.RESOLUTION_NOT_STARTED\n        elif self.size == 0:\n            return TestSuiteStatus.TESTS_NOT_FOUND\n        elif self.size > 0:\n            return TestSuiteStatus.TESTS_FOUND\n        else:\n            return TestSuiteStatus.UNKNOWN\n\n    @property\n    def tags_stats(self):\n        \"\"\"Return a statistics dict with the current tests tags.\"\"\"\n        runner_name = self.config.get('run.test_runner') or 'runner'\n        if runner_name == 'runner':\n            return self._get_tags_stats_from_runner()\n        elif runner_name == 'nrunner':\n            return self._get_tags_stats_from_nrunner()\n        return {}\n\n    @property\n    def test_parameters(self):\n        \"\"\"Placeholder for test parameters.\n\n        This is related to --test-parameters command line option or\n        (run.test_parameters).\n        \"\"\"\n        if self._test_parameters is None:\n            self._test_parameters = {name: value for name, value\n                                     in self.config.get('run.test_parameters',\n                                                        [])}\n        return self._test_parameters\n\n    @property\n    def variants(self):\n        if self._variants is None:\n            variants = Varianter()\n            if not variants.is_parsed():\n                try:\n                    variants.parse(self.config)\n                except (IOError, ValueError) as details:\n                    raise OptionValidationError(\"Unable to parse \"\n                                                \"variant: %s\" % details)\n            self._variants = variants\n        return self._variants\n\n    def run(self, job):\n        \"\"\"Run this test suite with the job context in mind.\n\n        :param job: A :class:`avocado.core.job.Job` instance.\n        :rtype: set\n        \"\"\"\n        return self.runner.run_suite(job, self)\n\n    @classmethod\n    def from_config(cls, config, name=None, job_config=None):\n        \"\"\"Helper method to create a TestSuite from config dicts.\n\n        This is different from the TestSuite() initialization because here we\n        are assuming that you need some help to build the test suite. Avocado\n        will try to resolve tests based on the configuration information insead\n        of assuming pre populated tests.\n\n        If you need to create a custom TestSuite, please use the TestSuite()\n        constructor instead of this method.\n\n        :param config: A config dict to be used on the desired test suite.\n        :type config: dict\n        :param name: The name of the test suite. This is optional and default\n                     is a random uuid.\n        :type name: str\n        :param job_config: The job config dict (a global config). Use this to\n                           avoid huge configs per test suite. This is also\n                           optional.\n        :type job_config: dict\n        \"\"\"\n        if job_config:\n            config.update(job_config)\n        runner = config.get('run.test_runner') or 'runner'\n        if runner == 'nrunner':\n            suite = cls._from_config_with_resolver(config, name)\n        else:\n            suite = cls._from_config_with_loader(config, name)\n\n        if not config.get('run.ignore_missing_references'):\n            if not suite.tests:\n                msg = (\"Test Suite could not be create. No test references \"\n                       \"provided nor any other arguments resolved into tests\")\n                raise TestSuiteError(msg)\n\n        return suite\n", "path": "avocado/core/suite.py"}]}