1# -*- coding: utf-8 -*-
2# Copyright (c) 2018 gevent community
3#
4# Permission is hereby granted, free of charge, to any person obtaining a copy
5# of this software and associated documentation files (the "Software"), to deal
6# in the Software without restriction, including without limitation the rights
7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8# copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be included in
12# all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20# THE SOFTWARE.
21
22"""
23Test environment setup.
24
25This establishes the resources that are available for use,
26which are tested with `support.is_resource_enabled`.
27
28"""
29from __future__ import absolute_import, division, print_function
30
31# This file may be imported early, so it should take care not to import
32# things it doesn't need, which means deferred imports.
33
34
35def get_ALL_RESOURCES():
36    "Return a fresh list of resource names."
37    # RESOURCE_NAMES is the list of all known resources, including those that
38    # shouldn't be enabled by default or when asking for "all" resources.
39    # ALL_RESOURCES is the list of resources enabled by default or with "all" resources.
40
41    try:
42        # 3.6 and 3.7
43        from test.libregrtest import ALL_RESOURCES
44    except ImportError:
45        # 2.7 through 3.5
46
47        # Don't do this:
48        ## from test.regrtest import ALL_RESOURCES
49
50        # On Python 2.7 to 3.5, importing regrtest iterates
51        # sys.modules and does modifications. That doesn't work well
52        # when it's imported from another module at module scope.
53        # Also, it makes some assumptions about module __file__ that
54        # may not hold true (at least on 2.7), especially when six or
55        # other module proxy objects are involved.
56        # So we hardcode the list. This is from 2.7, which is a superset
57        # of the defined resources through 3.5.
58
59        ALL_RESOURCES = (
60            'audio', 'curses', 'largefile', 'network', 'bsddb',
61            'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui',
62            'xpickle'
63        )
64
65    return list(ALL_RESOURCES) + [
66        # Do we test the stdlib monkey-patched?
67        'gevent_monkey',
68    ]
69
70
71def parse_resources(resource_str=None):
72    # str -> Sequence[str]
73
74    # Parse it like libregrtest.cmdline documents:
75
76    # -u is used to specify which special resource intensive tests to run,
77    # such as those requiring large file support or network connectivity.
78    # The argument is a comma-separated list of words indicating the
79    # resources to test.  Currently only the following are defined:
80
81    #     all -       Enable all special resources.
82    #
83    #     none -      Disable all special resources (this is the default).
84    # <snip>
85    #     network -   It is okay to run tests that use external network
86    #                 resource, e.g. testing SSL support for sockets.
87    # <snip>
88    #
89    #     subprocess  Run all tests for the subprocess module.
90    # <snip>
91    #
92    # To enable all resources except one, use '-uall,-<resource>'.  For
93    # example, to run all the tests except for the gui tests, give the
94    # option '-uall,-gui'.
95
96    # We make a change though: we default to 'all' resources, instead of
97    # 'none'. Encountering either of those later in the string resets
98    # it, for ease of working with appending to environment variables.
99
100    if resource_str is None:
101        import os
102        resource_str = os.environ.get('GEVENTTEST_USE_RESOURCES')
103
104    resources = get_ALL_RESOURCES()
105
106    if not resource_str:
107        return resources
108
109    requested_resources = resource_str.split(',')
110
111    for requested_resource in requested_resources:
112        # empty strings are ignored; this can happen when working with
113        # the environment variable if not already set:
114        # ENV=$ENV,-network
115        if not requested_resource:
116            continue
117        if requested_resource == 'all':
118            resources = get_ALL_RESOURCES()
119        elif requested_resource == 'none':
120            resources = []
121        elif requested_resource.startswith('-'):
122            if requested_resource[1:] in resources:
123                resources.remove(requested_resource[1:])
124        else:
125            # TODO: Produce a warning if it's an unknown resource?
126            resources.append(requested_resource)
127
128    return resources
129
130def unparse_resources(resources):
131    """
132    Given a list of enabled resources, produce the correct environment variable
133    setting to enable (only) that list.
134    """
135    # By default, we assume all resources are enabled, so explicitly
136    # listing them here doesn't actually disable anything. To do that, we want to
137    # list the ones that are disabled. This is usually shorter than starting with
138    # 'none', and manually adding them back in one by one.
139    #
140    # 'none' must be special cased because an empty option string
141    # means 'all'. Still, we're explicit about that.
142    #
143    # TODO: Make this produce the minimal output; sometimes 'none' and
144    # adding would be shorter.
145
146    all_resources = set(get_ALL_RESOURCES())
147    enabled = set(resources)
148
149    if enabled == all_resources:
150        result = 'all'
151    elif resources:
152        explicitly_disabled = all_resources - enabled
153        result = ''.join(sorted('-' + x for x in explicitly_disabled))
154    else:
155        result = 'none'
156    return result
157
158
159def setup_resources(resources=None):
160    """
161    Call either with a list of resources or a resource string.
162
163    If ``None`` is given, get the resource string from the environment.
164    """
165
166    if isinstance(resources, str) or resources is None:
167        resources = parse_resources(resources)
168
169    from . import support
170    support.use_resources = list(resources)
171    support.gevent_has_setup_resources = True
172
173    return resources
174
175def ensure_setup_resources():
176    # Call when you don't know if resources have been setup and you want to
177    # get the environment variable if needed.
178    # Returns an object with `is_resource_enabled`.
179    from . import support
180    if not support.gevent_has_setup_resources:
181        setup_resources()
182
183    return support
184
185def exit_without_resource(resource):
186    """
187    Call this in standalone test modules that can't use unittest.SkipTest.
188
189    Exits with a status of 0 if the resource isn't enabled.
190    """
191
192    if not ensure_setup_resources().is_resource_enabled(resource):
193        print("Skipped: %r not enabled" % (resource,))
194        import sys
195        sys.exit(0)
196
197def skip_without_resource(resource, reason=''):
198    requires = 'Requires resource %r' % (resource,)
199    if not reason:
200        reason = requires
201    else:
202        reason = reason + ' (' + requires + ')'
203
204    if not ensure_setup_resources().is_resource_enabled(resource):
205        import unittest
206        raise unittest.SkipTest(reason)
207
208if __name__ == '__main__':
209    print(setup_resources())
210