[Notes] [Git][BuildStream/buildstream][Qinusty/526-fail-on-warnings] 8 commits: Fix tracking of junctions used in project.conf.



Title: GitLab

Qinusty pushed to branch Qinusty/526-fail-on-warnings at BuildStream / buildstream

Commits:

20 changed files:

Changes:

  • buildstream/_project.py
    ... ... @@ -31,6 +31,7 @@ from ._options import OptionPool
    31 31
     from ._artifactcache import ArtifactCache
    
    32 32
     from ._elementfactory import ElementFactory
    
    33 33
     from ._sourcefactory import SourceFactory
    
    34
    +from .plugin import CoreWarnings
    
    34 35
     from ._projectrefs import ProjectRefs, ProjectRefStorage
    
    35 36
     from ._versions import BST_FORMAT_VERSION
    
    36 37
     from ._loader import Loader
    
    ... ... @@ -105,7 +106,7 @@ class Project():
    105 106
             self.first_pass_config = ProjectConfig()
    
    106 107
     
    
    107 108
             self.junction = junction                 # The junction Element object, if this is a subproject
    
    108
    -        self.fail_on_overlap = False             # Whether overlaps are treated as errors
    
    109
    +
    
    109 110
             self.ref_storage = None                  # ProjectRefStorage setting
    
    110 111
             self.base_environment = {}               # The base set of environment variables
    
    111 112
             self.base_env_nocache = None             # The base nocache mask (list) for the environment
    
    ... ... @@ -120,6 +121,8 @@ class Project():
    120 121
             self._cli_options = cli_options
    
    121 122
             self._cache_key = None
    
    122 123
     
    
    124
    +        self._fatal_warnings = []             # A list of warnings which should trigger an error
    
    125
    +
    
    123 126
             self._shell_command = []      # The default interactive shell command
    
    124 127
             self._shell_environment = {}  # Statically set environment vars
    
    125 128
             self._shell_host_files = []   # A list of HostMount objects
    
    ... ... @@ -456,7 +459,7 @@ class Project():
    456 459
                 'split-rules', 'elements', 'plugins',
    
    457 460
                 'aliases', 'name',
    
    458 461
                 'artifacts', 'options',
    
    459
    -            'fail-on-overlap', 'shell',
    
    462
    +            'fail-on-overlap', 'shell', 'fatal-warnings',
    
    460 463
                 'ref-storage', 'sandbox', 'mirrors'
    
    461 464
             ])
    
    462 465
     
    
    ... ... @@ -478,8 +481,25 @@ class Project():
    478 481
             # Load project split rules
    
    479 482
             self._splits = _yaml.node_get(config, Mapping, 'split-rules')
    
    480 483
     
    
    481
    -        # Fail on overlap
    
    482
    -        self.fail_on_overlap = _yaml.node_get(config, bool, 'fail-on-overlap')
    
    484
    +        # Fatal warnings
    
    485
    +        self._fatal_warnings = _yaml.node_get(config, list, 'fatal-warnings', default_value=[])
    
    486
    +
    
    487
    +        # Support backwards compatibility for fail-on-overlap
    
    488
    +        fail_on_overlap = _yaml.node_get(config, bool, 'fail-on-overlap', default_value=None)
    
    489
    +
    
    490
    +        if (CoreWarnings.OVERLAPS not in self._fatal_warnings) and fail_on_overlap:
    
    491
    +            self._fatal_warnings.append(CoreWarnings.OVERLAPS)
    
    492
    +
    
    493
    +        # Deprecation check
    
    494
    +        if fail_on_overlap is not None:
    
    495
    +            self._context.message(
    
    496
    +                Message(
    
    497
    +                    None,
    
    498
    +                    MessageType.WARN,
    
    499
    +                    "Use of fail-on-overlap within project.conf " +
    
    500
    +                    "is deprecated. Consider using fatal-warnings instead."
    
    501
    +                )
    
    502
    +            )
    
    483 503
     
    
    484 504
             # Load project.refs if it exists, this may be ignored.
    
    485 505
             if self.ref_storage == ProjectRefStorage.PROJECT_REFS:
    
    ... ... @@ -712,3 +732,17 @@ class Project():
    712 732
                     # paths are passed in relative to the project, but must be absolute
    
    713 733
                     origin_dict['path'] = os.path.join(self.directory, path)
    
    714 734
                 destination.append(origin_dict)
    
    735
    +
    
    736
    +    # _warning_is_fatal():
    
    737
    +    #
    
    738
    +    # Returns true if the warning in question should be considered fatal based on
    
    739
    +    # the project configuration.
    
    740
    +    #
    
    741
    +    # Args:
    
    742
    +    #   warning_str (str): The warning configuration string to check against
    
    743
    +    #
    
    744
    +    # Returns:
    
    745
    +    #    (bool): True if the warning should be considered fatal and cause an error.
    
    746
    +    #
    
    747
    +    def _warning_is_fatal(self, warning_str):
    
    748
    +        return warning_str in self._fatal_warnings

  • buildstream/_stream.py
    ... ... @@ -267,8 +267,11 @@ class Stream():
    267 267
                   except_targets=None,
    
    268 268
                   cross_junctions=False):
    
    269 269
     
    
    270
    +        # We pass no target to build. Only to track. Passing build targets
    
    271
    +        # would fully load project configuration which might not be
    
    272
    +        # possible before tracking is done.
    
    270 273
             _, elements = \
    
    271
    -            self._load(targets, targets,
    
    274
    +            self._load([], targets,
    
    272 275
                            selection=selection, track_selection=selection,
    
    273 276
                            except_targets=except_targets,
    
    274 277
                            track_except_targets=except_targets,
    
    ... ... @@ -824,6 +827,12 @@ class Stream():
    824 827
         #
    
    825 828
         # A convenience method for loading element lists
    
    826 829
         #
    
    830
    +    # If `targets` is not empty used project configuration will be
    
    831
    +    # fully loaded. If `targets` is empty, tracking will still be
    
    832
    +    # resolved for elements in `track_targets`, but no build pipeline
    
    833
    +    # will be resolved. This is behavior is import for track() to
    
    834
    +    # not trigger full loading of project configuration.
    
    835
    +    #
    
    827 836
         # Args:
    
    828 837
         #    targets (list of str): Main targets to load
    
    829 838
         #    track_targets (list of str): Tracking targets
    
    ... ... @@ -871,7 +880,7 @@ class Stream():
    871 880
             #
    
    872 881
             # This can happen with `bst build --track`
    
    873 882
             #
    
    874
    -        if not self._pipeline.targets_include(elements, track_elements):
    
    883
    +        if targets and not self._pipeline.targets_include(elements, track_elements):
    
    875 884
                 raise StreamError("Specified tracking targets that are not "
    
    876 885
                                   "within the scope of primary targets")
    
    877 886
     
    
    ... ... @@ -907,6 +916,10 @@ class Stream():
    907 916
             for element in track_selected:
    
    908 917
                 element._schedule_tracking()
    
    909 918
     
    
    919
    +        if not targets:
    
    920
    +            self._pipeline.resolve_elements(track_selected)
    
    921
    +            return [], track_selected
    
    922
    +
    
    910 923
             # ArtifactCache.setup_remotes expects all projects to be fully loaded
    
    911 924
             for project in self._context.get_projects():
    
    912 925
                 project.ensure_fully_loaded()
    

  • buildstream/_versions.py
    ... ... @@ -23,7 +23,7 @@
    23 23
     # This version is bumped whenever enhancements are made
    
    24 24
     # to the `project.conf` format or the core element format.
    
    25 25
     #
    
    26
    -BST_FORMAT_VERSION = 13
    
    26
    +BST_FORMAT_VERSION = 14
    
    27 27
     
    
    28 28
     
    
    29 29
     # The base BuildStream artifact version
    
    ... ... @@ -33,4 +33,4 @@ BST_FORMAT_VERSION = 13
    33 33
     # or if buildstream was changed in a way which can cause
    
    34 34
     # the same cache key to produce something that is no longer
    
    35 35
     # the same.
    
    36
    -BST_CORE_ARTIFACT_VERSION = 4
    36
    +BST_CORE_ARTIFACT_VERSION = 5

  • buildstream/data/projectconfig.yaml
    ... ... @@ -13,10 +13,6 @@ element-path: .
    13 13
     # Store source references in element files
    
    14 14
     ref-storage: inline
    
    15 15
     
    
    16
    -# Overlaps are just warnings
    
    17
    -fail-on-overlap: False
    
    18
    -
    
    19
    -
    
    20 16
     # Variable Configuration
    
    21 17
     #
    
    22 18
     variables:
    

  • buildstream/element.py
    ... ... @@ -94,6 +94,7 @@ from . import _cachekey
    94 94
     from . import _signals
    
    95 95
     from . import _site
    
    96 96
     from ._platform import Platform
    
    97
    +from .plugin import CoreWarnings
    
    97 98
     from .sandbox._config import SandboxConfig
    
    98 99
     
    
    99 100
     from .storage.directory import Directory
    
    ... ... @@ -746,32 +747,23 @@ class Element(Plugin):
    746 747
                     ignored[dep.name] = result.ignored
    
    747 748
     
    
    748 749
             if overlaps:
    
    749
    -            overlap_error = overlap_warning = False
    
    750
    -            error_detail = warning_detail = "Staged files overwrite existing files in staging area:\n"
    
    750
    +            overlap_warning = False
    
    751
    +            warning_detail = "Staged files overwrite existing files in staging area:\n"
    
    751 752
                 for f, elements in overlaps.items():
    
    752
    -                overlap_error_elements = []
    
    753 753
                     overlap_warning_elements = []
    
    754 754
                     # The bottom item overlaps nothing
    
    755 755
                     overlapping_elements = elements[1:]
    
    756 756
                     for elm in overlapping_elements:
    
    757 757
                         element = self.search(scope, elm)
    
    758
    -                    element_project = element._get_project()
    
    759 758
                         if not element.__file_is_whitelisted(f):
    
    760
    -                        if element_project.fail_on_overlap:
    
    761
    -                            overlap_error_elements.append(elm)
    
    762
    -                            overlap_error = True
    
    763
    -                        else:
    
    764
    -                            overlap_warning_elements.append(elm)
    
    765
    -                            overlap_warning = True
    
    759
    +                        overlap_warning_elements.append(elm)
    
    760
    +                        overlap_warning = True
    
    766 761
     
    
    767 762
                     warning_detail += _overlap_error_detail(f, overlap_warning_elements, elements)
    
    768
    -                error_detail += _overlap_error_detail(f, overlap_error_elements, elements)
    
    769 763
     
    
    770 764
                 if overlap_warning:
    
    771
    -                self.warn("Non-whitelisted overlaps detected", detail=warning_detail)
    
    772
    -            if overlap_error:
    
    773
    -                raise ElementError("Non-whitelisted overlaps detected and fail-on-overlaps is set",
    
    774
    -                                   detail=error_detail, reason="overlap-error")
    
    765
    +                self.warn("Non-whitelisted overlaps detected", detail=warning_detail,
    
    766
    +                          warning_token=CoreWarnings.OVERLAPS)
    
    775 767
     
    
    776 768
             if ignored:
    
    777 769
                 detail = "Not staging files which would replace non-empty directories:\n"
    
    ... ... @@ -2054,9 +2046,7 @@ class Element(Plugin):
    2054 2046
                     'cache': type(self.__artifacts).__name__
    
    2055 2047
                 }
    
    2056 2048
     
    
    2057
    -            # fail-on-overlap setting cannot affect elements without dependencies
    
    2058
    -            if project.fail_on_overlap and dependencies:
    
    2059
    -                self.__cache_key_dict['fail-on-overlap'] = True
    
    2049
    +            self.__cache_key_dict['fatal-warnings'] = sorted(project._fatal_warnings)
    
    2060 2050
     
    
    2061 2051
             cache_key_dict = self.__cache_key_dict.copy()
    
    2062 2052
             cache_key_dict['dependencies'] = dependencies
    

  • buildstream/plugin.py
    ... ... @@ -47,6 +47,23 @@ it is mandatory to implement the following abstract methods:
    47 47
       Once all configuration has been loaded and preflight checks have passed,
    
    48 48
       this method is used to inform the core of a plugin's unique configuration.
    
    49 49
     
    
    50
    +Configurable Warnings
    
    51
    +---------------------
    
    52
    +Warnings raised through calling :func:`Plugin.warn() <buildstream.plugin.Plugin.warn>` can provide an optional
    
    53
    +parameter ``warning_token``, this will raise a :class:`PluginError` if the warning is configured as fatal within
    
    54
    +the project configuration.
    
    55
    +
    
    56
    +Configurable warnings will be prefixed with :func:`Plugin.get_kind() <buildstream.plugin.Plugin.get_kind>`
    
    57
    +within buildstream and must be prefixed as such in project configurations. For more detail on project configuration
    
    58
    +see :ref:`Configurable Warnings <configurable_warnings>`.
    
    59
    +
    
    60
    +It is important to document these warnings in your plugin documentation to allow users to make full use of them
    
    61
    +while configuring their projects.
    
    62
    +
    
    63
    +Example
    
    64
    +~~~~~~~
    
    65
    +If the :class:`git <buildstream.plugins.sources.git.GitSource>` plugin uses the warning ``"inconsistent-submodule"``
    
    66
    +then it could be referenced in project configuration as ``"git:inconsistent-submodule"``.
    
    50 67
     
    
    51 68
     Plugin Structure
    
    52 69
     ----------------
    
    ... ... @@ -166,7 +183,6 @@ class Plugin():
    166 183
             # Infer the kind identifier
    
    167 184
             modulename = type(self).__module__
    
    168 185
             self.__kind = modulename.split('.')[-1]
    
    169
    -
    
    170 186
             self.debug("Created: {}".format(self))
    
    171 187
     
    
    172 188
         def __del__(self):
    
    ... ... @@ -473,14 +489,28 @@ class Plugin():
    473 489
             """
    
    474 490
             self.__message(MessageType.INFO, brief, detail=detail)
    
    475 491
     
    
    476
    -    def warn(self, brief, *, detail=None):
    
    477
    -        """Print a warning message
    
    492
    +    def warn(self, brief, *, detail=None, warning_token=None):
    
    493
    +        """Print a warning message, checks warning_token against project configuration
    
    478 494
     
    
    479 495
             Args:
    
    480 496
                brief (str): The brief message
    
    481 497
                detail (str): An optional detailed message, can be multiline output
    
    498
    +           warning_token (str): An optional configurable warning assosciated with this warning,
    
    499
    +                                this will cause PluginError to be raised if this warning is configured as fatal.
    
    500
    +                                (*Since 1.4*)
    
    501
    +
    
    502
    +        Raises:
    
    503
    +           (:class:`.PluginError`): When warning_token is considered fatal by the project configuration
    
    482 504
             """
    
    483
    -        self.__message(MessageType.WARN, brief, detail=detail)
    
    505
    +        if warning_token:
    
    506
    +            warning_token = _prefix_warning(self, warning_token)
    
    507
    +            brief = "[{}]: {}".format(warning_token, brief)
    
    508
    +            project = self._get_project()
    
    509
    +
    
    510
    +            if project._warning_is_fatal(warning_token):
    
    511
    +                raise PluginError(message="{}\n{}".format(brief, detail), reason=warning_token)
    
    512
    +
    
    513
    +        self.__message(MessageType.WARN, brief=brief, detail=detail)
    
    484 514
     
    
    485 515
         def log(self, brief, *, detail=None):
    
    486 516
             """Log a message into the plugin's log file
    
    ... ... @@ -709,6 +739,32 @@ class Plugin():
    709 739
                 return self.name
    
    710 740
     
    
    711 741
     
    
    742
    +class CoreWarnings():
    
    743
    +    """CoreWarnings()
    
    744
    +
    
    745
    +    Some common warnings which are raised by core functionalities within BuildStream are found in this class.
    
    746
    +    """
    
    747
    +
    
    748
    +    OVERLAPS = "overlaps"
    
    749
    +    """
    
    750
    +    This warning will be produced when buildstream detects an overlap on an element
    
    751
    +        which is not whitelisted. See :ref:`Overlap Whitelist <public_overlap_whitelist>`
    
    752
    +    """
    
    753
    +
    
    754
    +    REF_NOT_IN_TRACK = "ref-not-in-track"
    
    755
    +    """
    
    756
    +    This warning will be produced when a source is configured with a reference
    
    757
    +    which is found to be invalid based on the configured track
    
    758
    +    """
    
    759
    +
    
    760
    +
    
    761
    +__CORE_WARNINGS = [
    
    762
    +    value
    
    763
    +    for name, value in CoreWarnings.__dict__.items()
    
    764
    +    if not name.startswith("__")
    
    765
    +]
    
    766
    +
    
    767
    +
    
    712 768
     # Hold on to a lookup table by counter of all instantiated plugins.
    
    713 769
     # We use this to send the id back from child processes so we can lookup
    
    714 770
     # corresponding element/source in the master process.
    
    ... ... @@ -739,6 +795,23 @@ def _plugin_lookup(unique_id):
    739 795
         return __PLUGINS_TABLE[unique_id]
    
    740 796
     
    
    741 797
     
    
    798
    +# _prefix_warning():
    
    799
    +#
    
    800
    +# Prefix a warning with the plugin kind. CoreWarnings are not prefixed.
    
    801
    +#
    
    802
    +# Args:
    
    803
    +#   plugin (Plugin): The plugin which raised the warning
    
    804
    +#   warning (str): The warning to prefix
    
    805
    +#
    
    806
    +# Returns:
    
    807
    +#    (str): A prefixed warning
    
    808
    +#
    
    809
    +def _prefix_warning(plugin, warning):
    
    810
    +    if any((warning is core_warning for core_warning in __CORE_WARNINGS)):
    
    811
    +        return warning
    
    812
    +    return "{}:{}".format(plugin.get_kind(), warning)
    
    813
    +
    
    814
    +
    
    742 815
     # No need for unregister, WeakValueDictionary() will remove entries
    
    743 816
     # in itself when the referenced plugins are garbage collected.
    
    744 817
     def _plugin_register(plugin):
    

  • buildstream/plugins/sources/git.py
    ... ... @@ -68,6 +68,12 @@ git - stage files from a git repository
    68 68
            url: upstream:baz.git
    
    69 69
            checkout: False
    
    70 70
     
    
    71
    +**Configurable Warnings:**
    
    72
    +
    
    73
    +This plugin provides the following configurable warnings:
    
    74
    +
    
    75
    +- 'git:inconsistent-submodule' - A submodule was found to be missing from the underlying git repository.
    
    76
    +
    
    71 77
     """
    
    72 78
     
    
    73 79
     import os
    
    ... ... @@ -84,6 +90,9 @@ from buildstream import utils
    84 90
     
    
    85 91
     GIT_MODULES = '.gitmodules'
    
    86 92
     
    
    93
    +# Warnings
    
    94
    +INCONSISTENT_SUBMODULE = "inconsistent-submodules"
    
    95
    +
    
    87 96
     
    
    88 97
     # Because of handling of submodules, we maintain a GitMirror
    
    89 98
     # for the primary git source and also for each submodule it
    
    ... ... @@ -283,7 +292,7 @@ class GitMirror(SourceFetcher):
    283 292
                          "underlying git repository with `git submodule add`."
    
    284 293
     
    
    285 294
                 self.source.warn("{}: Ignoring inconsistent submodule '{}'"
    
    286
    -                             .format(self.source, submodule), detail=detail)
    
    295
    +                             .format(self.source, submodule), detail=detail, warning_token=INCONSISTENT_SUBMODULE)
    
    287 296
     
    
    288 297
                 return None
    
    289 298
     
    

  • doc/source/format_project.rst
    ... ... @@ -126,22 +126,29 @@ following to your ``project.conf``:
    126 126
        The ``ref-storage`` configuration is available since :ref:`format version 8 <project_format_version>`
    
    127 127
     
    
    128 128
     
    
    129
    -Fail on overlaps
    
    130
    -~~~~~~~~~~~~~~~~
    
    131
    -When multiple elements are staged, there's a possibility that different
    
    132
    -elements will try and stage different versions of the same file.
    
    129
    +.. _configurable_warnings:
    
    133 130
     
    
    134
    -When ``fail-on-overlap`` is true, if an overlap is detected
    
    135
    -that hasn't been allowed by the element's
    
    136
    -:ref:`overlap whitelist<public_overlap_whitelist>`,
    
    137
    -then an error will be raised and the build will fail.
    
    131
    +Configurable Warnings
    
    132
    +~~~~~~~~~~~~~~~~~~~~~
    
    133
    +Warnings can be configured as fatal using the ``fatal-warnings`` configuration item.
    
    134
    +When a warning is configured as fatal, where a warning would usually be thrown instead an error will be thrown
    
    135
    +causing the build to fail.
    
    138 136
     
    
    139
    -otherwise, a warning will be raised indicating which files had overlaps,
    
    140
    -and the order that the elements were overlapped.
    
    137
    +Individual warnings can be configured as fatal by setting ``fatal-warnings`` to a list of warnings.
    
    141 138
     
    
    142 139
     .. code:: yaml
    
    143 140
     
    
    144
    -  fail-on-overlap: true
    
    141
    +  fatal-warnings:
    
    142
    +  - overlaps
    
    143
    +  - ref-not-in-track
    
    144
    +  - <plugin>:<warning>
    
    145
    +
    
    146
    +BuildStream provides a collection of :class:`Core Warnings <buildstream.plugin.CoreWarnings>` which may be raised
    
    147
    +by a variety of plugins. Other configurable warnings are plugin specific and should be noted within their individual documentation.
    
    148
    +
    
    149
    +.. note::
    
    150
    +
    
    151
    +  The ``fatal-warnings`` configuration is available since :ref:`format version 14 <project_format_version>`
    
    145 152
     
    
    146 153
     
    
    147 154
     .. _project_source_aliases:
    

  • setup.py
    ... ... @@ -258,7 +258,7 @@ setup(name='BuildStream',
    258 258
           install_requires=[
    
    259 259
               'setuptools',
    
    260 260
               'psutil',
    
    261
    -          'ruamel.yaml <= 0.15',
    
    261
    +          'ruamel.yaml < 0.15.52',
    
    262 262
               'pluginbase',
    
    263 263
               'Click',
    
    264 264
               'blessings',
    

  • tests/cachekey/cachekey.py
    ... ... @@ -37,7 +37,8 @@
    37 37
     #
    
    38 38
     from tests.testutils.runcli import cli
    
    39 39
     from tests.testutils.site import HAVE_BZR, HAVE_GIT, HAVE_OSTREE, IS_LINUX
    
    40
    -
    
    40
    +from buildstream.plugin import CoreWarnings
    
    41
    +from buildstream import _yaml
    
    41 42
     import os
    
    42 43
     from collections import OrderedDict
    
    43 44
     import pytest
    
    ... ... @@ -128,7 +129,6 @@ def assert_cache_keys(project_dir, output):
    128 129
                                  "Use tests/cachekey/update.py to automatically " +
    
    129 130
                                  "update this test case")
    
    130 131
     
    
    131
    -
    
    132 132
     ##############################################
    
    133 133
     #             Test Entry Point               #
    
    134 134
     ##############################################
    
    ... ... @@ -167,3 +167,46 @@ def test_cache_key(datafiles, cli):
    167 167
         ])
    
    168 168
         result.assert_success()
    
    169 169
         assert_cache_keys(project, result.output)
    
    170
    +
    
    171
    +
    
    172
    +@pytest.mark.datafiles(DATA_DIR)
    
    173
    +@pytest.mark.parametrize("first_warnings, second_warnings, identical_keys", [
    
    174
    +    [[], [], True],
    
    175
    +    [[], [CoreWarnings.REF_NOT_IN_TRACK], False],
    
    176
    +    [[CoreWarnings.REF_NOT_IN_TRACK], [], False],
    
    177
    +    [[CoreWarnings.REF_NOT_IN_TRACK], [CoreWarnings.REF_NOT_IN_TRACK], True],
    
    178
    +    [[CoreWarnings.REF_NOT_IN_TRACK, CoreWarnings.OVERLAPS], [CoreWarnings.OVERLAPS, CoreWarnings.REF_NOT_IN_TRACK], True],
    
    179
    +])
    
    180
    +def test_cache_key_fatal_warnings(cli, tmpdir, first_warnings, second_warnings, identical_keys):
    
    181
    +
    
    182
    +    # Builds project, Runs bst show, gathers cache keys
    
    183
    +    def run_get_cache_key(project_name, warnings):
    
    184
    +        config = {
    
    185
    +            'name': project_name,
    
    186
    +            'element-path': 'elements',
    
    187
    +            'fatal-warnings': warnings
    
    188
    +        }
    
    189
    +
    
    190
    +        project_dir = tmpdir.mkdir(project_name)
    
    191
    +        project_config_file = str(project_dir.join('project.conf'))
    
    192
    +        _yaml.dump(_yaml.node_sanitize(config), filename=project_config_file)
    
    193
    +
    
    194
    +        elem_dir = project_dir.mkdir('elements')
    
    195
    +        element_file = str(elem_dir.join('stack.bst'))
    
    196
    +        _yaml.dump({'kind': 'stack'}, filename=element_file)
    
    197
    +
    
    198
    +        result = cli.run(project=str(project_dir), args=[
    
    199
    +            'show',
    
    200
    +            '--format', '%{name}::%{full-key}',
    
    201
    +            'stack.bst'
    
    202
    +        ])
    
    203
    +        return result.output
    
    204
    +
    
    205
    +    # Returns true if all keys are identical
    
    206
    +    def compare_cache_keys(first_keys, second_keys):
    
    207
    +        return not any((x != y for x,y in zip(first_keys, second_keys)))
    
    208
    +
    
    209
    +    first_keys = run_get_cache_key("first", first_warnings)
    
    210
    +    second_keys = run_get_cache_key("second", second_warnings)
    
    211
    +
    
    212
    +    assert compare_cache_keys(first_keys, second_keys) == identical_keys
    \ No newline at end of file

  • tests/frontend/configurable_warnings.py
    1
    +import pytest
    
    2
    +import os
    
    3
    +
    
    4
    +from buildstream.plugin import CoreWarnings
    
    5
    +from buildstream._exceptions import ErrorDomain, LoadErrorReason
    
    6
    +from buildstream import _yaml
    
    7
    +from tests.testutils.runcli import cli
    
    8
    +
    
    9
    +TOP_DIR = os.path.join(
    
    10
    +    os.path.dirname(os.path.realpath(__file__)),
    
    11
    +    "configuredwarning"
    
    12
    +)
    
    13
    +
    
    14
    +
    
    15
    +def get_project(fatal_warnings):
    
    16
    +    return {
    
    17
    +        "name": "test",
    
    18
    +        "element-path": "elements",
    
    19
    +        "plugins": [
    
    20
    +            {
    
    21
    +                "origin": "local",
    
    22
    +                "path": "plugins",
    
    23
    +                "elements": {
    
    24
    +                    "warninga": 0,
    
    25
    +                    "warningb": 0,
    
    26
    +                    "corewarn": 0,
    
    27
    +                }
    
    28
    +            }
    
    29
    +        ],
    
    30
    +        "fatal-warnings": fatal_warnings
    
    31
    +    }
    
    32
    +
    
    33
    +
    
    34
    +def build_project(datafiles, fatal_warnings):
    
    35
    +    project_path = os.path.join(datafiles.dirname, datafiles.basename)
    
    36
    +
    
    37
    +    project = get_project(fatal_warnings)
    
    38
    +
    
    39
    +    _yaml.dump(project, os.path.join(project_path, "project.conf"))
    
    40
    +
    
    41
    +    return project_path
    
    42
    +
    
    43
    +
    
    44
    +@pytest.mark.datafiles(TOP_DIR)
    
    45
    +@pytest.mark.parametrize("element_name, fatal_warnings, expect_fatal, error_domain", [
    
    46
    +    ("corewarn.bst", [CoreWarnings.OVERLAPS], True, ErrorDomain.STREAM),
    
    47
    +    ("warninga.bst", ["warninga:warning-a"], True, ErrorDomain.STREAM),
    
    48
    +    ("warningb.bst", ["warningb:warning-b"], True, ErrorDomain.STREAM),
    
    49
    +    ("corewarn.bst", [], False, None),
    
    50
    +    ("warninga.bst", [], False, None),
    
    51
    +    ("warningb.bst", [], False, None),
    
    52
    +    ("warninga.bst", [CoreWarnings.OVERLAPS], False, None),
    
    53
    +    ("warningb.bst", [CoreWarnings.OVERLAPS], False, None),
    
    54
    +])
    
    55
    +def test_fatal_warnings(cli, datafiles, element_name,
    
    56
    +                        fatal_warnings, expect_fatal, error_domain):
    
    57
    +    project_path = build_project(datafiles, fatal_warnings)
    
    58
    +
    
    59
    +    result = cli.run(project=project_path, args=["build", element_name])
    
    60
    +    if expect_fatal:
    
    61
    +        result.assert_main_error(error_domain, None, "Expected fatal execution")
    
    62
    +    else:
    
    63
    +        result.assert_success("Unexpected fatal execution")

  • tests/frontend/configuredwarning/elements/corewarn.bst
    1
    +kind: corewarn
    \ No newline at end of file

  • tests/frontend/configuredwarning/elements/warninga.bst
    1
    +kind: warninga

  • tests/frontend/configuredwarning/elements/warningb.bst
    1
    +kind: warningb

  • tests/frontend/configuredwarning/plugins/corewarn.py
    1
    +from buildstream import Element
    
    2
    +from buildstream.plugin import CoreWarnings
    
    3
    +
    
    4
    +
    
    5
    +class CoreWarn(Element):
    
    6
    +    def configure(self, node):
    
    7
    +        pass
    
    8
    +
    
    9
    +    def preflight(self):
    
    10
    +        pass
    
    11
    +
    
    12
    +    def get_unique_key(self):
    
    13
    +        pass
    
    14
    +
    
    15
    +    def configure_sandbox(self, sandbox):
    
    16
    +        pass
    
    17
    +
    
    18
    +    def stage(self, sandbox):
    
    19
    +        pass
    
    20
    +
    
    21
    +    def assemble(self, sandbox):
    
    22
    +        self.warn("Testing: CoreWarning produced during assemble",
    
    23
    +                  warning_token=CoreWarnings.OVERLAPS)
    
    24
    +
    
    25
    +
    
    26
    +def setup():
    
    27
    +    return CoreWarn

  • tests/frontend/configuredwarning/plugins/warninga.py
    1
    +from buildstream import Element
    
    2
    +
    
    3
    +WARNING_A = "warning-a"
    
    4
    +
    
    5
    +
    
    6
    +class WarningA(Element):
    
    7
    +    def configure(self, node):
    
    8
    +        pass
    
    9
    +
    
    10
    +    def preflight(self):
    
    11
    +        pass
    
    12
    +
    
    13
    +    def get_unique_key(self):
    
    14
    +        pass
    
    15
    +
    
    16
    +    def configure_sandbox(self, sandbox):
    
    17
    +        pass
    
    18
    +
    
    19
    +    def stage(self, sandbox):
    
    20
    +        pass
    
    21
    +
    
    22
    +    def assemble(self, sandbox):
    
    23
    +        self.warn("Testing: warning-a produced during assemble", warning_token=WARNING_A)
    
    24
    +
    
    25
    +
    
    26
    +def setup():
    
    27
    +    return WarningA

  • tests/frontend/configuredwarning/plugins/warningb.py
    1
    +from buildstream import Element
    
    2
    +
    
    3
    +WARNING_B = "warning-b"
    
    4
    +
    
    5
    +
    
    6
    +class WarningB(Element):
    
    7
    +    def configure(self, node):
    
    8
    +        pass
    
    9
    +
    
    10
    +    def preflight(self):
    
    11
    +        pass
    
    12
    +
    
    13
    +    def get_unique_key(self):
    
    14
    +        pass
    
    15
    +
    
    16
    +    def configure_sandbox(self, sandbox):
    
    17
    +        pass
    
    18
    +
    
    19
    +    def stage(self, sandbox):
    
    20
    +        pass
    
    21
    +
    
    22
    +    def assemble(self, sandbox):
    
    23
    +        self.warn("Testing: warning-b produced during assemble", warning_token=WARNING_B)
    
    24
    +
    
    25
    +
    
    26
    +def setup():
    
    27
    +    return WarningB

  • tests/frontend/configuredwarning/project.conf
    1
    +name: test
    
    2
    +element-path: elements
    
    3
    +plugins:
    
    4
    +- origin: local
    
    5
    +  path: element_plugins
    
    6
    +  elements:
    
    7
    +    warninga: 0
    
    8
    +    warningb: 0

  • tests/frontend/overlaps.py
    ... ... @@ -3,6 +3,7 @@ import pytest
    3 3
     from tests.testutils.runcli import cli
    
    4 4
     from buildstream._exceptions import ErrorDomain
    
    5 5
     from buildstream import _yaml
    
    6
    +from buildstream.plugin import CoreWarnings
    
    6 7
     
    
    7 8
     # Project directory
    
    8 9
     DATA_DIR = os.path.join(
    
    ... ... @@ -16,30 +17,35 @@ project_template = {
    16 17
     }
    
    17 18
     
    
    18 19
     
    
    19
    -def gen_project(project_dir, fail_on_overlap):
    
    20
    +def gen_project(project_dir, fail_on_overlap, use_fatal_warnings=True):
    
    20 21
         template = dict(project_template)
    
    21
    -    template["fail-on-overlap"] = fail_on_overlap
    
    22
    +    if use_fatal_warnings:
    
    23
    +        template["fatal-warnings"] = [CoreWarnings.OVERLAPS] if fail_on_overlap else []
    
    24
    +    else:
    
    25
    +        template["fail-on-overlap"] = fail_on_overlap
    
    22 26
         projectfile = os.path.join(project_dir, "project.conf")
    
    23 27
         _yaml.dump(template, projectfile)
    
    24 28
     
    
    25 29
     
    
    26 30
     @pytest.mark.datafiles(DATA_DIR)
    
    27
    -def test_overlaps(cli, datafiles):
    
    31
    +@pytest.mark.parametrize("use_fatal_warnings", [True, False])
    
    32
    +def test_overlaps(cli, datafiles, use_fatal_warnings):
    
    28 33
         project_dir = str(datafiles)
    
    29
    -    gen_project(project_dir, False)
    
    34
    +    gen_project(project_dir, False, use_fatal_warnings)
    
    30 35
         result = cli.run(project=project_dir, silent=True, args=[
    
    31 36
             'build', 'collect.bst'])
    
    32 37
         result.assert_success()
    
    33 38
     
    
    34 39
     
    
    35 40
     @pytest.mark.datafiles(DATA_DIR)
    
    36
    -def test_overlaps_error(cli, datafiles):
    
    41
    +@pytest.mark.parametrize("use_fatal_warnings", [True, False])
    
    42
    +def test_overlaps_error(cli, datafiles, use_fatal_warnings):
    
    37 43
         project_dir = str(datafiles)
    
    38
    -    gen_project(project_dir, True)
    
    44
    +    gen_project(project_dir, True, use_fatal_warnings)
    
    39 45
         result = cli.run(project=project_dir, silent=True, args=[
    
    40 46
             'build', 'collect.bst'])
    
    41 47
         result.assert_main_error(ErrorDomain.STREAM, None)
    
    42
    -    result.assert_task_error(ErrorDomain.ELEMENT, "overlap-error")
    
    48
    +    result.assert_task_error(ErrorDomain.PLUGIN, CoreWarnings.OVERLAPS)
    
    43 49
     
    
    44 50
     
    
    45 51
     @pytest.mark.datafiles(DATA_DIR)
    
    ... ... @@ -70,15 +76,16 @@ def test_overlaps_whitelist_on_overlapper(cli, datafiles):
    70 76
         result = cli.run(project=project_dir, silent=True, args=[
    
    71 77
             'build', 'collect-partially-whitelisted.bst'])
    
    72 78
         result.assert_main_error(ErrorDomain.STREAM, None)
    
    73
    -    result.assert_task_error(ErrorDomain.ELEMENT, "overlap-error")
    
    79
    +    result.assert_task_error(ErrorDomain.PLUGIN, CoreWarnings.OVERLAPS)
    
    74 80
     
    
    75 81
     
    
    76 82
     @pytest.mark.datafiles(DATA_DIR)
    
    77
    -def test_overlaps_script(cli, datafiles):
    
    83
    +@pytest.mark.parametrize("use_fatal_warnings", [True, False])
    
    84
    +def test_overlaps_script(cli, datafiles, use_fatal_warnings):
    
    78 85
         # Test overlaps with script element to test
    
    79 86
         # Element.stage_dependency_artifacts() with Scope.RUN
    
    80 87
         project_dir = str(datafiles)
    
    81
    -    gen_project(project_dir, False)
    
    88
    +    gen_project(project_dir, False, use_fatal_warnings)
    
    82 89
         result = cli.run(project=project_dir, silent=True, args=[
    
    83 90
             'build', 'script.bst'])
    
    84 91
         result.assert_success()

  • tests/frontend/track.py
    ... ... @@ -612,3 +612,25 @@ def test_track_include_junction(cli, tmpdir, datafiles, ref_storage, kind):
    612 612
             # Assert that we are now buildable because the source is
    
    613 613
             # now cached.
    
    614 614
             assert cli.get_element_state(project, element_name) == 'buildable'
    
    615
    +
    
    616
    +
    
    617
    +@pytest.mark.datafiles(DATA_DIR)
    
    618
    +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')])
    
    619
    +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS])
    
    620
    +def test_track_junction_included(cli, tmpdir, datafiles, ref_storage, kind):
    
    621
    +    project = os.path.join(datafiles.dirname, datafiles.basename)
    
    622
    +    element_path = os.path.join(project, 'elements')
    
    623
    +    subproject_path = os.path.join(project, 'files', 'sub-project')
    
    624
    +    sub_element_path = os.path.join(subproject_path, 'elements')
    
    625
    +    junction_path = os.path.join(element_path, 'junction.bst')
    
    626
    +
    
    627
    +    configure_project(project, {
    
    628
    +        'ref-storage': ref_storage,
    
    629
    +        '(@)': ['junction.bst:test.yml']
    
    630
    +    })
    
    631
    +
    
    632
    +    generate_junction(str(tmpdir.join('junction_repo')),
    
    633
    +                      subproject_path, junction_path, store_ref=False)
    
    634
    +
    
    635
    +    result = cli.run(project=project, args=['track', 'junction.bst'])
    
    636
    +    result.assert_success()



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]