[Notes] [Git][BuildStream/buildstream][Qinusty/526-fail-on-warnings] 4 commits: _project.py: Add fatal-warnings configuration item



Title: GitLab

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

Commits:

17 changed files:

Changes:

  • buildstream/_frontend/app.py
    ... ... @@ -230,6 +230,12 @@ class App():
    230 230
             # Propagate pipeline feedback to the user
    
    231 231
             self.context.set_message_handler(self._message_handler)
    
    232 232
     
    
    233
    +        # Deprecation check now that message handler is initialized
    
    234
    +        if self.project._fail_on_overlap is not None:
    
    235
    +            self._message(MessageType.WARN,
    
    236
    +                          "Use of fail-on-overlap within project.conf " +
    
    237
    +                          "is deprecated. Please use fatal-warnings instead.")
    
    238
    +
    
    233 239
             # Now that we have a logger and message handler,
    
    234 240
             # we can override the global exception hook.
    
    235 241
             sys.excepthook = self._global_exception_handler
    

  • buildstream/_project.py
    ... ... @@ -32,6 +32,7 @@ from ._options import OptionPool
    32 32
     from ._artifactcache import ArtifactCache
    
    33 33
     from ._elementfactory import ElementFactory
    
    34 34
     from ._sourcefactory import SourceFactory
    
    35
    +from .plugin import all_warnings, CoreWarnings
    
    35 36
     from ._projectrefs import ProjectRefs, ProjectRefStorage
    
    36 37
     from ._versions import BST_FORMAT_VERSION
    
    37 38
     
    
    ... ... @@ -39,7 +40,6 @@ from ._versions import BST_FORMAT_VERSION
    39 40
     # Project Configuration file
    
    40 41
     _PROJECT_CONF_FILE = 'project.conf'
    
    41 42
     
    
    42
    -
    
    43 43
     # HostMount()
    
    44 44
     #
    
    45 45
     # A simple object describing the behavior of
    
    ... ... @@ -85,7 +85,7 @@ class Project():
    85 85
     
    
    86 86
             self.options = None                      # OptionPool
    
    87 87
             self.junction = junction                 # The junction Element object, if this is a subproject
    
    88
    -        self.fail_on_overlap = False             # Whether overlaps are treated as errors
    
    88
    +
    
    89 89
             self.ref_storage = None                  # ProjectRefStorage setting
    
    90 90
             self.base_variables = {}                 # The base set of variables
    
    91 91
             self.base_environment = {}               # The base set of environment variables
    
    ... ... @@ -108,6 +108,10 @@ class Project():
    108 108
             self._source_format_versions = {}
    
    109 109
             self._element_format_versions = {}
    
    110 110
     
    
    111
    +        self._fail_on_overlap = False         # Whether to fail on overlaps or not # Deprecated use _warning_is_fatal()
    
    112
    +        self._fatal_warnings = []             # A list of warnings which should trigger an error
    
    113
    +        self._fatal_warnings_provenance = {}  # A lookup table for where fatal warnings came from.
    
    114
    +
    
    111 115
             self._shell_command = []      # The default interactive shell command
    
    112 116
             self._shell_environment = {}  # Statically set environment vars
    
    113 117
             self._shell_host_files = []   # A list of HostMount objects
    
    ... ... @@ -232,6 +236,18 @@ class Project():
    232 236
             mirror_list.append(self._aliases[alias])
    
    233 237
             return mirror_list
    
    234 238
     
    
    239
    +    # fail_on_overlap
    
    240
    +    #
    
    241
    +    # Property added to continue support of Project.fail_on_overlap after
    
    242
    +    # introduction of fatal-warnings configuration item.
    
    243
    +    #
    
    244
    +    # Returns:
    
    245
    +    #    (bool): True if the configuration specifies that overlaps should
    
    246
    +    #            cause errors instead of warnings.
    
    247
    +    @property
    
    248
    +    def fail_on_overlap(self):
    
    249
    +        return self._fail_on_overlap or self._warning_is_fatal(CoreWarnings.OVERLAPS)
    
    250
    +
    
    235 251
         # _load():
    
    236 252
         #
    
    237 253
         # Loads the project configuration file in the project directory.
    
    ... ... @@ -278,7 +294,7 @@ class Project():
    278 294
                 'split-rules', 'elements', 'plugins',
    
    279 295
                 'aliases', 'name',
    
    280 296
                 'artifacts', 'options',
    
    281
    -            'fail-on-overlap', 'shell',
    
    297
    +            'fail-on-overlap', 'shell', 'fatal-warnings',
    
    282 298
                 'ref-storage', 'sandbox', 'mirrors',
    
    283 299
             ])
    
    284 300
     
    
    ... ... @@ -404,7 +420,22 @@ class Project():
    404 420
             self._splits = _yaml.node_get(config, Mapping, 'split-rules')
    
    405 421
     
    
    406 422
             # Fail on overlap
    
    407
    -        self.fail_on_overlap = _yaml.node_get(config, bool, 'fail-on-overlap')
    
    423
    +        self._fail_on_overlap = _yaml.node_get(config, bool, 'fail-on-overlap', default_value=None)
    
    424
    +
    
    425
    +        # Fatal warnings
    
    426
    +        p = _yaml.node_get_provenance(config, 'fatal-warnings')
    
    427
    +        try:  # Check for bool type
    
    428
    +            fatal_warnings = _yaml.node_get(config, bool, 'fatal-warnings', default_value=False)
    
    429
    +        except (ValueError, LoadError) as e:
    
    430
    +            try:  # Check for list type
    
    431
    +                fatal_warnings = _yaml.node_get(config, list, 'fatal-warnings', default_value=[])
    
    432
    +            except (ValueError, LoadError):
    
    433
    +                raise LoadError(LoadErrorReason.INVALID_DATA,
    
    434
    +                                "{}: Invalid value specified for 'fatal-warnings', ".format(p) +
    
    435
    +                                "must be list or bool.")
    
    436
    +
    
    437
    +        # Validate and set fatal warnings
    
    438
    +        self._set_fatal_warnings(fatal_warnings, p)
    
    408 439
     
    
    409 440
             # Use separate file for storing source references
    
    410 441
             self.ref_storage = _yaml.node_get(config, str, 'ref-storage')
    
    ... ... @@ -532,3 +563,60 @@ class Project():
    532 563
                 directory = parent_dir
    
    533 564
     
    
    534 565
             return directory
    
    566
    +
    
    567
    +    # _warning_is_fatal():
    
    568
    +    #
    
    569
    +    # Returns true if the warning in question should be considered fatal based on
    
    570
    +    # the project configuration.
    
    571
    +    #
    
    572
    +    # Args:
    
    573
    +    #   warning_str (str): The warning configuration string to check against
    
    574
    +    #
    
    575
    +    # Returns:
    
    576
    +    #    (bool): True if the warning should be considered fatal and cause an error.
    
    577
    +    #
    
    578
    +    def _warning_is_fatal(self, warning_str):
    
    579
    +        return warning_str in self._fatal_warnings
    
    580
    +
    
    581
    +    # _set_fatal_warnings():
    
    582
    +    #
    
    583
    +    # Sets self._fatal_warnings appropriately
    
    584
    +    #
    
    585
    +    # Args:
    
    586
    +    #   warnings (list|bool): The warnings to set self._fatal_warnings to.
    
    587
    +    #                         If True, plugin.ALL_WARNINGS is used. If False, [] is used.
    
    588
    +    #   provenance (str): The provenance assosciated with the warnings parameter.
    
    589
    +    #
    
    590
    +    def _set_fatal_warnings(self, warnings, provenance):
    
    591
    +        if warnings is None:
    
    592
    +            return
    
    593
    +        elif isinstance(warnings, bool):
    
    594
    +            # This can make self._fatal_warnings a reference to all_warnings if warnings is true.
    
    595
    +            # We can't copy because plugins have not been loaded and their warnings aren't in all_warnings.
    
    596
    +            self._fatal_warnings = all_warnings if warnings else []
    
    597
    +        else:
    
    598
    +            self._fatal_warnings = warnings
    
    599
    +
    
    600
    +        # Record the provenance for this configuration for use within _assert_fatal_warnings
    
    601
    +        self._fatal_warnings_provenance = {warning: provenance for warning in self._fatal_warnings}
    
    602
    +
    
    603
    +    # _assert_fatal_warnings():
    
    604
    +    #
    
    605
    +    # Checks that the configured fatal-warnings are valid with the currently loaded plugins.
    
    606
    +    #
    
    607
    +    # Raises:
    
    608
    +    #   LoadError: When self._fatal_warnings contains warnings not within plugin.all_warnings
    
    609
    +    def _assert_fatal_warnings(self):
    
    610
    +        # Check for unknown warnings.
    
    611
    +        unknown_warnings = list(filter(lambda x: x not in all_warnings, self._fatal_warnings))
    
    612
    +        if unknown_warnings:  # Restrict 'fatal-warnings' to known warnings
    
    613
    +            provenance = self._fatal_warnings_provenance.get(unknown_warnings[0])
    
    614
    +            quoted_unknown_warnings = ["'{}'".format(warning) for warning in unknown_warnings]
    
    615
    +            quoted_all_warnings = ["'{}'".format(warning) for warning in all_warnings]
    
    616
    +            raise LoadError(LoadErrorReason.INVALID_DATA,
    
    617
    +                            ("{}: Invalid {} provided to fatal-warnings ({})\n" +
    
    618
    +                             "Valid options are: ({})")
    
    619
    +                            .format(provenance,
    
    620
    +                                    "warning" if len(unknown_warnings) == 1 else "warnings",
    
    621
    +                                    ", ".join(quoted_unknown_warnings),
    
    622
    +                                    ", ".join(quoted_all_warnings)))

  • buildstream/_stream.py
    ... ... @@ -861,6 +861,9 @@ class Stream():
    861 861
                                     rewritable=rewritable,
    
    862 862
                                     fetch_subprojects=fetch_subprojects)
    
    863 863
     
    
    864
    +        # Ensure configured warnings are available within loaded plugins
    
    865
    +        self._project._assert_fatal_warnings()
    
    866
    +
    
    864 867
             # Hold on to the targets
    
    865 868
             self.targets = elements
    
    866 869
     
    

  • 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 = 11
    
    26
    +BST_FORMAT_VERSION = 12
    
    27 27
     
    
    28 28
     
    
    29 29
     # The base BuildStream artifact version
    

  • buildstream/data/projectconfig.yaml
    ... ... @@ -14,7 +14,15 @@ element-path: .
    14 14
     ref-storage: inline
    
    15 15
     
    
    16 16
     # Overlaps are just warnings
    
    17
    -fail-on-overlap: False
    
    17
    +# This has been DEPRECATED in favour of fatal-warnings
    
    18
    +#fail-on-overlap: False
    
    19
    +
    
    20
    +# Allows a collection of warnings to be configured to be raised as errors.
    
    21
    +# Setting this value to true will enable all possible fatal-warnings
    
    22
    +# fatal-warnings: True
    
    23
    +
    
    24
    +# fatal-warnings:
    
    25
    +# - overlaps
    
    18 26
     
    
    19 27
     
    
    20 28
     # Variable Configuration
    

  • buildstream/plugin.py
    ... ... @@ -47,6 +47,24 @@ 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
    +For plugins which wish to provide configurable warnings, it is necessary to override
    
    53
    +:func:`Plugin.get_warnings() <buildstream.plugin.Plugin.get_warnings>` to get them registered
    
    54
    +with buildstream.
    
    55
    +
    
    56
    +These warnings are used when calling :func:`Plugin.warn() <buildstream.plugin.Plugin.warn>` as an optional
    
    57
    +parameter ``warning_token``, this will raise a :class:`PluginError` if the warning is configured as fatal.
    
    58
    +
    
    59
    +Configurable warnings will be prefixed with :func:`Plugin.get_kind() <buildstream.plugin.Plugin.get_kind>`
    
    60
    +within buildstream and must be prefixed as such in project configurations. For more detail on project configuration
    
    61
    +see :ref:`Configurable Warnings <configurable_warnings>`.
    
    62
    +
    
    63
    +Example
    
    64
    +~~~~~~~
    
    65
    +If the ``git.py`` plugin made the warning ``"inconsistent-submodule"`` available through
    
    66
    +:func:`Plugin.get_warnings() <buildstream.plugin.Plugin.get_warnings>` then it could be referenced in project
    
    67
    +configuration as ``"git:inconsistent-submodule"``
    
    50 68
     
    
    51 69
     Plugin Structure
    
    52 70
     ----------------
    
    ... ... @@ -102,6 +120,20 @@ from . import utils
    102 120
     from ._exceptions import PluginError, ImplError
    
    103 121
     from ._message import Message, MessageType
    
    104 122
     
    
    123
    +# Core Warnings must be prefixed with core:
    
    124
    +CORE_WARNING_PREFIX = "core:"
    
    125
    +class CoreWarnings():
    
    126
    +    OVERLAPS = CORE_WARNING_PREFIX + "overlaps"
    
    127
    +    REF_NOT_IN_TRACK = CORE_WARNING_PREFIX + "ref-not-in-track"
    
    128
    +
    
    129
    +CORE_WARNINGS = [
    
    130
    +    warning_label
    
    131
    +    for var, warning_label in CoreWarnings.__dict__.items()
    
    132
    +    if not var.startswith("__")
    
    133
    +]
    
    134
    +
    
    135
    +all_warnings = CORE_WARNINGS + []
    
    136
    +
    
    105 137
     
    
    106 138
     class Plugin():
    
    107 139
         """Plugin()
    
    ... ... @@ -166,7 +198,7 @@ class Plugin():
    166 198
             # Infer the kind identifier
    
    167 199
             modulename = type(self).__module__
    
    168 200
             self.__kind = modulename.split('.')[-1]
    
    169
    -
    
    201
    +        _register_warnings(self, self.get_warnings())
    
    170 202
             self.debug("Created: {}".format(self))
    
    171 203
     
    
    172 204
         def __del__(self):
    
    ... ... @@ -473,13 +505,21 @@ class Plugin():
    473 505
             """
    
    474 506
             self.__message(MessageType.INFO, brief, detail=detail)
    
    475 507
     
    
    476
    -    def warn(self, brief, *, detail=None):
    
    477
    -        """Print a warning message
    
    508
    +    def warn(self, brief, *, detail=None, warning_token=None):
    
    509
    +        """Print a warning message, checks warning_token against project configuration
    
    478 510
     
    
    479 511
             Args:
    
    480 512
                brief (str): The brief message
    
    481 513
                detail (str): An optional detailed message, can be multiline output
    
    514
    +           warning_token (str): An optional configurable warning assosciated with this warning,
    
    515
    +                                this will cause PluginError to be raised if this warning is configured as fatal.
    
    516
    +                                (*Since 1.4*)
    
    517
    +
    
    518
    +        Raises:
    
    519
    +           (:class:`.PluginError`): When warning_token is considered fatal by the project configuration
    
    482 520
             """
    
    521
    +        if warning_token and self.__warning_is_fatal(_prefix_warning(self, warning_token)):
    
    522
    +            raise PluginError(message="{}\n{}".format(brief, detail))
    
    483 523
             self.__message(MessageType.WARN, brief, detail=detail)
    
    484 524
     
    
    485 525
         def log(self, brief, *, detail=None):
    
    ... ... @@ -606,6 +646,21 @@ class Plugin():
    606 646
             """
    
    607 647
             return self.__call(*popenargs, collect_stdout=True, fail=fail, fail_temporarily=fail_temporarily, **kwargs)
    
    608 648
     
    
    649
    +    def get_warnings(self):
    
    650
    +        """Return a collection of configurable warnings
    
    651
    +
    
    652
    +        Returns:
    
    653
    +        (list of str): A list of strings which will extend the configurable warnings collection.
    
    654
    +
    
    655
    +        Warnings can be configured as fatal in a project configuration file, any warnings provided here
    
    656
    +        will be added to the collection used by buildstream.
    
    657
    +
    
    658
    +        **Since 1.4**
    
    659
    +
    
    660
    +        Plugin implementors should override this method to add their own configurable warnings.
    
    661
    +        """
    
    662
    +        return []
    
    663
    +
    
    609 664
         #############################################################
    
    610 665
         #            Private Methods used in BuildStream            #
    
    611 666
         #############################################################
    
    ... ... @@ -708,6 +763,10 @@ class Plugin():
    708 763
             else:
    
    709 764
                 return self.name
    
    710 765
     
    
    766
    +    def __warning_is_fatal(self, warning):
    
    767
    +        return self._get_project()._warning_is_fatal(warning)
    
    768
    +
    
    769
    +
    
    711 770
     
    
    712 771
     # Hold on to a lookup table by counter of all instantiated plugins.
    
    713 772
     # We use this to send the id back from child processes so we can lookup
    
    ... ... @@ -739,6 +798,26 @@ def _plugin_lookup(unique_id):
    739 798
         return __PLUGINS_TABLE[unique_id]
    
    740 799
     
    
    741 800
     
    
    801
    +def _prefix_warning(plugin, warning):
    
    802
    +    if warning.startswith(CORE_WARNING_PREFIX):
    
    803
    +        return warning
    
    804
    +    return "{}:{}".format(plugin.get_kind(), warning)
    
    805
    +
    
    806
    +# _register_warnings():
    
    807
    +#
    
    808
    +# Registers a collection of warnings from a plugin into all_warnings
    
    809
    +#
    
    810
    +# Args:
    
    811
    +#   plugin (Plugin): The plugin assosciated with the warnings.
    
    812
    +#   warnings (list of str): The collection of warnings to prefix and register.
    
    813
    +#
    
    814
    +def _register_warnings(plugin, warnings):
    
    815
    +    prefixed_warnings = [
    
    816
    +        _prefix_warning(plugin, warning)
    
    817
    +        for warning in warnings
    
    818
    +    ]
    
    819
    +    all_warnings.extend(prefixed_warnings)
    
    820
    +
    
    742 821
     # No need for unregister, WeakValueDictionary() will remove entries
    
    743 822
     # in itself when the referenced plugins are garbage collected.
    
    744 823
     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,8 @@ from buildstream import utils
    84 90
     
    
    85 91
     GIT_MODULES = '.gitmodules'
    
    86 92
     
    
    93
    +# Warnings
    
    94
    +INCONSISTENT_SUBMODULE = "inconsistent-submodules"
    
    87 95
     
    
    88 96
     # Because of handling of submodules, we maintain a GitMirror
    
    89 97
     # for the primary git source and also for each submodule it
    
    ... ... @@ -283,7 +291,7 @@ class GitMirror(SourceFetcher):
    283 291
                          "underlying git repository with `git submodule add`."
    
    284 292
     
    
    285 293
                 self.source.warn("{}: Ignoring inconsistent submodule '{}'"
    
    286
    -                             .format(self.source, submodule), detail=detail)
    
    294
    +                             .format(self.source, submodule), detail=detail, warning_token=INCONSISTENT_SUBMODULE)
    
    287 295
     
    
    288 296
                 return None
    
    289 297
     
    
    ... ... @@ -350,6 +358,9 @@ class GitSource(Source):
    350 358
                 return Consistency.RESOLVED
    
    351 359
             return Consistency.INCONSISTENT
    
    352 360
     
    
    361
    +    def get_warnings(self):
    
    362
    +        return [INCONSISTENT_SUBMODULE]
    
    363
    +
    
    353 364
         def load_ref(self, node):
    
    354 365
             self.mirror.ref = self.node_get_member(node, str, 'ref', None)
    
    355 366
     
    

  • doc/source/format_project.rst
    ... ... @@ -126,23 +126,72 @@ 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
    +.. _configurable_warnings:
    
    130
    +
    
    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.
    
    136
    +
    
    137
    +When ``fatal-warnings`` is True, all configurable fatal warnings will be set as fatal. Individual warnings
    
    138
    +can also be set by setting ``fatal-warnings`` to a list of warnings.
    
    139
    +
    
    140
    +.. code::
    
    141
    +
    
    142
    +  fatal-warnings:
    
    143
    +  - core:overlaps
    
    144
    +  - core:ref-not-in-track
    
    145
    +  - <plugin>:<warning>
    
    146
    +
    
    147
    +Core Configurable warnings include:
    
    148
    +
    
    149
    +- :ref:`core:overlaps <fail_on_overlaps>`
    
    150
    +- :ref:`core:ref-not-in-track <ref_not_in_track>`
    
    151
    +
    
    152
    +.. note::
    
    153
    +
    
    154
    +  The ``ref-storage`` configuration is available since :ref:`format version 12 <project_format_version>`
    
    155
    +
    
    156
    +.. note::
    
    157
    +
    
    158
    +  Other configurable warnings are plugin specific and should be noted within their individual documentation.
    
    159
    +
    
    160
    +.. _fail_on_overlaps:
    
    161
    +
    
    129 162
     Fail on overlaps
    
    130 163
     ~~~~~~~~~~~~~~~~
    
    131 164
     When multiple elements are staged, there's a possibility that different
    
    132 165
     elements will try and stage different versions of the same file.
    
    133 166
     
    
    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.
    
    167
    +.. deprecated:: 1.4
    
    168
    +
    
    138 169
     
    
    139
    -otherwise, a warning will be raised indicating which files had overlaps,
    
    140
    -and the order that the elements were overlapped.
    
    170
    +  When ``fail-on-overlap`` is true, if an overlap is detected
    
    171
    +  that hasn't been allowed by the element's
    
    172
    +  :ref:`overlap whitelist<public_overlap_whitelist>`,
    
    173
    +  then an error will be raised and the build will fail.
    
    174
    +
    
    175
    +  Otherwise, a warning will be raised indicating which files had overlaps,
    
    176
    +  and the order that the elements were overlapped.
    
    141 177
     
    
    142 178
     .. code:: yaml
    
    143 179
     
    
    180
    +  # Deprecated
    
    144 181
       fail-on-overlap: true
    
    145 182
     
    
    183
    +.. note::
    
    184
    +
    
    185
    +  Since deprecation in :ref:`format version 12 <project_format_version>` the recommended
    
    186
    +  solution to this is :ref:`Configurable Warnings <configurable_warnings>`
    
    187
    +
    
    188
    +
    
    189
    +.. _ref_not_in_track:
    
    190
    +
    
    191
    +Ref not in track
    
    192
    +~~~~~~~~~~~~~~~~
    
    193
    +The configured ref is not valid for the configured track.
    
    194
    +
    
    146 195
     
    
    147 196
     .. _project_source_aliases:
    
    148 197
     
    

  • 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
    +    ("corewarn.bst", "true", True, ErrorDomain.STREAM),
    
    53
    +    ("warninga.bst", "true", True, ErrorDomain.STREAM),
    
    54
    +    ("warningb.bst", "true", True, ErrorDomain.STREAM),
    
    55
    +    ("warninga.bst", [CoreWarnings.OVERLAPS], False, None),
    
    56
    +    ("warningb.bst", [CoreWarnings.OVERLAPS], False, None),
    
    57
    +])
    
    58
    +def test_fatal_warnings(cli, datafiles, element_name,
    
    59
    +                        fatal_warnings, expect_fatal, error_domain):
    
    60
    +    project_path = build_project(datafiles, fatal_warnings)
    
    61
    +
    
    62
    +    result = cli.run(project=project_path, args=["build", element_name])
    
    63
    +    if expect_fatal:
    
    64
    +        result.assert_main_error(error_domain, None, "Expected fatal execution")
    
    65
    +    else:
    
    66
    +        result.assert_success("Unexpected fatal execution")
    
    67
    +
    
    68
    +
    
    69
    +@pytest.mark.datafiles(TOP_DIR)
    
    70
    +def test_invalid_warning(cli, datafiles):
    
    71
    +    project_path = build_project(datafiles, ["non-existant-warning"])
    
    72
    +
    
    73
    +    result = cli.run(project=project_path, args=["build", "corewarn.bst"])
    
    74
    +    result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.INVALID_DATA)

  • 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
    +class CoreWarn(Element):
    
    5
    +    def configure(self, node):
    
    6
    +        pass
    
    7
    +
    
    8
    +    def preflight(self):
    
    9
    +        pass
    
    10
    +    def get_unique_key(self):
    
    11
    +        pass
    
    12
    +
    
    13
    +    def get_warnings(self):
    
    14
    +        return []  # CoreWarnings should be included regardless of plugins.
    
    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: CoreWarning produced during assemble",
    
    24
    +                  warning_token=CoreWarnings.OVERLAPS)
    
    25
    +
    
    26
    +
    
    27
    +def setup():
    
    28
    +    return CoreWarn
    \ No newline at end of file

  • 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 get_warnings(self):
    
    17
    +        return [WARNING_A]
    
    18
    +
    
    19
    +    def configure_sandbox(self, sandbox):
    
    20
    +        pass
    
    21
    +
    
    22
    +    def stage(self, sandbox):
    
    23
    +        pass
    
    24
    +
    
    25
    +    def assemble(self, sandbox):
    
    26
    +        self.warn("Testing: warning-a produced during assemble", warning_token=WARNING_A)
    
    27
    +
    
    28
    +def setup():
    
    29
    +    return WarningA
    \ No newline at end of file

  • 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 get_warnings(self):
    
    17
    +        return [WARNING_B]
    
    18
    +
    
    19
    +    def configure_sandbox(self, sandbox):
    
    20
    +        pass
    
    21
    +
    
    22
    +    def stage(self, sandbox):
    
    23
    +        pass
    
    24
    +
    
    25
    +    def assemble(self, sandbox):
    
    26
    +        self.warn("Testing: warning-b produced during assemble", warning_token=WARNING_B)
    
    27
    +
    
    28
    +def setup():
    
    29
    +    return WarningB
    \ No newline at end of file

  • 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,26 +17,31 @@ 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)
    
    ... ... @@ -74,11 +80,12 @@ def test_overlaps_whitelist_on_overlapper(cli, datafiles):
    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()



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