mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge pull request #8118 from ilanschnell/yaml_updates
This commit is contained in:
commit
cbfbc29b63
16 changed files with 374 additions and 60 deletions
|
@ -29,7 +29,7 @@ def generate_yaml_tests(directory):
|
||||||
"""
|
"""
|
||||||
Generate yaml test cases from the yaml files in the given directory
|
Generate yaml test cases from the yaml files in the given directory
|
||||||
"""
|
"""
|
||||||
for yml_file in directory.glob("*/*.yml"):
|
for yml_file in directory.glob("*.yml"):
|
||||||
data = yaml.safe_load(yml_file.read_text())
|
data = yaml.safe_load(yml_file.read_text())
|
||||||
assert "cases" in data, "A fixture needs cases to be used in testing"
|
assert "cases" in data, "A fixture needs cases to be used in testing"
|
||||||
|
|
||||||
|
@ -40,18 +40,23 @@ def generate_yaml_tests(directory):
|
||||||
base = data.get("base", {})
|
base = data.get("base", {})
|
||||||
cases = data["cases"]
|
cases = data["cases"]
|
||||||
|
|
||||||
for i, case_template in enumerate(cases):
|
for resolver in 'old', 'new':
|
||||||
case = base.copy()
|
for i, case_template in enumerate(cases):
|
||||||
case.update(case_template)
|
case = base.copy()
|
||||||
|
case.update(case_template)
|
||||||
|
|
||||||
case[":name:"] = base_name
|
case[":name:"] = base_name
|
||||||
if len(cases) > 1:
|
if len(cases) > 1:
|
||||||
case[":name:"] += "-" + str(i)
|
case[":name:"] += "-" + str(i)
|
||||||
|
case[":name:"] += "*" + resolver
|
||||||
|
case[":resolver:"] = resolver
|
||||||
|
|
||||||
if case.pop("skip", False):
|
skip = case.pop("skip", False)
|
||||||
case = pytest.param(case, marks=pytest.mark.xfail)
|
assert skip in [False, True, 'old', 'new']
|
||||||
|
if skip is True or skip == resolver:
|
||||||
|
case = pytest.param(case, marks=pytest.mark.xfail)
|
||||||
|
|
||||||
yield case
|
yield case
|
||||||
|
|
||||||
|
|
||||||
def id_func(param):
|
def id_func(param):
|
||||||
|
@ -92,60 +97,44 @@ def convert_to_dict(string):
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
|
||||||
def handle_request(script, action, requirement, options):
|
def handle_request(script, action, requirement, options, new_resolver=False):
|
||||||
assert isinstance(requirement, str), (
|
|
||||||
"Need install requirement to be a string only"
|
|
||||||
)
|
|
||||||
if action == 'install':
|
if action == 'install':
|
||||||
args = ['install', "--no-index", "--find-links",
|
args = ['install']
|
||||||
path_to_url(script.scratch_path)]
|
if new_resolver:
|
||||||
|
args.append("--unstable-feature=resolver")
|
||||||
|
args.extend(["--no-index", "--find-links",
|
||||||
|
path_to_url(script.scratch_path)])
|
||||||
elif action == 'uninstall':
|
elif action == 'uninstall':
|
||||||
args = ['uninstall', '--yes']
|
args = ['uninstall', '--yes']
|
||||||
else:
|
else:
|
||||||
raise "Did not excpet action: {!r}".format(action)
|
raise "Did not excpet action: {!r}".format(action)
|
||||||
args.append(requirement)
|
|
||||||
|
if isinstance(requirement, str):
|
||||||
|
args.append(requirement)
|
||||||
|
elif isinstance(requirement, list):
|
||||||
|
args.extend(requirement)
|
||||||
|
else:
|
||||||
|
raise "requirement neither str nor list {!r}".format(requirement)
|
||||||
|
|
||||||
args.extend(options)
|
args.extend(options)
|
||||||
args.append("--verbose")
|
args.append("--verbose")
|
||||||
|
|
||||||
result = script.pip(*args,
|
result = script.pip(*args,
|
||||||
allow_stderr_error=True,
|
allow_stderr_error=True,
|
||||||
allow_stderr_warning=True)
|
allow_stderr_warning=True,
|
||||||
|
allow_error=True)
|
||||||
|
|
||||||
retval = {
|
# Check which packages got installed
|
||||||
"_result_object": result,
|
state = []
|
||||||
}
|
for path in os.listdir(script.site_packages_path):
|
||||||
if result.returncode == 0:
|
if path.endswith(".dist-info"):
|
||||||
# Check which packages got installed
|
name, version = (
|
||||||
retval["state"] = []
|
os.path.basename(path)[:-len(".dist-info")]
|
||||||
|
).rsplit("-", 1)
|
||||||
|
# TODO: information about extras.
|
||||||
|
state.append(" ".join((name, version)))
|
||||||
|
|
||||||
for path in os.listdir(script.site_packages_path):
|
return {"result": result, "state": sorted(state)}
|
||||||
if path.endswith(".dist-info"):
|
|
||||||
name, version = (
|
|
||||||
os.path.basename(path)[:-len(".dist-info")]
|
|
||||||
).rsplit("-", 1)
|
|
||||||
|
|
||||||
# TODO: information about extras.
|
|
||||||
|
|
||||||
retval["state"].append(" ".join((name, version)))
|
|
||||||
|
|
||||||
retval["state"].sort()
|
|
||||||
|
|
||||||
elif "conflicting" in result.stderr.lower():
|
|
||||||
retval["conflicting"] = []
|
|
||||||
|
|
||||||
message = result.stderr.rsplit("\n", 1)[-1]
|
|
||||||
|
|
||||||
# XXX: There might be a better way than parsing the message
|
|
||||||
for match in re.finditer(message, _conflict_finder_pat):
|
|
||||||
di = match.groupdict()
|
|
||||||
retval["conflicting"].append(
|
|
||||||
{
|
|
||||||
"required_by": "{} {}".format(di["name"], di["version"]),
|
|
||||||
"selector": di["selector"]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return retval
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.yaml
|
@pytest.mark.yaml
|
||||||
|
@ -184,7 +173,26 @@ def test_yaml_based(script, case):
|
||||||
# Perform the requested action
|
# Perform the requested action
|
||||||
effect = handle_request(script, action,
|
effect = handle_request(script, action,
|
||||||
request[action],
|
request[action],
|
||||||
request.get('options', '').split())
|
request.get('options', '').split(),
|
||||||
|
case[':resolver:'] == 'new')
|
||||||
|
|
||||||
assert effect['state'] == (response['state'] or []), \
|
if 0: # for analyzing output easier
|
||||||
str(effect["_result_object"])
|
with open(DATA_DIR.parent / "yaml" /
|
||||||
|
case[':name:'].replace('*', '-'), 'w') as fo:
|
||||||
|
result = effect['result']
|
||||||
|
fo.write("=== RETURNCODE = %d\n" % result.returncode)
|
||||||
|
fo.write("=== STDERR ===:\n%s\n" % result.stderr)
|
||||||
|
|
||||||
|
if 'state' in response:
|
||||||
|
assert effect['state'] == (response['state'] or []), \
|
||||||
|
str(effect["result"])
|
||||||
|
|
||||||
|
error = False
|
||||||
|
if 'conflicting' in response:
|
||||||
|
error = True
|
||||||
|
|
||||||
|
if error:
|
||||||
|
if case[":resolver:"] == 'old':
|
||||||
|
assert effect["result"].returncode == 0, str(effect["result"])
|
||||||
|
elif case[":resolver:"] == 'new':
|
||||||
|
assert effect["result"].returncode == 1, str(effect["result"])
|
||||||
|
|
|
@ -533,6 +533,10 @@ class PipTestEnvironment(TestFileEnvironment):
|
||||||
`allow_stderr_warning` since warnings are weaker than errors.
|
`allow_stderr_warning` since warnings are weaker than errors.
|
||||||
:param allow_stderr_warning: whether a logged warning (or
|
:param allow_stderr_warning: whether a logged warning (or
|
||||||
deprecation message) is allowed in stderr.
|
deprecation message) is allowed in stderr.
|
||||||
|
:param allow_error: if True (default is False) does not raise
|
||||||
|
exception when the command exit value is non-zero. Implies
|
||||||
|
expect_error, but in contrast to expect_error will not assert
|
||||||
|
that the exit value is zero.
|
||||||
:param expect_error: if False (the default), asserts that the command
|
:param expect_error: if False (the default), asserts that the command
|
||||||
exits with 0. Otherwise, asserts that the command exits with a
|
exits with 0. Otherwise, asserts that the command exits with a
|
||||||
non-zero exit code. Passing True also implies allow_stderr_error
|
non-zero exit code. Passing True also implies allow_stderr_error
|
||||||
|
@ -553,10 +557,14 @@ class PipTestEnvironment(TestFileEnvironment):
|
||||||
# Partial fix for ScriptTest.run using `shell=True` on Windows.
|
# Partial fix for ScriptTest.run using `shell=True` on Windows.
|
||||||
args = [str(a).replace('^', '^^').replace('&', '^&') for a in args]
|
args = [str(a).replace('^', '^^').replace('&', '^&') for a in args]
|
||||||
|
|
||||||
# Remove `allow_stderr_error` and `allow_stderr_warning` before
|
# Remove `allow_stderr_error`, `allow_stderr_warning` and
|
||||||
# calling run() because PipTestEnvironment doesn't support them.
|
# `allow_error` before calling run() because PipTestEnvironment
|
||||||
|
# doesn't support them.
|
||||||
allow_stderr_error = kw.pop('allow_stderr_error', None)
|
allow_stderr_error = kw.pop('allow_stderr_error', None)
|
||||||
allow_stderr_warning = kw.pop('allow_stderr_warning', None)
|
allow_stderr_warning = kw.pop('allow_stderr_warning', None)
|
||||||
|
allow_error = kw.pop('allow_error', None)
|
||||||
|
if allow_error:
|
||||||
|
kw['expect_error'] = True
|
||||||
|
|
||||||
# Propagate default values.
|
# Propagate default values.
|
||||||
expect_error = kw.get('expect_error')
|
expect_error = kw.get('expect_error')
|
||||||
|
@ -596,7 +604,7 @@ class PipTestEnvironment(TestFileEnvironment):
|
||||||
kw['expect_stderr'] = True
|
kw['expect_stderr'] = True
|
||||||
result = super(PipTestEnvironment, self).run(cwd=cwd, *args, **kw)
|
result = super(PipTestEnvironment, self).run(cwd=cwd, *args, **kw)
|
||||||
|
|
||||||
if expect_error:
|
if expect_error and not allow_error:
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
raise AssertionError("Script passed unexpectedly.")
|
raise AssertionError("Script passed unexpectedly.")
|
||||||
|
|
|
@ -1,5 +1,31 @@
|
||||||
# Fixtures
|
# Fixtures
|
||||||
|
|
||||||
This directory contains fixtures for testing pip's resolver. The fixtures are written as yml files, with a convenient format that allows for specifying a custom index for temporary use.
|
This directory contains fixtures for testing pip's resolver.
|
||||||
|
The fixtures are written as `.yml` files, with a convenient format
|
||||||
|
that allows for specifying a custom index for temporary use.
|
||||||
|
|
||||||
|
The `.yml` files are organized in the following way. A `base` section
|
||||||
|
which ...
|
||||||
|
|
||||||
|
The linter is very useful for initally checking `.yml` files, e.g.:
|
||||||
|
|
||||||
|
$ python linter.py -v simple.yml
|
||||||
|
|
||||||
|
To run only the yaml tests, use (from the root of the source tree):
|
||||||
|
|
||||||
|
$ tox -e py38 -- -m yaml -vv
|
||||||
|
|
||||||
|
Or, in order to avoid collecting all the test cases:
|
||||||
|
|
||||||
|
$ tox -e py38 -- tests/functional/test_yaml.py
|
||||||
|
|
||||||
|
Or, only a specific test:
|
||||||
|
|
||||||
|
$ tox -e py38 -- tests/functional/test_yaml.py -k simple
|
||||||
|
|
||||||
|
Or, just a specific test case:
|
||||||
|
|
||||||
|
$ tox -e py38 -- tests/functional/test_yaml.py -k simple-0
|
||||||
|
|
||||||
|
|
||||||
<!-- TODO: Add a good description of the format and how it can be used. -->
|
<!-- TODO: Add a good description of the format and how it can be used. -->
|
||||||
|
|
40
tests/yaml/backtrack.yml
Normal file
40
tests/yaml/backtrack.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# Pradyun's backtracking example
|
||||||
|
base:
|
||||||
|
available:
|
||||||
|
- A 1.0.0; depends B == 1.0.0
|
||||||
|
- A 2.0.0; depends B == 2.0.0, C == 1.0.0
|
||||||
|
- A 3.0.0; depends B == 3.0.0, C == 2.0.0
|
||||||
|
- A 4.0.0; depends B == 4.0.0, C == 3.0.0
|
||||||
|
- A 5.0.0; depends B == 5.0.0, C == 4.0.0
|
||||||
|
- A 6.0.0; depends B == 6.0.0, C == 5.0.0
|
||||||
|
- A 7.0.0; depends B == 7.0.0, C == 6.0.0
|
||||||
|
- A 8.0.0; depends B == 8.0.0, C == 7.0.0
|
||||||
|
|
||||||
|
- B 1.0.0; depends C == 1.0.0
|
||||||
|
- B 2.0.0; depends C == 2.0.0
|
||||||
|
- B 3.0.0; depends C == 3.0.0
|
||||||
|
- B 4.0.0; depends C == 4.0.0
|
||||||
|
- B 5.0.0; depends C == 5.0.0
|
||||||
|
- B 6.0.0; depends C == 6.0.0
|
||||||
|
- B 7.0.0; depends C == 7.0.0
|
||||||
|
- B 8.0.0; depends C == 8.0.0
|
||||||
|
|
||||||
|
- C 1.0.0
|
||||||
|
- C 2.0.0
|
||||||
|
- C 3.0.0
|
||||||
|
- C 4.0.0
|
||||||
|
- C 5.0.0
|
||||||
|
- C 6.0.0
|
||||||
|
- C 7.0.0
|
||||||
|
- C 8.0.0
|
||||||
|
|
||||||
|
cases:
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: A
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- A 1.0.0
|
||||||
|
- B 1.0.0
|
||||||
|
- C 1.0.0
|
||||||
|
skip: old
|
|
@ -16,6 +16,7 @@ cases:
|
||||||
- B 1.0.0
|
- B 1.0.0
|
||||||
- C 1.0.0
|
- C 1.0.0
|
||||||
- D 1.0.0
|
- D 1.0.0
|
||||||
|
skip: new
|
||||||
-
|
-
|
||||||
request:
|
request:
|
||||||
- install: B
|
- install: B
|
||||||
|
@ -25,6 +26,7 @@ cases:
|
||||||
- B 1.0.0
|
- B 1.0.0
|
||||||
- C 1.0.0
|
- C 1.0.0
|
||||||
- D 1.0.0
|
- D 1.0.0
|
||||||
|
skip: new
|
||||||
-
|
-
|
||||||
request:
|
request:
|
||||||
- install: C
|
- install: C
|
||||||
|
@ -34,6 +36,7 @@ cases:
|
||||||
- B 1.0.0
|
- B 1.0.0
|
||||||
- C 1.0.0
|
- C 1.0.0
|
||||||
- D 1.0.0
|
- D 1.0.0
|
||||||
|
skip: new
|
||||||
-
|
-
|
||||||
request:
|
request:
|
||||||
- install: D
|
- install: D
|
||||||
|
@ -43,3 +46,4 @@ cases:
|
||||||
- B 1.0.0
|
- B 1.0.0
|
||||||
- C 1.0.0
|
- C 1.0.0
|
||||||
- D 1.0.0
|
- D 1.0.0
|
||||||
|
skip: new
|
|
@ -39,4 +39,4 @@ cases:
|
||||||
- D 1.0.0
|
- D 1.0.0
|
||||||
- E 1.0.0
|
- E 1.0.0
|
||||||
- F 1.0.0
|
- F 1.0.0
|
||||||
skip: true
|
skip: old
|
92
tests/yaml/linter.py
Normal file
92
tests/yaml/linter.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import sys
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
sys.path.insert(0, '../../src')
|
||||||
|
sys.path.insert(0, '../..')
|
||||||
|
|
||||||
|
|
||||||
|
def check_dict(d, required=None, optional=None):
|
||||||
|
assert isinstance(d, dict)
|
||||||
|
if required is None:
|
||||||
|
required = []
|
||||||
|
if optional is None:
|
||||||
|
optional = []
|
||||||
|
for key in required:
|
||||||
|
if key not in d:
|
||||||
|
sys.exit("key %r is required" % key)
|
||||||
|
allowed_keys = set(required)
|
||||||
|
allowed_keys.update(optional)
|
||||||
|
for key in d.keys():
|
||||||
|
if key not in allowed_keys:
|
||||||
|
sys.exit("key %r is not allowed. Allowed keys are: %r" %
|
||||||
|
(key, allowed_keys))
|
||||||
|
|
||||||
|
|
||||||
|
def lint_case(case, verbose=False):
|
||||||
|
from tests.functional.test_yaml import convert_to_dict
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print("--- linting case ---")
|
||||||
|
pprint(case)
|
||||||
|
|
||||||
|
check_dict(case, optional=['available', 'request', 'response', 'skip'])
|
||||||
|
available = case.get("available", [])
|
||||||
|
requests = case.get("request", [])
|
||||||
|
responses = case.get("response", [])
|
||||||
|
assert isinstance(available, list)
|
||||||
|
assert isinstance(requests, list)
|
||||||
|
assert isinstance(responses, list)
|
||||||
|
assert len(requests) == len(responses)
|
||||||
|
|
||||||
|
for package in available:
|
||||||
|
if isinstance(package, str):
|
||||||
|
package = convert_to_dict(package)
|
||||||
|
if verbose:
|
||||||
|
pprint(package)
|
||||||
|
check_dict(package,
|
||||||
|
required=['name', 'version'],
|
||||||
|
optional=['depends', 'extras'])
|
||||||
|
|
||||||
|
for request, response in zip(requests, responses):
|
||||||
|
check_dict(request, optional=['install', 'uninstall', 'options'])
|
||||||
|
check_dict(response, optional=['state', 'conflicting'])
|
||||||
|
assert len(response) == 1
|
||||||
|
assert isinstance(response.get('state') or [], list)
|
||||||
|
|
||||||
|
|
||||||
|
def lint_yml(yml_file, verbose=False):
|
||||||
|
if verbose:
|
||||||
|
print("=== linting: %s ===" % yml_file)
|
||||||
|
assert yml_file.endswith(".yml")
|
||||||
|
with open(yml_file) as fi:
|
||||||
|
data = yaml.safe_load(fi)
|
||||||
|
if verbose:
|
||||||
|
pprint(data)
|
||||||
|
|
||||||
|
check_dict(data, required=['cases'], optional=['base'])
|
||||||
|
base = data.get("base", {})
|
||||||
|
cases = data["cases"]
|
||||||
|
for i, case_template in enumerate(cases):
|
||||||
|
case = base.copy()
|
||||||
|
case.update(case_template)
|
||||||
|
lint_case(case, verbose)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
p = OptionParser(usage="usage: %prog [options] FILE ...",
|
||||||
|
description="linter for pip's yaml test FILE(s)")
|
||||||
|
|
||||||
|
p.add_option('-v', '--verbose',
|
||||||
|
action="store_true")
|
||||||
|
|
||||||
|
opts, args = p.parse_args()
|
||||||
|
|
||||||
|
if len(args) < 1:
|
||||||
|
p.error('at least one argument required, try -h')
|
||||||
|
|
||||||
|
for yml_file in args:
|
||||||
|
lint_yml(yml_file, opts.verbose)
|
44
tests/yaml/overlap1.yml
Normal file
44
tests/yaml/overlap1.yml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# https://medium.com/knerd/the-nine-circles-of-python-dependency-hell-481d53e3e025
|
||||||
|
# Circle 4: Overlapping transitive dependencies
|
||||||
|
base:
|
||||||
|
available:
|
||||||
|
- myapp 0.2.4; depends fussy, capridous
|
||||||
|
- name: fussy
|
||||||
|
version: 3.8.0
|
||||||
|
depends: ['requests >=1.2.0,<3']
|
||||||
|
- name: capridous
|
||||||
|
version: 1.1.0
|
||||||
|
depends: ['requests >=1.0.3,<2']
|
||||||
|
- requests 1.0.1
|
||||||
|
- requests 1.0.3
|
||||||
|
- requests 1.1.0
|
||||||
|
- requests 1.2.0
|
||||||
|
- requests 1.3.0
|
||||||
|
- requests 2.1.0
|
||||||
|
- requests 3.2.0
|
||||||
|
|
||||||
|
cases:
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: myapp
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- capridous 1.1.0
|
||||||
|
- fussy 3.8.0
|
||||||
|
- myapp 0.2.4
|
||||||
|
- requests 1.3.0
|
||||||
|
skip: old
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: fussy
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- fussy 3.8.0
|
||||||
|
- requests 2.1.0
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: capridous
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- capridous 1.1.0
|
||||||
|
- requests 1.3.0
|
37
tests/yaml/pip988.yml
Normal file
37
tests/yaml/pip988.yml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# https://github.com/pypa/pip/issues/988#issuecomment-606967707
|
||||||
|
base:
|
||||||
|
available:
|
||||||
|
- A 1.0.0; depends B >= 1.0.0, C >= 1.0.0
|
||||||
|
- A 2.0.0; depends B >= 2.0.0, C >= 1.0.0
|
||||||
|
- B 1.0.0; depends C >= 1.0.0
|
||||||
|
- B 2.0.0; depends C >= 2.0.0
|
||||||
|
- C 1.0.0
|
||||||
|
- C 2.0.0
|
||||||
|
|
||||||
|
cases:
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: C==1.0.0
|
||||||
|
- install: B==1.0.0
|
||||||
|
- install: A==1.0.0
|
||||||
|
- install: A==2.0.0
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- C 1.0.0
|
||||||
|
- state:
|
||||||
|
- B 1.0.0
|
||||||
|
- C 1.0.0
|
||||||
|
- state:
|
||||||
|
- A 1.0.0
|
||||||
|
- B 1.0.0
|
||||||
|
- C 1.0.0
|
||||||
|
- state:
|
||||||
|
- A 2.0.0
|
||||||
|
- B 2.0.0
|
||||||
|
- C 2.0.0
|
||||||
|
# for the last install (A==2.0.0) the old resolver gives
|
||||||
|
# - A 2.0.0
|
||||||
|
# - B 2.0.0
|
||||||
|
# - C 1.0.0
|
||||||
|
# but because B 2.0.0 depends on C >=2.0.0 this is wrong
|
||||||
|
skip: old
|
24
tests/yaml/poetry2298.yml
Normal file
24
tests/yaml/poetry2298.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# see: https://github.com/python-poetry/poetry/issues/2298
|
||||||
|
base:
|
||||||
|
available:
|
||||||
|
- poetry 1.0.5; depends zappa == 0.51.0, sphinx == 3.0.1
|
||||||
|
- zappa 0.51.0; depends boto3
|
||||||
|
- sphinx 3.0.1; depends docutils
|
||||||
|
- boto3 1.4.5; depends botocore ~=1.5.0
|
||||||
|
- botocore 1.5.92; depends docutils <0.16
|
||||||
|
- docutils 0.16.0
|
||||||
|
- docutils 0.15.0
|
||||||
|
|
||||||
|
cases:
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: poetry
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- boto3 1.4.5
|
||||||
|
- botocore 1.5.92
|
||||||
|
- docutils 0.15.0
|
||||||
|
- poetry 1.0.5
|
||||||
|
- sphinx 3.0.1
|
||||||
|
- zappa 0.51.0
|
||||||
|
skip: old
|
|
@ -38,3 +38,10 @@ cases:
|
||||||
response:
|
response:
|
||||||
- state:
|
- state:
|
||||||
- base 0.1.0
|
- base 0.1.0
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: ['dep', 'simple==0.1.0']
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- dep 0.1.0
|
||||||
|
- simple 0.1.0
|
24
tests/yaml/trivial.yml
Normal file
24
tests/yaml/trivial.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
base:
|
||||||
|
available:
|
||||||
|
- a 0.1.0
|
||||||
|
- b 0.2.0
|
||||||
|
- c 0.3.0
|
||||||
|
|
||||||
|
cases:
|
||||||
|
-
|
||||||
|
request:
|
||||||
|
- install: ['a', 'b']
|
||||||
|
- install: c
|
||||||
|
- uninstall: ['b', 'c']
|
||||||
|
- uninstall: a
|
||||||
|
response:
|
||||||
|
- state:
|
||||||
|
- a 0.1.0
|
||||||
|
- b 0.2.0
|
||||||
|
- state:
|
||||||
|
- a 0.1.0
|
||||||
|
- b 0.2.0
|
||||||
|
- c 0.3.0
|
||||||
|
- state:
|
||||||
|
- a 0.1.0
|
||||||
|
- state: null
|
Loading…
Reference in a new issue