# 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 textwrap import pkg_resources import testscenarios import testtools from openstack_requirements import requirement load_tests = testscenarios.load_tests_apply_scenarios class TestParseRequirement(testtools.TestCase): dist_scenarios = [ ('package', dict( line='swift', req=requirement.Requirement('swift', '', '', '', ''))), ('specifier', dict( line='alembic>=0.4.1', req=requirement.Requirement('alembic', '', '>=0.4.1', '', ''))), ('specifiers', dict( line='alembic>=0.4.1,!=1.1.8', req=requirement.Requirement('alembic', '', '!=1.1.8,>=0.4.1', '', ''))), ('comment-only', dict( line='# foo', req=requirement.Requirement('', '', '', '', '# foo'))), ('comment', dict( line='Pint>=0.5 # BSD', req=requirement.Requirement('Pint', '', '>=0.5', '', '# BSD'))), ('comment-with-semicolon', dict( line='Pint>=0.5 # BSD;fred', req=requirement.Requirement('Pint', '', '>=0.5', '', '# BSD;fred'))), ('case', dict( line='Babel>=1.3', req=requirement.Requirement('Babel', '', '>=1.3', '', ''))), ('markers', dict( line="pywin32;sys_platform=='win32'", req=requirement.Requirement('pywin32', '', '', "sys_platform=='win32'", ''))), ('markers-with-comment', dict( line="Sphinx<=1.2; python_version=='2.7'# Sadface", req=requirement.Requirement('Sphinx', '', '<=1.2', "python_version=='2.7'", '# Sadface')))] url_scenarios = [ ('url', dict( line='file:///path/to/thing#egg=thing', req=requirement.Requirement('thing', 'file:///path/to/thing', '', '', ''), permit_urls=True)), ('oslo-url', dict( line='file:///path/to/oslo.thing#egg=oslo.thing', req=requirement.Requirement('oslo.thing', 'file:///path/to/oslo.thing', '', '', ''), permit_urls=True)), ('url-comment', dict( line='file:///path/to/thing#egg=thing # http://altpath#egg=boo', req=requirement.Requirement('thing', 'file:///path/to/thing', '', '', '# http://altpath#egg=boo'), permit_urls=True)), ('editable', dict( line='-e file:///path/to/bar#egg=bar', req=requirement.Requirement('bar', '-e file:///path/to/bar', '', '', ''), permit_urls=True)), ('editable_vcs_git', dict( line='-e git+http://github.com/path/to/oslo.bar#egg=oslo.bar', req=requirement.Requirement('oslo.bar', '-e git+http://github.com' '/path/to/oslo.bar', '', '', ''), permit_urls=True)), ('editable_vcs_git_ssh', dict( line='-e git+ssh://github.com/path/to/oslo.bar#egg=oslo.bar', req=requirement.Requirement('oslo.bar', '-e git+ssh://github.com' '/path/to/oslo.bar', '', '', ''), permit_urls=True)), ] scenarios = dist_scenarios + url_scenarios def test_parse(self): parsed = requirement.parse_line( self.line, permit_urls=getattr(self, 'permit_urls', False)) self.assertEqual(self.req, parsed) class TestParseRequirementFailures(testtools.TestCase): scenarios = [ ('url', dict(line='http://tarballs.openstack.org/oslo.config/' 'oslo.config-1.2.0a3.tar.gz#egg=oslo.config')), ('-e', dict(line='-e git+https://foo.com#egg=foo')), ('-f', dict(line='-f http://tarballs.openstack.org/'))] def test_does_not_parse(self): with testtools.ExpectedException(pkg_resources.RequirementParseError): requirement.parse_line(self.line) class TestToContent(testtools.TestCase): def test_smoke(self): reqs = requirement.to_content(requirement.Requirements( [requirement.Requirement( 'foo', '', '<=1', "python_version=='2.7'", '# BSD')]), marker_sep='!') self.assertEqual( ''.join(requirement._REQS_HEADER + ["foo<=1!python_version=='2.7' # BSD\n"]), reqs) def test_location(self): reqs = requirement.to_content(requirement.Requirements( [requirement.Requirement( 'foo', 'file://foo', '', "python_version=='2.7'", '# BSD')])) self.assertEqual( ''.join(requirement._REQS_HEADER + ["file://foo#egg=foo;python_version=='2.7' # BSD\n"]), reqs) class TestToReqs(testtools.TestCase): def test_editable(self): line = '-e file:///foo#egg=foo' reqs = list(requirement.to_reqs(line, permit_urls=True)) req = requirement.Requirement('foo', '-e file:///foo', '', '', '') self.assertEqual(reqs, [(req, line)]) def test_urls(self): line = 'file:///foo#egg=foo' reqs = list(requirement.to_reqs(line, permit_urls=True)) req = requirement.Requirement('foo', 'file:///foo', '', '', '') self.assertEqual(reqs, [(req, line)]) def test_not_urls(self): with testtools.ExpectedException(pkg_resources.RequirementParseError): list(requirement.to_reqs('file:///foo#egg=foo')) def test_multiline(self): content = textwrap.dedent("""\ oslo.config>=1.11.0 # Apache-2.0 oslo.concurrency>=2.3.0 # Apache-2.0 oslo.context>=0.2.0 # Apache-2.0 """) reqs = requirement.parse(content) self.assertEqual( set(['oslo.config', 'oslo.concurrency', 'oslo.context']), set(reqs.keys()), ) def test_extras(self): content = textwrap.dedent("""\ oslo.config>=1.11.0 # Apache-2.0 oslo.concurrency[fixtures]>=1.11.0 # Apache-2.0 oslo.db[fixtures,mysql]>=1.11.0 # Apache-2.0 """) reqs = requirement.parse(content) self.assertEqual( set(['oslo.config', 'oslo.concurrency', 'oslo.db']), set(reqs.keys()), ) self.assertEqual(reqs['oslo.config'][0][0].extras, frozenset(())) self.assertEqual(reqs['oslo.concurrency'][0][0].extras, frozenset(('fixtures',))) self.assertEqual(reqs['oslo.db'][0][0].extras, frozenset(('fixtures', 'mysql'))) self.assertItemsEqual(reqs, ['oslo.config', 'oslo.concurrency', 'oslo.db']) class TestCanonicalName(testtools.TestCase): def test_underscores(self): self.assertEqual('foo-bar', requirement.canonical_name('Foo_bar')) class TestToDict(testtools.TestCase): def test_canonicalises(self): req = requirement.Requirement('Foo_bar', '', '', '', '') self.assertEqual( {'foo-bar': [(req, '')]}, requirement.to_dict([(req, '')])) class TestReqPolicy(testtools.TestCase): def test_requirements_policy_pass(self): content = textwrap.dedent("""\ cffi>=1.1.1,!=1.1.2 other>=1.1.1 """) reqs = requirement.parse(content) policy_check = [x for x in requirement.check_reqs_bounds_policy(reqs)] self.assertEqual(len(policy_check), 0) def test_requirements_policy_fail(self): content = textwrap.dedent("""\ cffi>=1.1.1,!=1.1.0 other>=1,>=2,!=1.1.0 no_lower_bound """) reqs = requirement.parse(content) self.assertEqual([ 'Requirement cffi has a !=1.1.0 specifier that is not >=1.1.1', 'Requirement no-lower-bound needs a >= specifier', 'Requirement other has multiple >= specifier'], sorted([x for x in requirement.check_reqs_bounds_policy(reqs)]))