requirements/openstack_requirements/tests/test_update.py
Hervé Beraud 55387a4f4a Use unittest.mock instead of mock
The mock third party library was needed for mock support in py2
runtimes. Since we now only support py36 and later, we can use the
standard lib unittest.mock module instead.

Change-Id: Ib4184a05e753c3d1368167b91fc43bcc64a176c3
2020-06-09 19:16:34 +02:00

457 lines
20 KiB
Python

# Copyright 2013 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
import sys
import textwrap
from unittest import mock
import fixtures
import testscenarios
import testtools
from testtools import matchers
from openstack_requirements.cmds import update
from openstack_requirements import project
from openstack_requirements import requirement
from openstack_requirements.tests import common
load_tests = testscenarios.load_tests_apply_scenarios
class SmokeTest(testtools.TestCase):
def test_project(self):
global_env = self.useFixture(common.GlobalRequirements())
global_reqs = common._file_to_list(global_env.req_file)
# This is testing our test input data. Perhaps remove? (lifeless)
self.assertIn("jsonschema!=1.4.0,<2,>=1.0.0", global_reqs)
# And test the end to end call of update.py, UI and all.
self.project = self.useFixture(common.project_fixture)
capture = six.StringIO()
update.main(['--source', global_env.root, self.project.root], capture)
reqs = common._file_to_list(self.project.req_file)
# ensure various updates take
self.assertIn("jsonschema!=1.4.0,<2,>=1.0.0", reqs)
self.assertIn("python-keystoneclient>=0.4.1", reqs)
self.assertIn("SQLAlchemy<=0.7.99,>=0.7", reqs)
expected = ('Version change for: greenlet, SQLAlchemy, eventlet, PasteDeploy, routes, WebOb, wsgiref, boto, kombu, python-swiftclient, lxml, jsonschema, python-keystoneclient\n' # noqa
"""Updated %(project)s/requirements.txt:
greenlet>=0.3.1 -> greenlet>=0.3.2
SQLAlchemy>=0.7.8,<=1.0.17 -> SQLAlchemy<=0.7.99,>=0.7
eventlet>=0.9.12 -> eventlet>=0.12.0
PasteDeploy -> PasteDeploy>=1.5.0
routes -> Routes>=1.12.3
WebOb>=1.2 -> WebOb<1.3,>=1.2.3
wsgiref -> wsgiref>=0.1.2
boto -> boto>=2.4.0
kombu>2.4.7 -> kombu>=2.4.8
python-swiftclient>=1.2,<4 -> python-swiftclient>=1.2
lxml -> lxml>=2.3
jsonschema -> jsonschema!=1.4.0,<2,>=1.0.0
python-keystoneclient>=0.2.0 -> python-keystoneclient>=0.4.1
Version change for: mox, mox3, testrepository, testtools
Updated %(project)s/test-requirements.txt:
mox==0.5.3 -> mox>=0.5.3
mox3==0.21.0 -> mox3>=0.7.0
testrepository>=0.0.13 -> testrepository>=0.0.17
testtools>=0.9.27 -> testtools>=0.9.32
""") % dict(project=self.project.root)
self.assertEqual(expected, capture.getvalue())
class UpdateTest(testtools.TestCase):
def test_project(self):
reqs = common.project_file(
self.fail, common.project_project, 'requirements.txt')
# ensure various updates take
self.assertIn("jsonschema!=1.4.0,<2,>=1.0.0", reqs)
self.assertIn("python-keystoneclient>=0.4.1", reqs)
self.assertIn("SQLAlchemy<=0.7.99,>=0.7", reqs)
def test_requirements_header(self):
_REQS_HEADER = [
'# The order of packages is significant, because pip processes '
'them in the order',
'# of appearance. Changing the order has an impact on the overall '
'integration',
'# process, which may cause wedges in the gate later.',
]
reqs = common.project_file(
self.fail, common.project_project, 'requirements.txt')
self.assertEqual(_REQS_HEADER, reqs[:3])
def test_project_with_oslo(self):
reqs = common.project_file(
self.fail, common.oslo_project, 'requirements.txt')
oslo_tar = ("-f http://tarballs.openstack.org/oslo.config/"
"oslo.config-1.2.0a3.tar.gz#egg=oslo.config-1.2.0a3")
self.assertIn(oslo_tar, reqs)
def test_test_project(self):
reqs = common.project_file(
self.fail, common.project_project, 'test-requirements.txt')
self.assertIn("testtools>=0.9.32", reqs)
self.assertIn("testrepository>=0.0.17", reqs)
# make sure we didn't add something we shouldn't
self.assertNotIn("sphinxcontrib-pecanwsme>=0.2", reqs)
def test_install_setup(self):
setup_contents = common.project_file(
self.fail, common.project_project, 'setup.py', suffix='global')
self.assertIn("# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO"
" - DO NOT EDIT", setup_contents)
def test_no_install_setup(self):
actions = update._process_project(
common.oslo_project, common.global_reqs, None, None, None,
False)
for action in actions:
if type(action) is project.File:
self.assertNotEqual(action.filename, 'setup.py')
# These are tests which don't need to run the project update in advance
def test_requirement_not_in_global(self):
actions = update._process_project(
common.bad_project, common.global_reqs, None, None, None, False)
errors = [a for a in actions if type(a) is project.Error]
msg = u"'thisisnotarealdependency' is not in global-requirements.txt"
self.assertIn(msg, errors[0].message)
def test_requirement_in_blacklist(self):
actions = update._process_project(
common.bad_project, common.global_reqs, None, None, None, False,
blacklist={'thisisnotarealdependency': None})
errors = [a for a in actions if type(a) is project.Error]
self.assertEqual([], errors)
def test_requirement_not_in_global_non_fatal(self):
reqs = common.project_file(
self.fail, common.bad_project, 'requirements.txt',
non_std_reqs=True)
self.assertNotIn("thisisnotarealdependency", reqs)
def test_requirement_soft_update(self):
reqs = common.project_file(
self.fail, common.bad_project, 'requirements.txt',
softupdate=True)
self.assertIn("thisisnotarealdependency", reqs)
# testing output
def test_non_verbose_output(self):
actions = update._process_project(
common.project_project, common.global_reqs, None, None, None,
False)
capture = six.StringIO()
project.write(
common.project_project, actions, capture, False, True)
expected = ('Version change for: greenlet, SQLAlchemy, eventlet, PasteDeploy, routes, WebOb, wsgiref, boto, kombu, python-swiftclient, lxml, jsonschema, python-keystoneclient\n' # noqa
"""Updated %(project)s/requirements.txt:
greenlet>=0.3.1 -> greenlet>=0.3.2
SQLAlchemy>=0.7.8,<=1.0.17 -> SQLAlchemy<=0.7.99,>=0.7
eventlet>=0.9.12 -> eventlet>=0.12.0
PasteDeploy -> PasteDeploy>=1.5.0
routes -> Routes>=1.12.3
WebOb>=1.2 -> WebOb<1.3,>=1.2.3
wsgiref -> wsgiref>=0.1.2
boto -> boto>=2.4.0
kombu>2.4.7 -> kombu>=2.4.8
python-swiftclient>=1.2,<4 -> python-swiftclient>=1.2
lxml -> lxml>=2.3
jsonschema -> jsonschema!=1.4.0,<2,>=1.0.0
python-keystoneclient>=0.2.0 -> python-keystoneclient>=0.4.1
Version change for: mox, mox3, testrepository, testtools
Updated %(project)s/test-requirements.txt:
mox==0.5.3 -> mox>=0.5.3
mox3==0.21.0 -> mox3>=0.7.0
testrepository>=0.0.13 -> testrepository>=0.0.17
testtools>=0.9.27 -> testtools>=0.9.32
""") % dict(project=common.project_project['root'])
self.assertEqual(expected, capture.getvalue())
def test_verbose_output(self):
actions = update._process_project(
common.project_project, common.global_reqs, None, None, None,
False)
capture = six.StringIO()
project.write(
common.project_project, actions, capture, True, True)
expected = ("""Syncing %(project)s/requirements.txt
Version change for: greenlet, SQLAlchemy, eventlet, PasteDeploy, routes, WebOb, wsgiref, boto, kombu, python-swiftclient, lxml, jsonschema, python-keystoneclient\n""" # noqa
"""Updated %(project)s/requirements.txt:
greenlet>=0.3.1 -> greenlet>=0.3.2
SQLAlchemy>=0.7.8,<=1.0.17 -> SQLAlchemy<=0.7.99,>=0.7
eventlet>=0.9.12 -> eventlet>=0.12.0
PasteDeploy -> PasteDeploy>=1.5.0
routes -> Routes>=1.12.3
WebOb>=1.2 -> WebOb<1.3,>=1.2.3
wsgiref -> wsgiref>=0.1.2
boto -> boto>=2.4.0
kombu>2.4.7 -> kombu>=2.4.8
python-swiftclient>=1.2,<4 -> python-swiftclient>=1.2
lxml -> lxml>=2.3
jsonschema -> jsonschema!=1.4.0,<2,>=1.0.0
python-keystoneclient>=0.2.0 -> python-keystoneclient>=0.4.1
Syncing %(project)s/test-requirements.txt
Version change for: mox, mox3, testrepository, testtools
Updated %(project)s/test-requirements.txt:
mox==0.5.3 -> mox>=0.5.3
mox3==0.21.0 -> mox3>=0.7.0
testrepository>=0.0.13 -> testrepository>=0.0.17
testtools>=0.9.27 -> testtools>=0.9.32
Syncing setup.py
""") % dict(project=common.project_project['root'])
self.assertEqual(expected, capture.getvalue())
class TestMain(testtools.TestCase):
@mock.patch('os.path.isdir', return_value=True)
def test_smoke(self, mock_isdir):
def check_params(
root, source, suffix, softupdate, hacking, stdout, verbose,
non_std_reqs):
self.expectThat(root, matchers.Equals('/dev/zero'))
self.expectThat(source, matchers.Equals('/dev/null'))
self.expectThat(suffix, matchers.Equals(''))
self.expectThat(softupdate, matchers.Equals(None))
self.expectThat(hacking, matchers.Equals(None))
self.expectThat(stdout, matchers.Equals(sys.stdout))
self.expectThat(verbose, matchers.Equals(None))
self.expectThat(non_std_reqs, matchers.Equals(True))
with fixtures.EnvironmentVariable('NON_STANDARD_REQS', '1'):
update.main(
['--source', '/dev/null', '/dev/zero'], _worker=check_params)
self.expectThat(mock_isdir.called, matchers.Equals(True))
@mock.patch('os.path.isdir', return_value=True)
def test_suffix(self, mock_isdir):
def check_params(
root, source, suffix, softupdate, hacking, stdout, verbose,
non_std_reqs):
self.expectThat(suffix, matchers.Equals('global'))
update.main(['-o', 'global', '/dev/zero'], _worker=check_params)
self.expectThat(mock_isdir.called, matchers.Equals(True))
def test_isdirectory(self):
def never_called(
root, source, suffix, softupdate, hacking, stdout, verbose,
non_std_reqs):
self.expectThat(False, matchers.Equals(True),
message=("update.main() should riase an "
"exception before getting here"))
with testtools.ExpectedException(Exception,
"/dev/zero is not a directory"):
update.main(['/dev/zero'], _worker=never_called)
class TestSyncRequirementsFile(testtools.TestCase):
def test_multiple_lines_in_global_one_in_project(self):
global_content = textwrap.dedent("""\
foo<2;python_version=='2.7'
foo>1;python_version!='2.7'
""")
project_content = textwrap.dedent("""\
foo
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
self.assertEqual(requirement.Requirements([
requirement.Requirement(
'foo', '', '<2', "python_version=='2.7'", ''),
requirement.Requirement(
'foo', '', '>1', "python_version!='2.7'", '')]),
reqs)
self.assertEqual(project.StdOut(
" foo "
"-> foo<2;python_version=='2.7'\n"), actions[2])
self.assertEqual(project.StdOut(
" "
"-> foo>1;python_version!='2.7'\n"), actions[3])
self.assertThat(actions, matchers.HasLength(4))
def test_multiple_lines_separated_in_project_nochange(self):
global_content = textwrap.dedent("""\
foo<2;python_version=='2.7'
foo>1;python_version!='2.7'
""")
project_content = textwrap.dedent("""\
foo<2;python_version=='2.7'
# mumbo gumbo
foo>1;python_version!='2.7'
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
self.assertEqual(requirement.Requirements([
requirement.Requirement(
'foo', '', '<2', "python_version=='2.7'", ''),
requirement.Requirement(
'foo', '', '>1', "python_version!='2.7'", ''),
requirement.Requirement(
'', '', '', '', "# mumbo gumbo")]),
reqs)
self.assertThat(actions, matchers.HasLength(0))
def test_multiple_lines_separated_in_project(self):
global_content = textwrap.dedent("""\
foo<2;python_version=='2.7'
foo>1;python_version!='2.7'
""")
project_content = textwrap.dedent("""\
foo<1.8;python_version=='2.7'
# mumbo gumbo
foo>0.9;python_version!='2.7'
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
self.assertEqual(requirement.Requirements([
requirement.Requirement(
'foo', '', '<2', "python_version=='2.7'", ''),
requirement.Requirement(
'foo', '', '>1', "python_version!='2.7'", ''),
requirement.Requirement(
'', '', '', '', "# mumbo gumbo")]),
reqs)
self.assertEqual(project.StdOut(
" foo<1.8;python_version=='2.7' -> "
"foo<2;python_version=='2.7'\n"), actions[2])
self.assertEqual(project.StdOut(
" foo>0.9;python_version!='2.7' -> "
"foo>1;python_version!='2.7'\n"), actions[3])
self.assertThat(actions, matchers.HasLength(4))
def test_multiple_lines_nochange(self):
global_content = textwrap.dedent("""\
foo<2;python_version=='2.7'
foo>1;python_version!='2.7'
""")
project_content = textwrap.dedent("""\
foo<2;python_version=='2.7'
foo>1;python_version!='2.7'
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
self.assertEqual(requirement.Requirements([
requirement.Requirement(
'foo', '', '<2', "python_version=='2.7'", ''),
requirement.Requirement(
'foo', '', '>1', "python_version!='2.7'", '')]),
reqs)
self.assertThat(actions, matchers.HasLength(0))
def test_single_global_multiple_in_project(self):
global_content = textwrap.dedent("""\
foo>1
""")
project_content = textwrap.dedent("""\
foo<2;python_version=='2.7'
foo>1;python_version!='2.7'
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
self.assertEqual(requirement.Requirements([
requirement.Requirement('foo', '', '>1', "", '')]),
reqs)
self.assertEqual(project.StdOut(
" foo<2;python_version=='2.7' -> foo>1\n"), actions[2])
self.assertEqual(project.StdOut(
" foo>1;python_version!='2.7' -> \n"), actions[3])
self.assertThat(actions, matchers.HasLength(4))
def test_unparseable_line(self):
global_content = textwrap.dedent("""\
foo
""")
project_content = textwrap.dedent("""\
foo
-e https://git.openstack.org/openstack/neutron.git#egg=neutron
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
n = '-e https://git.openstack.org/openstack/neutron.git#egg=neutron'
self.assertEqual(requirement.Requirements([
requirement.Requirement('foo', '', '', '', ''),
requirement.Requirement('', '', '', '', n)]),
reqs)
def test_extras_kept(self):
global_content = textwrap.dedent("""\
oslo.db>1.4.1
""")
project_content = textwrap.dedent("""\
oslo.db[fixture,mysql]>1.3
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
self.assertEqual(requirement.Requirements([
requirement.Requirement(
'oslo.db', '', '>1.4.1', '', '', ['fixture', 'mysql'])]),
reqs)
self.assertThat(actions, matchers.HasLength(3))
self.assertEqual(project.StdOut(
" oslo.db[fixture,mysql]>1.3 -> "
"oslo.db[fixture,mysql]>1.4.1\n"), actions[2])
class TestCopyRequires(testtools.TestCase):
def test_extras_no_change(self):
global_content = textwrap.dedent(u"""\
foo<2;python_version=='2.7' # BSD
foo>1;python_version!='2.7'
freddy
""")
setup_cfg = textwrap.dedent(u"""\
[metadata]
name = openstack.requirements
[extras]
test =
foo<2:python_version=='2.7' # BSD
foo>1:python_version!='2.7'
opt =
freddy
""")
proj = {}
proj['root'] = '/dev/null'
proj['requirements'] = {}
proj['setup.cfg'] = setup_cfg
global_reqs = requirement.parse(global_content)
actions = update._copy_requires(
u'', False, False, proj, global_reqs, False)
self.assertEqual([
project.Verbose('Syncing extra [opt]'),
project.Verbose('Syncing extra [test]'),
project.File('setup.cfg', setup_cfg)], actions)