Tom Pollard pushed to branch tpollard/591 at BuildStream / buildstream
Commits:
-
323a5403
by Chandan Singh at 2018-08-29T12:13:28Z
-
5fc7b5ac
by Tristan Van Berkom at 2018-08-29T12:37:08Z
-
42ad937d
by Josh Smith at 2018-08-29T12:38:15Z
-
aa3a33b3
by Tristan Van Berkom at 2018-08-29T13:03:58Z
-
2a2a79de
by Valentin David at 2018-08-29T14:11:48Z
-
2df7607c
by Tristan Van Berkom at 2018-08-29T14:43:55Z
-
c146dde5
by Benjamin Schubert at 2018-08-29T16:00:24Z
-
19e87afb
by Benjamin Schubert at 2018-08-29T16:01:12Z
-
9cc5817f
by Qinusty at 2018-08-29T16:25:48Z
-
3c8646e3
by Josh Smith at 2018-08-29T16:34:22Z
-
d41c42d6
by Josh Smith at 2018-08-29T16:34:22Z
-
c7b72c30
by Qinusty at 2018-08-29T16:58:48Z
-
d28e1353
by Tristan Van Berkom at 2018-08-30T06:02:41Z
-
e77ae07a
by Tristan Van Berkom at 2018-08-30T06:36:41Z
-
9ea40e2c
by Tristan Van Berkom at 2018-08-30T06:38:13Z
-
8aec1102
by Tristan Van Berkom at 2018-08-30T07:06:17Z
-
fcc17c82
by Tristan Van Berkom at 2018-08-30T08:44:34Z
-
60ad044a
by Tristan Van Berkom at 2018-08-30T08:44:34Z
-
c339f917
by Tristan Van Berkom at 2018-08-30T08:44:34Z
-
26565f11
by Tristan Van Berkom at 2018-08-30T09:12:28Z
-
44a81a5c
by Tom Pollard at 2018-08-30T10:27:18Z
17 changed files:
- .gitlab-ci.yml
- buildstream/_artifactcache/cascache.py
- buildstream/_exceptions.py
- buildstream/_project.py
- buildstream/_variables.py
- buildstream/plugins/sources/git.py
- buildstream/source.py
- buildstream/utils.py
- dev-requirements.txt
- setup.py
- tests/format/variables.py
- + tests/format/variables/cyclic_variables/cyclic.bst
- + tests/format/variables/cyclic_variables/project.conf
- tests/frontend/mirror.py
- tests/frontend/pull.py
- tests/plugins/filter.py
- tests/testutils/runcli.py
Changes:
| ... | ... | @@ -84,25 +84,25 @@ source_dist: |
| 84 | 84 |
- coverage-linux/
|
| 85 | 85 |
|
| 86 | 86 |
tests-debian-9:
|
| 87 |
- image: buildstream/testsuite-debian:9-master-114-4cab18e3
|
|
| 87 |
+ image: buildstream/testsuite-debian:9-master-117-aa3a33b3
|
|
| 88 | 88 |
<<: *linux-tests
|
| 89 | 89 |
|
| 90 | 90 |
tests-fedora-27:
|
| 91 |
- image: buildstream/testsuite-fedora:27-master-114-4cab18e3
|
|
| 91 |
+ image: buildstream/testsuite-fedora:27-master-117-aa3a33b3
|
|
| 92 | 92 |
<<: *linux-tests
|
| 93 | 93 |
|
| 94 | 94 |
tests-fedora-28:
|
| 95 |
- image: buildstream/testsuite-fedora:28-master-114-4cab18e3
|
|
| 95 |
+ image: buildstream/testsuite-fedora:28-master-117-aa3a33b3
|
|
| 96 | 96 |
<<: *linux-tests
|
| 97 | 97 |
|
| 98 | 98 |
tests-ubuntu-18.04:
|
| 99 |
- image: buildstream/testsuite-ubuntu:18.04-master-114-4cab18e3
|
|
| 99 |
+ image: buildstream/testsuite-ubuntu:18.04-master-117-aa3a33b3
|
|
| 100 | 100 |
<<: *linux-tests
|
| 101 | 101 |
|
| 102 | 102 |
tests-unix:
|
| 103 | 103 |
# Use fedora here, to a) run a test on fedora and b) ensure that we
|
| 104 | 104 |
# can get rid of ostree - this is not possible with debian-8
|
| 105 |
- image: buildstream/testsuite-fedora:27-master-114-4cab18e3
|
|
| 105 |
+ image: buildstream/testsuite-fedora:27-master-117-aa3a33b3
|
|
| 106 | 106 |
stage: test
|
| 107 | 107 |
variables:
|
| 108 | 108 |
BST_FORCE_BACKEND: "unix"
|
| ... | ... | @@ -249,6 +249,13 @@ class CASCache(ArtifactCache): |
| 249 | 249 |
if e.code() != grpc.StatusCode.NOT_FOUND:
|
| 250 | 250 |
raise ArtifactError("Failed to pull artifact {}: {}".format(
|
| 251 | 251 |
element._get_brief_display_key(), e)) from e
|
| 252 |
+ else:
|
|
| 253 |
+ self.context.message(Message(
|
|
| 254 |
+ None,
|
|
| 255 |
+ MessageType.SKIPPED,
|
|
| 256 |
+ "Remote ({}) does not have {} cached".format(
|
|
| 257 |
+ remote.spec.url, element._get_brief_display_key())
|
|
| 258 |
+ ))
|
|
| 252 | 259 |
|
| 253 | 260 |
return False
|
| 254 | 261 |
|
| ... | ... | @@ -217,6 +217,9 @@ class LoadErrorReason(Enum): |
| 217 | 217 |
# A recursive include has been encountered.
|
| 218 | 218 |
RECURSIVE_INCLUDE = 21
|
| 219 | 219 |
|
| 220 |
+ # A recursive variable has been encountered
|
|
| 221 |
+ RECURSIVE_VARIABLE = 22
|
|
| 222 |
+ |
|
| 220 | 223 |
|
| 221 | 224 |
# LoadError
|
| 222 | 225 |
#
|
| ... | ... | @@ -398,6 +398,11 @@ class Project(): |
| 398 | 398 |
"Project requested format version {}, but BuildStream {}.{} only supports up until format version {}"
|
| 399 | 399 |
.format(format_version, major, minor, BST_FORMAT_VERSION))
|
| 400 | 400 |
|
| 401 |
+ # Check if project.conf contains name key field
|
|
| 402 |
+ if self._project_conf.get('name') is None:
|
|
| 403 |
+ raise LoadError(LoadErrorReason.INVALID_DATA,
|
|
| 404 |
+ "{}: project.conf does not contain expected key '{}'".format(projectfile, 'name'))
|
|
| 405 |
+ |
|
| 401 | 406 |
# The project name, element path and option declarations
|
| 402 | 407 |
# are constant and cannot be overridden by option conditional statements
|
| 403 | 408 |
self.name = _yaml.node_get(pre_config_node, str, 'name')
|
| ... | ... | @@ -61,7 +61,7 @@ class Variables(): |
| 61 | 61 |
# LoadError, if the string contains unresolved variable references.
|
| 62 | 62 |
#
|
| 63 | 63 |
def subst(self, string):
|
| 64 |
- substitute, unmatched = self._subst(string, self.variables)
|
|
| 64 |
+ substitute, unmatched, _ = self._subst(string, self.variables)
|
|
| 65 | 65 |
unmatched = list(set(unmatched))
|
| 66 | 66 |
if unmatched:
|
| 67 | 67 |
if len(unmatched) == 1:
|
| ... | ... | @@ -82,6 +82,7 @@ class Variables(): |
| 82 | 82 |
def subst_callback(match):
|
| 83 | 83 |
nonlocal variables
|
| 84 | 84 |
nonlocal unmatched
|
| 85 |
+ nonlocal matched
|
|
| 85 | 86 |
|
| 86 | 87 |
token = match.group(0)
|
| 87 | 88 |
varname = match.group(1)
|
| ... | ... | @@ -91,6 +92,7 @@ class Variables(): |
| 91 | 92 |
# We have to check if the inner string has variables
|
| 92 | 93 |
# and return unmatches for those
|
| 93 | 94 |
unmatched += re.findall(_VARIABLE_MATCH, value)
|
| 95 |
+ matched += [varname]
|
|
| 94 | 96 |
else:
|
| 95 | 97 |
# Return unmodified token
|
| 96 | 98 |
unmatched += [varname]
|
| ... | ... | @@ -98,10 +100,11 @@ class Variables(): |
| 98 | 100 |
|
| 99 | 101 |
return value
|
| 100 | 102 |
|
| 103 |
+ matched = []
|
|
| 101 | 104 |
unmatched = []
|
| 102 | 105 |
replacement = re.sub(_VARIABLE_MATCH, subst_callback, string)
|
| 103 | 106 |
|
| 104 |
- return (replacement, unmatched)
|
|
| 107 |
+ return (replacement, unmatched, matched)
|
|
| 105 | 108 |
|
| 106 | 109 |
# Variable resolving code
|
| 107 | 110 |
#
|
| ... | ... | @@ -131,7 +134,15 @@ class Variables(): |
| 131 | 134 |
# Ensure stringness of the value before substitution
|
| 132 | 135 |
value = _yaml.node_get(variables, str, key)
|
| 133 | 136 |
|
| 134 |
- resolved_var, item_unmatched = self._subst(value, variables)
|
|
| 137 |
+ resolved_var, item_unmatched, matched = self._subst(value, variables)
|
|
| 138 |
+ |
|
| 139 |
+ if _wrap_variable(key) in resolved_var:
|
|
| 140 |
+ referenced_through = find_recursive_variable(key, matched, variables)
|
|
| 141 |
+ raise LoadError(LoadErrorReason.RECURSIVE_VARIABLE,
|
|
| 142 |
+ "{}: ".format(_yaml.node_get_provenance(variables, key)) +
|
|
| 143 |
+ ("Variable '{}' expands to contain a reference to itself. " +
|
|
| 144 |
+ "Perhaps '{}' contains '{}").format(key, referenced_through, _wrap_variable(key)))
|
|
| 145 |
+ |
|
| 135 | 146 |
resolved[key] = resolved_var
|
| 136 | 147 |
unmatched += item_unmatched
|
| 137 | 148 |
|
| ... | ... | @@ -168,8 +179,21 @@ class Variables(): |
| 168 | 179 |
# Helper function to fetch information about the node referring to a variable
|
| 169 | 180 |
#
|
| 170 | 181 |
def _find_references(self, varname):
|
| 171 |
- fullname = '%{' + varname + '}'
|
|
| 182 |
+ fullname = _wrap_variable(varname)
|
|
| 172 | 183 |
for key, value in _yaml.node_items(self.original):
|
| 173 | 184 |
if fullname in value:
|
| 174 | 185 |
provenance = _yaml.node_get_provenance(self.original, key)
|
| 175 | 186 |
yield (key, provenance)
|
| 187 |
+ |
|
| 188 |
+ |
|
| 189 |
+def find_recursive_variable(variable, matched_variables, all_vars):
|
|
| 190 |
+ matched_values = (_yaml.node_get(all_vars, str, key) for key in matched_variables)
|
|
| 191 |
+ for key, value in zip(matched_variables, matched_values):
|
|
| 192 |
+ if _wrap_variable(variable) in value:
|
|
| 193 |
+ return key
|
|
| 194 |
+ else:
|
|
| 195 |
+ return None
|
|
| 196 |
+ |
|
| 197 |
+ |
|
| 198 |
+def _wrap_variable(var):
|
|
| 199 |
+ return "%{" + var + "}"
|
| ... | ... | @@ -387,8 +387,10 @@ class GitSource(Source): |
| 387 | 387 |
detail=detail, reason="track-attempt-no-track")
|
| 388 | 388 |
return None
|
| 389 | 389 |
|
| 390 |
+ # Resolve the URL for the message
|
|
| 391 |
+ resolved_url = self.translate_url(self.mirror.url)
|
|
| 390 | 392 |
with self.timed_activity("Tracking {} from {}"
|
| 391 |
- .format(self.tracking, self.mirror.url),
|
|
| 393 |
+ .format(self.tracking, resolved_url),
|
|
| 392 | 394 |
silent_nested=True):
|
| 393 | 395 |
self.mirror.ensure()
|
| 394 | 396 |
self.mirror._fetch()
|
| ... | ... | @@ -219,10 +219,7 @@ class SourceFetcher(): |
| 219 | 219 |
Args:
|
| 220 | 220 |
url (str): The url used to download.
|
| 221 | 221 |
"""
|
| 222 |
- # Not guaranteed to be a valid alias yet.
|
|
| 223 |
- # Ensuring it's a valid alias currently happens in Project.get_alias_uris
|
|
| 224 |
- alias, _ = url.split(utils._ALIAS_SEPARATOR, 1)
|
|
| 225 |
- self.__alias = alias
|
|
| 222 |
+ self.__alias = _extract_alias(url)
|
|
| 226 | 223 |
|
| 227 | 224 |
#############################################################
|
| 228 | 225 |
# Private Methods used in BuildStream #
|
| ... | ... | @@ -448,20 +445,6 @@ class Source(Plugin): |
| 448 | 445 |
"""
|
| 449 | 446 |
self.stage(directory)
|
| 450 | 447 |
|
| 451 |
- def mark_download_url(self, url):
|
|
| 452 |
- """Identifies the URL that this Source uses to download
|
|
| 453 |
- |
|
| 454 |
- This must be called during :func:`~buildstream.plugin.Plugin.configure` if
|
|
| 455 |
- :func:`~buildstream.source.Source.translate_url` is not called.
|
|
| 456 |
- |
|
| 457 |
- Args:
|
|
| 458 |
- url (str): The url used to download
|
|
| 459 |
- |
|
| 460 |
- *Since: 1.2*
|
|
| 461 |
- """
|
|
| 462 |
- alias, _ = url.split(utils._ALIAS_SEPARATOR, 1)
|
|
| 463 |
- self.__expected_alias = alias
|
|
| 464 |
- |
|
| 465 | 448 |
def get_source_fetchers(self):
|
| 466 | 449 |
"""Get the objects that are used for fetching
|
| 467 | 450 |
|
| ... | ... | @@ -525,12 +508,24 @@ class Source(Plugin): |
| 525 | 508 |
else:
|
| 526 | 509 |
# Sneakily store the alias if it hasn't already been stored
|
| 527 | 510 |
if not self.__expected_alias and url and utils._ALIAS_SEPARATOR in url:
|
| 528 |
- url_alias, _ = url.split(utils._ALIAS_SEPARATOR, 1)
|
|
| 529 |
- self.__expected_alias = url_alias
|
|
| 511 |
+ self.mark_download_url(url)
|
|
| 530 | 512 |
|
| 531 | 513 |
project = self._get_project()
|
| 532 | 514 |
return project.translate_url(url, first_pass=self.__first_pass)
|
| 533 | 515 |
|
| 516 |
+ def mark_download_url(self, url):
|
|
| 517 |
+ """Identifies the URL that this Source uses to download
|
|
| 518 |
+ |
|
| 519 |
+ This must be called during :func:`~buildstream.plugin.Plugin.configure` if
|
|
| 520 |
+ :func:`~buildstream.source.Source.translate_url` is not called.
|
|
| 521 |
+ |
|
| 522 |
+ Args:
|
|
| 523 |
+ url (str): The url used to download
|
|
| 524 |
+ |
|
| 525 |
+ *Since: 1.2*
|
|
| 526 |
+ """
|
|
| 527 |
+ self.__expected_alias = _extract_alias(url)
|
|
| 528 |
+ |
|
| 534 | 529 |
def get_project_directory(self):
|
| 535 | 530 |
"""Fetch the project base directory
|
| 536 | 531 |
|
| ... | ... | @@ -774,7 +769,8 @@ class Source(Plugin): |
| 774 | 769 |
#
|
| 775 | 770 |
# Step 2 - Set the ref in memory, and determine changed state
|
| 776 | 771 |
#
|
| 777 |
- changed = self._set_ref(new_ref, node)
|
|
| 772 |
+ if not self._set_ref(new_ref, node):
|
|
| 773 |
+ return False
|
|
| 778 | 774 |
|
| 779 | 775 |
def do_save_refs(refs):
|
| 780 | 776 |
try:
|
| ... | ... | @@ -811,7 +807,7 @@ class Source(Plugin): |
| 811 | 807 |
.format(provenance.filename.shortname),
|
| 812 | 808 |
reason="tracking-junction-fragment")
|
| 813 | 809 |
|
| 814 |
- return changed
|
|
| 810 |
+ return True
|
|
| 815 | 811 |
|
| 816 | 812 |
# Wrapper for track()
|
| 817 | 813 |
#
|
| ... | ... | @@ -869,10 +865,12 @@ class Source(Plugin): |
| 869 | 865 |
def __do_fetch(self, **kwargs):
|
| 870 | 866 |
project = self._get_project()
|
| 871 | 867 |
source_fetchers = self.get_source_fetchers()
|
| 868 |
+ |
|
| 869 |
+ # Use the source fetchers if they are provided
|
|
| 870 |
+ #
|
|
| 872 | 871 |
if source_fetchers:
|
| 873 | 872 |
for fetcher in source_fetchers:
|
| 874 | 873 |
alias = fetcher._get_alias()
|
| 875 |
- success = False
|
|
| 876 | 874 |
for uri in project.get_alias_uris(alias, first_pass=self.__first_pass):
|
| 877 | 875 |
try:
|
| 878 | 876 |
fetcher.fetch(uri)
|
| ... | ... | @@ -881,10 +879,16 @@ class Source(Plugin): |
| 881 | 879 |
except BstError as e:
|
| 882 | 880 |
last_error = e
|
| 883 | 881 |
continue
|
| 884 |
- success = True
|
|
| 882 |
+ |
|
| 883 |
+ # No error, we're done with this fetcher
|
|
| 885 | 884 |
break
|
| 886 |
- if not success:
|
|
| 885 |
+ |
|
| 886 |
+ else:
|
|
| 887 |
+ # No break occurred, raise the last detected error
|
|
| 887 | 888 |
raise last_error
|
| 889 |
+ |
|
| 890 |
+ # Default codepath is to reinstantiate the Source
|
|
| 891 |
+ #
|
|
| 888 | 892 |
else:
|
| 889 | 893 |
alias = self._get_alias()
|
| 890 | 894 |
if self.__first_pass:
|
| ... | ... | @@ -908,18 +912,22 @@ class Source(Plugin): |
| 908 | 912 |
except BstError as e:
|
| 909 | 913 |
last_error = e
|
| 910 | 914 |
continue
|
| 915 |
+ |
|
| 916 |
+ # No error, we're done here
|
|
| 911 | 917 |
return
|
| 918 |
+ |
|
| 919 |
+ # Re raise the last detected error
|
|
| 912 | 920 |
raise last_error
|
| 913 | 921 |
|
| 914 | 922 |
# Tries to call track for every mirror, stopping once it succeeds
|
| 915 | 923 |
def __do_track(self, **kwargs):
|
| 916 | 924 |
project = self._get_project()
|
| 917 |
- # If there are no mirrors, or no aliases to replace, there's nothing to do here.
|
|
| 918 | 925 |
alias = self._get_alias()
|
| 919 | 926 |
if self.__first_pass:
|
| 920 | 927 |
mirrors = project.first_pass_config.mirrors
|
| 921 | 928 |
else:
|
| 922 | 929 |
mirrors = project.config.mirrors
|
| 930 |
+ # If there are no mirrors, or no aliases to replace, there's nothing to do here.
|
|
| 923 | 931 |
if not mirrors or not alias:
|
| 924 | 932 |
return self.track(**kwargs)
|
| 925 | 933 |
|
| ... | ... | @@ -988,3 +996,11 @@ class Source(Plugin): |
| 988 | 996 |
|
| 989 | 997 |
if src.get_consistency() == Consistency.RESOLVED:
|
| 990 | 998 |
src._fetch(previous_sources[0:index])
|
| 999 |
+ |
|
| 1000 |
+ |
|
| 1001 |
+def _extract_alias(url):
|
|
| 1002 |
+ parts = url.split(utils._ALIAS_SEPARATOR, 1)
|
|
| 1003 |
+ if len(parts) > 1 and not parts[0].lower() in utils._URI_SCHEMES:
|
|
| 1004 |
+ return parts[0]
|
|
| 1005 |
+ else:
|
|
| 1006 |
+ return ""
|
| ... | ... | @@ -47,6 +47,7 @@ _magic_timestamp = calendar.timegm([2011, 11, 11, 11, 11, 11]) |
| 47 | 47 |
|
| 48 | 48 |
# The separator we use for user specified aliases
|
| 49 | 49 |
_ALIAS_SEPARATOR = ':'
|
| 50 |
+_URI_SCHEMES = ["http", "https", "ftp", "file", "git", "sftp", "ssh"]
|
|
| 50 | 51 |
|
| 51 | 52 |
|
| 52 | 53 |
class UtilError(BstError):
|
| ... | ... | @@ -8,3 +8,4 @@ pytest-env |
| 8 | 8 |
pytest-pep8
|
| 9 | 9 |
pytest-pylint
|
| 10 | 10 |
pytest-xdist
|
| 11 |
+pytest-timeout
|
| ... | ... | @@ -25,7 +25,14 @@ import subprocess |
| 25 | 25 |
import sys
|
| 26 | 26 |
import versioneer
|
| 27 | 27 |
|
| 28 |
-if sys.version_info[0] != 3 or sys.version_info[1] < 5:
|
|
| 28 |
+ |
|
| 29 |
+##################################################################
|
|
| 30 |
+# Python requirements
|
|
| 31 |
+##################################################################
|
|
| 32 |
+REQUIRED_PYTHON_MAJOR = 3
|
|
| 33 |
+REQUIRED_PYTHON_MINOR = 5
|
|
| 34 |
+ |
|
| 35 |
+if sys.version_info[0] != REQUIRED_PYTHON_MAJOR or sys.version_info[1] < REQUIRED_PYTHON_MINOR:
|
|
| 29 | 36 |
print("BuildStream requires Python >= 3.5")
|
| 30 | 37 |
sys.exit(1)
|
| 31 | 38 |
|
| ... | ... | @@ -242,11 +249,28 @@ setup(name='BuildStream', |
| 242 | 249 |
|
| 243 | 250 |
author='BuildStream Developers',
|
| 244 | 251 |
author_email='buildstream-list gnome org',
|
| 252 |
+ classifiers=[
|
|
| 253 |
+ 'Environment :: Console',
|
|
| 254 |
+ 'Intended Audience :: Developers',
|
|
| 255 |
+ 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
|
|
| 256 |
+ 'Operating System :: POSIX',
|
|
| 257 |
+ 'Programming Language :: Python :: 3',
|
|
| 258 |
+ 'Programming Language :: Python :: 3.5',
|
|
| 259 |
+ 'Programming Language :: Python :: 3.6',
|
|
| 260 |
+ 'Programming Language :: Python :: 3.7',
|
|
| 261 |
+ 'Topic :: Software Development :: Build Tools'
|
|
| 262 |
+ ],
|
|
| 245 | 263 |
description='A framework for modelling build pipelines in YAML',
|
| 246 | 264 |
license='LGPL',
|
| 247 | 265 |
long_description=long_description,
|
| 248 | 266 |
long_description_content_type='text/x-rst; charset=UTF-8',
|
| 249 | 267 |
url='https://gitlab.com/BuildStream/buildstream',
|
| 268 |
+ project_urls={
|
|
| 269 |
+ 'Documentation': 'https://buildstream.gitlab.io/buildstream/',
|
|
| 270 |
+ 'Tracker': 'https://gitlab.com/BuildStream/buildstream/issues',
|
|
| 271 |
+ 'Mailing List': 'https://mail.gnome.org/mailman/listinfo/buildstream-list'
|
|
| 272 |
+ },
|
|
| 273 |
+ python_requires='~={}.{}'.format(REQUIRED_PYTHON_MAJOR, REQUIRED_PYTHON_MINOR),
|
|
| 250 | 274 |
packages=find_packages(exclude=('tests', 'tests.*')),
|
| 251 | 275 |
package_data={'buildstream': ['plugins/*/*.py', 'plugins/*/*.yaml',
|
| 252 | 276 |
'data/*.yaml', 'data/*.sh.in']},
|
| 1 | 1 |
import os
|
| 2 | 2 |
import pytest
|
| 3 |
+import sys
|
|
| 3 | 4 |
from buildstream import _yaml
|
| 4 | 5 |
from buildstream._exceptions import ErrorDomain, LoadErrorReason
|
| 5 | 6 |
from tests.testutils.runcli import cli
|
| ... | ... | @@ -72,3 +73,20 @@ def test_missing_variable(cli, datafiles, tmpdir): |
| 72 | 73 |
])
|
| 73 | 74 |
result.assert_main_error(ErrorDomain.LOAD,
|
| 74 | 75 |
LoadErrorReason.UNRESOLVED_VARIABLE)
|
| 76 |
+ |
|
| 77 |
+ |
|
| 78 |
+@pytest.mark.timeout(3, method="signal")
|
|
| 79 |
+@pytest.mark.datafiles(os.path.join(DATA_DIR, 'cyclic_variables'))
|
|
| 80 |
+def test_cyclic_variables(cli, datafiles):
|
|
| 81 |
+ print_warning("Performing cyclic test, if this test times out it will " +
|
|
| 82 |
+ "exit the test sequence")
|
|
| 83 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
| 84 |
+ result = cli.run(project=project, silent=True, args=[
|
|
| 85 |
+ "build", "cyclic.bst"
|
|
| 86 |
+ ])
|
|
| 87 |
+ result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.RECURSIVE_VARIABLE)
|
|
| 88 |
+ |
|
| 89 |
+ |
|
| 90 |
+def print_warning(msg):
|
|
| 91 |
+ RED, END = "\033[91m", "\033[0m"
|
|
| 92 |
+ print(("\n{}{}{}").format(RED, msg, END), file=sys.stderr)
|
| 1 |
+kind: manual
|
|
| 2 |
+ |
|
| 3 |
+variables:
|
|
| 4 |
+ a: "%{prefix}/a"
|
|
| 5 |
+ prefix: "%{a}/some_prefix/"
|
|
| \ No newline at end of file |
| 1 |
+name: test
|
| ... | ... | @@ -142,10 +142,6 @@ def test_mirror_fetch(cli, tmpdir, datafiles, kind): |
| 142 | 142 |
@pytest.mark.datafiles(DATA_DIR)
|
| 143 | 143 |
@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS])
|
| 144 | 144 |
def test_mirror_fetch_upstream_absent(cli, tmpdir, datafiles, kind):
|
| 145 |
- if kind == 'ostree':
|
|
| 146 |
- # FIXME: Mirroring fallback fails with ostree
|
|
| 147 |
- pytest.skip("Bug #538 - ostree mirror fallback breaks assertion")
|
|
| 148 |
- |
|
| 149 | 145 |
bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr')
|
| 150 | 146 |
dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr')
|
| 151 | 147 |
upstream_repodir = os.path.join(str(tmpdir), 'upstream')
|
| ... | ... | @@ -338,3 +338,22 @@ def test_pull_missing_blob(cli, tmpdir, datafiles): |
| 338 | 338 |
|
| 339 | 339 |
# Assert that no artifacts were pulled
|
| 340 | 340 |
assert len(result.get_pulled_elements()) == 0
|
| 341 |
+ |
|
| 342 |
+ |
|
| 343 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
| 344 |
+def test_pull_missing_notifies_user(caplog, cli, tmpdir, datafiles):
|
|
| 345 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
| 346 |
+ caplog.set_level(1)
|
|
| 347 |
+ |
|
| 348 |
+ with create_artifact_share(os.path.join(str(tmpdir), 'artifactshare')) as share:
|
|
| 349 |
+ |
|
| 350 |
+ cli.configure({
|
|
| 351 |
+ 'artifacts': {'url': share.repo}
|
|
| 352 |
+ })
|
|
| 353 |
+ result = cli.run(project=project, args=['build', 'target.bst'])
|
|
| 354 |
+ |
|
| 355 |
+ result.assert_success()
|
|
| 356 |
+ assert not result.get_pulled_elements(), \
|
|
| 357 |
+ "No elements should have been pulled since the cache was empty"
|
|
| 358 |
+ |
|
| 359 |
+ assert "SKIPPED Remote ({}) does not have".format(share.repo) in result.stderr
|
| ... | ... | @@ -174,9 +174,8 @@ def test_filter_workspace_reset(datafiles, cli, tmpdir): |
| 174 | 174 |
|
| 175 | 175 |
|
| 176 | 176 |
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
|
| 177 |
-@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS if kind not in ("patch", "local")])
|
|
| 178 |
-def test_filter_track(datafiles, cli, tmpdir, kind):
|
|
| 179 |
- repo = create_repo(kind, str(tmpdir))
|
|
| 177 |
+def test_filter_track(datafiles, cli, tmpdir):
|
|
| 178 |
+ repo = create_repo('git', str(tmpdir))
|
|
| 180 | 179 |
ref = repo.create(os.path.join(str(datafiles), "files"))
|
| 181 | 180 |
elements_dir = os.path.join(str(tmpdir), "elements")
|
| 182 | 181 |
project = str(tmpdir)
|
| ... | ... | @@ -228,9 +227,8 @@ def test_filter_track(datafiles, cli, tmpdir, kind): |
| 228 | 227 |
|
| 229 | 228 |
|
| 230 | 229 |
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
|
| 231 |
-@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS if kind not in ("patch", "local")])
|
|
| 232 |
-def test_filter_track_excepted(datafiles, cli, tmpdir, kind):
|
|
| 233 |
- repo = create_repo(kind, str(tmpdir))
|
|
| 230 |
+def test_filter_track_excepted(datafiles, cli, tmpdir):
|
|
| 231 |
+ repo = create_repo('git', str(tmpdir))
|
|
| 234 | 232 |
ref = repo.create(os.path.join(str(datafiles), "files"))
|
| 235 | 233 |
elements_dir = os.path.join(str(tmpdir), "elements")
|
| 236 | 234 |
project = str(tmpdir)
|
| ... | ... | @@ -282,9 +280,8 @@ def test_filter_track_excepted(datafiles, cli, tmpdir, kind): |
| 282 | 280 |
|
| 283 | 281 |
|
| 284 | 282 |
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
|
| 285 |
-@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS if kind not in ("patch", "local")])
|
|
| 286 |
-def test_filter_track_multi_to_one(datafiles, cli, tmpdir, kind):
|
|
| 287 |
- repo = create_repo(kind, str(tmpdir))
|
|
| 283 |
+def test_filter_track_multi_to_one(datafiles, cli, tmpdir):
|
|
| 284 |
+ repo = create_repo('git', str(tmpdir))
|
|
| 288 | 285 |
ref = repo.create(os.path.join(str(datafiles), "files"))
|
| 289 | 286 |
elements_dir = os.path.join(str(tmpdir), "elements")
|
| 290 | 287 |
project = str(tmpdir)
|
| ... | ... | @@ -336,9 +333,8 @@ def test_filter_track_multi_to_one(datafiles, cli, tmpdir, kind): |
| 336 | 333 |
|
| 337 | 334 |
|
| 338 | 335 |
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
|
| 339 |
-@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS if kind not in ("patch", "local")])
|
|
| 340 |
-def test_filter_track_multi(datafiles, cli, tmpdir, kind):
|
|
| 341 |
- repo = create_repo(kind, str(tmpdir))
|
|
| 336 |
+def test_filter_track_multi(datafiles, cli, tmpdir):
|
|
| 337 |
+ repo = create_repo('git', str(tmpdir))
|
|
| 342 | 338 |
ref = repo.create(os.path.join(str(datafiles), "files"))
|
| 343 | 339 |
elements_dir = os.path.join(str(tmpdir), "elements")
|
| 344 | 340 |
project = str(tmpdir)
|
| ... | ... | @@ -398,9 +394,8 @@ def test_filter_track_multi(datafiles, cli, tmpdir, kind): |
| 398 | 394 |
|
| 399 | 395 |
|
| 400 | 396 |
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
|
| 401 |
-@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS if kind not in ("patch", "local")])
|
|
| 402 |
-def test_filter_track_multi_exclude(datafiles, cli, tmpdir, kind):
|
|
| 403 |
- repo = create_repo(kind, str(tmpdir))
|
|
| 397 |
+def test_filter_track_multi_exclude(datafiles, cli, tmpdir):
|
|
| 398 |
+ repo = create_repo('git', str(tmpdir))
|
|
| 404 | 399 |
ref = repo.create(os.path.join(str(datafiles), "files"))
|
| 405 | 400 |
elements_dir = os.path.join(str(tmpdir), "elements")
|
| 406 | 401 |
project = str(tmpdir)
|
| ... | ... | @@ -94,14 +94,28 @@ class Result(): |
| 94 | 94 |
# error_reason (any): The reason field of the error which occurred
|
| 95 | 95 |
# fail_message (str): An optional message to override the automatic
|
| 96 | 96 |
# assertion error messages
|
| 97 |
+ # debug (bool): If true, prints information regarding the exit state of the result()
|
|
| 97 | 98 |
# Raises:
|
| 98 | 99 |
# (AssertionError): If any of the assertions fail
|
| 99 | 100 |
#
|
| 100 | 101 |
def assert_main_error(self,
|
| 101 | 102 |
error_domain,
|
| 102 | 103 |
error_reason,
|
| 103 |
- fail_message=''):
|
|
| 104 |
- |
|
| 104 |
+ fail_message='',
|
|
| 105 |
+ *, debug=False):
|
|
| 106 |
+ if debug:
|
|
| 107 |
+ print(
|
|
| 108 |
+ """
|
|
| 109 |
+ Exit code: {}
|
|
| 110 |
+ Exception: {}
|
|
| 111 |
+ Domain: {}
|
|
| 112 |
+ Reason: {}
|
|
| 113 |
+ """.format(
|
|
| 114 |
+ self.exit_code,
|
|
| 115 |
+ self.exception,
|
|
| 116 |
+ self.exception.domain,
|
|
| 117 |
+ self.exception.reason
|
|
| 118 |
+ ))
|
|
| 105 | 119 |
assert self.exit_code == -1, fail_message
|
| 106 | 120 |
assert self.exc is not None, fail_message
|
| 107 | 121 |
assert self.exception is not None, fail_message
|
