diff --git a/openstack_requirements/cmds/check_exists.py b/openstack_requirements/cmds/check_exists.py new file mode 100644 index 0000000000..76d171b762 --- /dev/null +++ b/openstack_requirements/cmds/check_exists.py @@ -0,0 +1,113 @@ +# 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. + +"""Check to see if a package from a project's requrements file exist in g-r or +u-c. + +""" + +from __future__ import print_function + +import argparse + +from packaging.specifiers import SpecifierSet +from packaging.version import Version + +from openstack_requirements import project +from openstack_requirements import requirement + + +def read_requirements_file(filename): + with open(filename, 'rt') as f: + body = f.read() + return requirement.parse(body) + + +def main(args=None): + parser = argparse.ArgumentParser() + parser.add_argument( + 'project', + default='', + help='path to the project source root folder.') + parser.add_argument( + '-u', '--upper-constraints', + default='upper-constraints.txt', + help='path to the upper-constraints.txt file') + parser.add_argument( + '-g', '--global-requirements', + default='global-requirements.txt', + help='Path to the global-requirements.txt file') + parser.add_argument( + '-b', '--blacklist', + default='blacklist.txt', + help='Path to the blacklist.txt file') + parser.add_argument( + '-G', '--gr-check', action='store_true', + help='Do a specifier check of global-requirements') + args = parser.parse_args(args) + + upper_constraints = read_requirements_file(args.upper_constraints) + global_requirements = read_requirements_file(args.global_requirements) + blacklist = read_requirements_file(args.blacklist) + project_data = project.read(args.project) + error_count = 0 + + for require_file, data in project_data.get('requirements', {}).items(): + print(u'\nComparing %s with global-requirements and upper-constraints' + % require_file) + requirements = requirement.parse(data) + for name, spec_list in requirements.items(): + if not name or name in blacklist: + continue + if name not in global_requirements: + print(u'%s from %s not found in global-requirements' % ( + name, require_file)) + error_count += 1 + continue + if name not in upper_constraints: + print(u'%s from %s not found in upper-constraints' % ( + name, require_file)) + error_count += 1 + continue + elif spec_list: + uc = upper_constraints[name][0][0] + gr = global_requirements[name][0][0] + spec_gr = SpecifierSet(gr.specifiers) + for req, _ in spec_list: + specs = SpecifierSet(req.specifiers) + # This assumes uc will only have == specifiers + for uc_spec in SpecifierSet(uc.specifiers): + # if the uc version isn't in the lower specifier + # then something is wrong. + if Version(uc_spec.version) not in specs: + print( + u'%s must be <= %s from upper-constraints and ' + 'include the upper-constraints version' % + (name, uc_spec.version)) + error_count += 1 + continue + if args.gr_check: + for spec in specs: + # g-r will mostly define blocked versions. And a + # local project may define there own, so there is + # no point checking a != specifier + if spec.operator == '!=': + continue + if spec.version not in spec_gr: + print( + u'Specifier %s from %s is failing check ' + 'from global-requirements specifiers %s' % + (spec.version, name, str(spec_gr))) + error_count += 1 + continue + + return 1 if error_count else 0 diff --git a/openstack_requirements/tests/common.py b/openstack_requirements/tests/common.py index 3706491016..69c72c8162 100644 --- a/openstack_requirements/tests/common.py +++ b/openstack_requirements/tests/common.py @@ -89,6 +89,11 @@ def make_project(fixture): global_reqs = requirement.parse( open("openstack_requirements/tests/files/gr-base.txt", "rt").read()) +upper_constraints = requirement.parse( + open("openstack_requirements/tests/files/upper-constraints.txt", + "rt").read()) +blacklist = requirement.parse( + open("openstack_requirements/tests/files/blacklist.txt", "rt").read()) pbr_project = make_project(pbr_fixture) project_project = make_project(project_fixture) bad_project = make_project(bad_project_fixture) diff --git a/openstack_requirements/tests/files/blacklist.txt b/openstack_requirements/tests/files/blacklist.txt new file mode 100644 index 0000000000..cb713d4c2f --- /dev/null +++ b/openstack_requirements/tests/files/blacklist.txt @@ -0,0 +1,11 @@ +# linters - each project may have a different version with loose convergence +# over time. +flake8 +flake8_docstrings +flake8-import-order +hacking +mccabe +pep257 +pep8 +pyflakes +pylint diff --git a/openstack_requirements/tests/files/project.txt b/openstack_requirements/tests/files/project.txt index f8ae7931f0..5a2cb6efca 100644 --- a/openstack_requirements/tests/files/project.txt +++ b/openstack_requirements/tests/files/project.txt @@ -4,7 +4,7 @@ greenlet>=0.3.1 # < 0.8.0/0.8 does not work, see https://bugs.launchpad.net/bugs/1153983 -SQLAlchemy>=0.7.8,<=0.7.99 +SQLAlchemy>=0.7.8,<=1.0.17 anyjson>=0.3.3 eventlet>=0.9.12 PasteDeploy @@ -22,7 +22,7 @@ oslo.config>=1.1.0 # For Swift storage backend. -python-swiftclient>=1.2,<2 +python-swiftclient>=1.2,<4 # Note you will need gcc buildtools installed and must # have installed libxml headers for lxml to be successfully diff --git a/openstack_requirements/tests/files/test-project.txt b/openstack_requirements/tests/files/test-project.txt index 415e6d38ba..8b01fa16ba 100644 --- a/openstack_requirements/tests/files/test-project.txt +++ b/openstack_requirements/tests/files/test-project.txt @@ -4,7 +4,7 @@ discover feedparser fixtures>=0.3.12 mox==0.5.3 -mox3==0.7.3 +mox3==0.21.0 MySQL-python psycopg2 pylint==0.25.2 diff --git a/openstack_requirements/tests/files/upper-constraints.txt b/openstack_requirements/tests/files/upper-constraints.txt new file mode 100644 index 0000000000..5d39413d6d --- /dev/null +++ b/openstack_requirements/tests/files/upper-constraints.txt @@ -0,0 +1,507 @@ +voluptuous===0.9.3 +chardet===2.3.0 +enum-compat===0.0.2 +rsa===3.4.2 +restructuredtext-lint===1.0.1 +netmiko===1.2.8 +PasteDeploy===1.5.2 +typing===3.5.3.0 +python-saharaclient===1.1.0 +Routes===2.4.1 +rtslib-fb===2.1.63 +smmap===0.9.0 +XStatic-Angular-Bootstrap===2.2.0.0 +WebOb===1.6.3 +pecan===1.2.1 +ryu===4.12 +os-api-ref===1.3.0 +oslo.concurrency===3.19.0 +websocket-client===0.40.0 +osprofiler===1.6.0 +bandit===1.4.0 +tabulate===0.7.7 +python-ironic-inspector-client===1.11.0 +lxml===3.7.3 +python-kingbirdclient===0.1.0 +pytest===3.0.6 +python-etcd===0.4.5 +cursive===0.1.1 +oslo.service===1.20.0 +django-appconf===1.0.2 +pykerberos===1.1.14 +certifi===2017.1.23 +requests-aws===0.1.8 +alabaster===0.7.10 +pbr===2.0.0 +microversion-parse===0.1.4 +Pint===0.7.2 +oslo.i18n===3.13.0 +jsonpath-rw-ext===1.1.1 +python-mistralclient===3.0.0 +oslo.context===2.13.0 +python-senlinclient===1.2.0 +rcssmin===1.0.6 +pycadf===2.5.0 +pysendfile===2.0.1 +fixtures===3.0.0 +neutron-lib===1.2.0 +pystache===0.5.4 +XStatic-Font-Awesome===4.7.0.0 +nose===1.3.7 +click-spinner===0.1.7 +nosehtmloutput===0.0.5 +waitress===1.0.2 +mistral===4.0.0 +os-refresh-config===6.0.0 +jsbeautifier===1.6.11;python_version=='3.4' +jsbeautifier===1.6.11;python_version=='3.5' +pysnmp===4.3.4 +Mako===1.0.6 +XStatic-angular-ui-router===0.3.1.2 +pyScss===1.3.4 +XStatic-jQuery===1.10.2.1 +jsonmodels===2.1.5 +ddt===1.1.1 +ipaddress===1.0.18 +python-freezerclient===1.2.0 +os-xenapi===0.1.1 +python-vitrageclient===1.1.1 +nosexcover===1.0.11 +krest===1.3.1 +psycopg2===2.7 +networkx===1.11 +bashate===0.5.1 +XStatic-Angular===1.5.8.0 +pyngus===2.2.0 +Pillow===4.0.0 +python-mimeparse===1.6.0 +tripleo-common===6.0.0 +Tempita===0.5.2 +ply===3.10 +simplejson===3.10.0 +suds-jurko===0.6 +python-swiftclient===3.3.0 +pyOpenSSL===16.2.0 +monasca-common===1.5.0 +hyperframe===4.0.2;python_version=='3.4' +hyperframe===4.0.2;python_version=='3.5' +cssutils===1.0.2;python_version=='3.4' +cssutils===1.0.2;python_version=='3.5' +scipy===0.19.0 +MySQL-python===1.2.5;python_version=='2.7' +XStatic-Jasmine===2.4.1.1 +python-glanceclient===2.6.0 +pyinotify===0.9.6 +debtcollector===1.12.0 +requests-unixsocket===0.1.5 +asn1crypto===0.21.1 +croniter===0.3.15 +python-watcherclient===1.0.0 +MarkupSafe===1.0 +pypowervm===1.0.0.4.1 +doc8===0.7.0 +pymongo===3.4.0 +sqlparse===0.2.3 +oslotest===2.14.0 +jsonpointer===1.10 +netaddr===0.7.19 +pyghmi===1.0.18 +sphinxcontrib-blockdiag===1.5.5 +kaitaistruct===0.6;python_version=='3.4' +kaitaistruct===0.6;python_version=='3.5' +thrift===0.10.0;python_version=='2.7' +gnocchiclient===3.1.1 +wcwidth===0.1.7 +jsonpath-rw===1.4.0 +prettytable===0.7.2 +vine===1.1.3 +taskflow===2.10.0 +traceback2===1.4.0 +semantic-version===2.6.0 +tablib===0.11.4 +astroid===1.3.8 +deprecation===1.0 +SQLAlchemy===1.0.17 +pyroute2===0.4.13 +kazoo===2.2.1 +XStatic-roboto-fontface===0.5.0.0 +pyudev===0.21.0 +eventlet===0.19.0 +openstack-doc-tools===1.5.0 +frozendict===1.2 +oslo.messaging===5.18.0 +extras===1.0.0 +PyJWT===1.4.2 +paramiko===2.1.2 +ordereddict===1.1 +reno===2.1.2 +unicodecsv===0.14.1;python_version=='2.7' +imagesize===0.7.1 +pathlib===1.0.1;python_version=='2.7' +urllib3===1.20 +graphviz===0.6 +PyKMIP===0.6.0 +python-subunit===1.2.0 +tornado===4.4.2;python_version=='3.4' +tornado===4.4.2;python_version=='3.5' +pycparser===2.17 +mock===2.0.0 +PyYAML===3.12 +beautifulsoup4===4.5.3 +ovs===2.7.0 +cryptography===1.8.1 +backports.ssl-match-hostname===3.5.0.1 +pylxd===2.2.3 +anyjson===0.3.3 +requests-mock===1.3.0 +os-apply-config===6.0.0 +oslosphinx===4.11.0 +mox3===0.21.0 +gunicorn===19.7.0 +unittest2===1.1.0 +django-compressor===2.1.1 +libvirt-python===3.1.0 +tzlocal===1.3 +python-novaclient===7.1.0 +bcrypt===3.1.3 +os-client-config===1.26.0 +XStatic-Angular-Gettext===2.3.8.0 +Pygments===2.2.0 +XStatic-Hogan===2.0.0.2 +XStatic-objectpath===1.2.1.0 +python-manilaclient===1.14.0 +requests===2.12.5 +snowballstemmer===1.2.1 +Jinja2===2.9.5 +XStatic-Bootstrap-SCSS===3.3.7.1 +pyzabbix===0.7.4 +ptyprocess===0.5.1 +amqp===2.1.3 +ruamel.yaml===0.13.14;python_version=='3.4' +ruamel.yaml===0.13.14;python_version=='3.5' +websockify===0.8.0 +html2text===2016.9.19;python_version=='3.4' +html2text===2016.9.19;python_version=='3.5' +XStatic-JQuery.quicksearch===2.0.3.1 +mpmath===0.19 +XStatic-JQuery-Migrate===1.2.1.1 +appdirs===1.4.3 +tinyrpc===0.5 +influxdb===4.0.0 +funcparserlib===0.3.6 +passlib===1.7.1 +dib-utils===0.0.11 +cliff===2.4.0 +os-brick===1.11.0 +trollius===2.1 +scp===0.10.2 +python-zaqarclient===1.4.0 +funcsigs===1.0.2;python_version=='2.7' +zhmcclient===0.10.0 +dnspython3===1.15.0;python_version=='3.4' +dnspython3===1.15.0;python_version=='3.5' +ldappool===2.0.0 +termcolor===1.1.0 +hpack===2.3.0;python_version=='3.4' +hpack===2.3.0;python_version=='3.5' +hiredis===0.2.0 +google-api-python-client===1.6.2 +castellan===0.5.0 +oslo.versionedobjects===1.22.0 +webcolors===1.7 +aodhclient===0.9.0 +autobahn===0.17.2 +SQLAlchemy-Utils===0.32.12 +coverage===4.3.4 +freezegun===0.3.8 +python-pytun===2.2.1 +pyperclip===1.5.27;python_version=='3.4' +pyperclip===1.5.27;python_version=='3.5' +cassandra-driver===3.8.0 +mox===0.5.3 +XStatic-Angular-Schema-Form===0.8.13.0 +gabbi===1.32.0 +XStatic-bootswatch===3.3.7.0 +XStatic-term.js===0.0.7.0 +oslo.log===3.21.0 +nodeenv===1.1.2 +pylev===1.3.0 +python-searchlightclient===1.1.0 +oslo.middleware===3.24.0 +brotlipy===0.6.0;python_version=='3.4' +brotlipy===0.6.0;python_version=='3.5' +XStatic-mdi===1.4.57.0 +django-pyscss===2.0.2 +uritemplate===3.0.0 +django-babel===0.5.1 +docutils===0.13.1 +notifier===1.0.3 +pycrypto===2.6.1 +ujson===1.35 +selenium===3.3.0 +mypy===0.501;python_version=='3.4' +mypy===0.501;python_version=='3.5' +dogtag-pki===10.3.5.1 +sphinxcontrib-seqdiag===0.8.5 +os-win===1.4.1 +retrying===1.3.3 +pathlib2===2.2.1 +pydotplus===2.0.2 +urwid===1.3.1;python_version=='3.4' +urwid===1.3.1;python_version=='3.5' +singledispatch===3.4.0.3;python_version=='2.7' +oslo.serialization===2.17.0 +warlock===1.2.0 +sphinxcontrib-httpdomain===1.5.0 +murano-pkg-check===0.3.0 +oslo.vmware===2.18.0 +sqlalchemy-migrate===0.11.0 +gitdb===0.6.4 +python-monascaclient===1.5.0 +ldap3===2.2.1 +automaton===1.8.0 +argh===0.26.2;python_version=='3.4' +argh===0.26.2;python_version=='3.5' +keyring===10.3 +testscenarios===0.5.0 +sphinxcontrib-pecanwsme===0.8.0 +enum34===1.1.6 +packaging===16.8 +nose-exclude===0.5.0 +psutil===5.2.0 +py===1.4.32 +txaio===2.6.1 +elasticsearch===2.4.1 +django-nose===1.4.4 +XStatic-JQuery.TableSorter===2.14.5.1 +pifpaf===0.25.1 +pysmi===0.0.7 +blockdiag===1.5.3 +testtools===2.2.0 +Parsley===1.3 +XStatic-tv4===1.2.7.0 +positional===1.1.1 +XStatic-JSEncrypt===2.3.1.1 +python-cinderclient===1.11.0 +keystonemiddleware===4.14.0 +django-formtools===2.0 +python-ceilometerclient===2.8.0 +XStatic-Spin===1.2.5.2 +SecretStorage===2.3.1 +XStatic-Rickshaw===1.5.0.0 +iso8601===0.1.11 +tooz===1.50.0 +linecache2===1.0.0 +oauth2client===3.0.0 +idna===2.5 +python-karborclient===0.1.3 +weakrefmethod===1.0.3;python_version=='2.7' +PuLP===1.6.5 +crc16===0.1.1 +os-dpm===1.0.0 +python-neutronclient===6.1.0 +pika===0.10.0 +oslo.cache===1.18.0 +WebTest===2.0.26 +openstack.nose-plugin===0.11 +os-collect-config===6.0.0 +python-qpid-proton===0.17.0 +pysaml2===4.0.2 +oslo.reports===1.18.0 +ceilometermiddleware===1.0.1 +python-nss===1.0.1 +testrepository===0.0.20 +sympy===1.0 +sphinxmark===0.1.17 +osc-lib===1.3.0 +python-consul===0.7.0 +seqdiag===0.9.5 +numpy===1.12.0 +repoze.who===2.3 +Sphinx===1.5.3 +oslo.config===3.23.0 +tempest===15.0.0 +django-floppyforms===1.7.0 +openstackdocstheme===1.6.1 +progressbar2===3.12.0 +zake===0.2.2 +python-solumclient===2.2.0 +PyMySQL===0.7.10 +kubernetes===1.0.0 +httplib2===0.10.3 +os-cloud-config===6.0.0 +bottle===0.12.13 +betamax===0.8.0 +construct===2.8.10 +pyparsing===2.2.0 +dogpile.cache===0.6.2 +python-barbicanclient===4.2.0 +blinker===1.4;python_version=='3.4' +blinker===1.4;python_version=='3.5' +WSME===0.9.2 +msgpack-python===0.4.8 +proboscis===1.2.6.0 +fortiosclient===0.0.2 +stevedore===1.21.0 +botocore===1.5.24 +xmltodict===0.10.2 +pyasn1===0.2.3 +python-utils===2.0.1 +oslo.rootwrap===5.5.0 +Django===1.8.17 +pexpect===4.2.1 +cmd2===0.7.0 +redis===2.10.5 +jmespath===0.9.2 +click===6.7 +docker-pycreds===0.2.1 +XStatic-smart-table===1.4.13.2 +kuryr-lib===0.3.0 +scrypt===0.8.0 +jsonpatch===1.15 +typed-ast===1.0.2;python_version=='3.4' +typed-ast===1.0.2;python_version=='3.5' +os-testr===0.8.0 +stomp.py===4.1.17 +xattr===0.9.1 +python-memcached===1.58 +openstacksdk===0.9.13 +six===1.10.0 +h2===2.6.0;python_version=='3.4' +h2===2.6.0;python_version=='3.5' +dulwich===0.17.1 +pykafka===2.5.0 +kombu===4.0.1 +mitmproxy===2.0.0;python_version=='3.4' +mitmproxy===2.0.0;python_version=='3.5' +yaql===1.1.3 +requestsexceptions===1.2.0 +testresources===2.0.1 +falcon===1.1.0 +pycryptodome===3.4.5 +pyldap===2.4.28 +Flask-RESTful===0.3.5 +GitPython===2.1.3 +python-ironicclient===1.11.1 +XStatic===1.0.1 +click-repl===0.1.1 +XStatic-Angular-FileUpload===12.0.4.0 +python-openstackclient===3.9.0 +pika-pool===0.1.3 +pyzmq===16.0.2 +EditorConfig===0.12.1;python_version=='3.4' +EditorConfig===0.12.1;python_version=='3.5' +oslo.db===4.18.0 +simplegeneric===0.8.1 +abclient===0.2.3 +pymemcache===1.4.2 +wrapt===1.10.8 +oslo.privsep===1.17.0 +zope.interface===4.3.3 +oslo.policy===1.19.0 +python-muranoclient===0.12.0 +pyeclib===1.4.0 +django-openstack-auth===3.1.1 +wsgi-intercept===1.5.0 +ndg-httpsclient===0.4.2;python_version=='2.7' +tempest-lib===1.0.0 +spec-cleaner===0.9.2 +repoze.lru===0.6 +rfc3986===0.4.1 +tenacity===4.0.0 +logilab-common===1.3.0 +XStatic-Magic-Search===0.2.5.1 +python-designateclient===2.6.0 +Paste===2.0.3 +pycodestyle===2.3.1 +boto===2.46.1 +functools32===3.2.3.post2;python_version=='2.7' +watchdog===0.8.3;python_version=='3.4' +watchdog===0.8.3;python_version=='3.5' +gevent===1.2.1 +os-vif===1.4.0 +Werkzeug===0.12 +pyasn1-modules===0.0.8 +APScheduler===3.3.1 +monotonic===1.2 +python-smaugclient===0.0.8 +python-troveclient===2.8.0 +cliff-tablib===2.0 +XStatic-Bootstrap-Datepicker===1.3.1.0 +CouchDB===1.1 +netifaces===0.10.5 +cachetools===2.0.0 +ws4py===0.3.4 +backports-abc===0.5;python_version=='3.4' +backports-abc===0.5;python_version=='3.5' +keystoneauth1===2.18.0 +statsd===3.2.1 +XenAPI===1.2 +python-keystoneclient===3.10.0 +demjson===2.2.4 +diskimage-builder===1.28.0 +heat-translator===0.7.0 +python-magnumclient===2.5.0 +docker===2.1.0 +prompt-toolkit===1.0.13 +pathtools===0.1.2;python_version=='3.4' +pathtools===0.1.2;python_version=='3.5' +qpid-python===0.32.1;python_version=='2.7' +contextlib2===0.5.4 +XStatic-Angular-lrdragndrop===1.0.2.2 +python-congressclient===1.6.0 +aniso8601===1.2.0 +rjsmin===1.0.12 +icalendar===3.11.3 +decorator===4.0.11 +cffi===1.9.1 +futurist===0.22.0 +jsonschema===2.6.0 +alembic===0.9.1 +glance-store===0.20.0 +sphinx-testing===0.7.1 +dnspython===1.15.0 +oauthlib===2.0.1 +Babel===2.3.4 +logutils===0.3.4.1 +scandir===1.5 +sphinxcontrib-fulltoc===1.1 +smmap2===2.0.1 +olefile===0.44 +greenlet===0.4.12 +xvfbwrapper===0.2.9 +futures===3.0.5 +tosca-parser===0.7.0 +Flask===0.12 +pymod2pkg===0.7.2 +happybase===0.9;python_version=='2.7' +marathon===0.8.11 +docker-py===1.10.6 +fasteners===0.14.1 +sortedcontainers===1.5.7;python_version=='3.4' +sortedcontainers===1.5.7;python_version=='3.5' +python-tackerclient===0.9.0 +python-heatclient===1.8.0 +kafka-python===1.3.2 +oslo.utils===3.23.0 +python-editor===1.0.3 +gitdb2===2.0.0 +requests-kerberos===0.11.0 +itsdangerous===0.24 +XStatic-jquery-ui===1.12.0.1 +monasca-statsd===1.5.0 +python-dateutil===2.6.0 +virtualenv===15.1.0 +colorama===0.3.7 +ironic-lib===2.6.0 +pytz===2016.10 +XStatic-D3===3.5.17.0 +sysv-ipc===0.7.0 +scikit-learn===0.18.1 +wsgiref==0.1.2 +discover==0.4.0 +oslo.sphinx==0.9.27 +python-ldap==2.3.13 +argparse==1.4.0 +setuptools-git==25.1.4 +feedparser==5.2.1 diff --git a/openstack_requirements/tests/test_check_constraints.py b/openstack_requirements/tests/test_check_constraints.py new file mode 100644 index 0000000000..1048fcf64a --- /dev/null +++ b/openstack_requirements/tests/test_check_constraints.py @@ -0,0 +1,196 @@ +# 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. +from __future__ import print_function + +import io +import mock +import os +import testscenarios +import testtools + +from openstack_requirements.cmds import check_exists +from openstack_requirements import project +from openstack_requirements.tests import common + +load_tests = testscenarios.load_tests_apply_scenarios + + +def mock_read_requirements_file(filename): + if os.path.basename(filename) == 'upper-constraints.txt': + return common.upper_constraints + elif os.path.basename(filename) == 'global-requirements.txt': + return common.global_reqs + elif os.path.basename(filename) == 'blacklist.txt': + return common.blacklist + else: + raise IOError('No such file or directory: %s' % filename) + + +class CheckExistsTest(testtools.TestCase): + + def setUp(self): + super(CheckExistsTest, self).setUp() + + @mock.patch( + 'openstack_requirements.cmds.check_exists.read_requirements_file', + mock_read_requirements_file) + @mock.patch('openstack_requirements.project.read', + return_value=common.project_project) + def test_good_project(self, mock_project_read): + ret = check_exists.main([common.project_fixture.root]) + self.assertEqual(ret, 0) + + @mock.patch( + 'openstack_requirements.cmds.check_exists.read_requirements_file', + mock_read_requirements_file) + def test_project_missing_from_uc(self): + self.useFixture(common.project_fixture) + orig_mocked_read_req = check_exists.read_requirements_file + read_req_path = ('openstack_requirements.cmds.check_exists.' + 'read_requirements_file') + + def remove_req_read_reqs_file(filename): + if filename == 'upper-constraints.txt': + upper_cons = common.upper_constraints.copy() + upper_cons.pop('six') + return upper_cons + + return orig_mocked_read_req(filename) + + expected_out = ('six from requirements.txt not found in' + ' upper-constraints') + + # Start capturing some output + mock_stdout = io.StringIO() + with mock.patch('openstack_requirements.project.read', + return_value=common.project_project), \ + mock.patch('sys.stdout', mock_stdout), \ + mock.patch(read_req_path, remove_req_read_reqs_file): + ret = check_exists.main([common.project_fixture.root]) + self.assertEqual(ret, 1) + self.assertIn(expected_out, mock_stdout.getvalue()) + + @mock.patch( + 'openstack_requirements.cmds.check_exists.read_requirements_file', + mock_read_requirements_file) + def test_project_missing_from_gr(self): + self.useFixture(common.project_fixture) + + # Add some random package that wont exist in G-R + with open(common.project_fixture.req_file, 'a') as req_file: + req_file.write(u'SomeRandomModule #Some random module\n') + req_file.flush() + + expected_out = ('somerandommodule from requirements.txt not found in' + ' global-requirements') + + # Start capturing some output + mock_stdout = io.StringIO() + proj_read = project.read(common.project_fixture.root) + with mock.patch('openstack_requirements.project.read', + return_value=proj_read), \ + mock.patch('sys.stdout', mock_stdout): + ret = check_exists.main([common.project_fixture.root]) + self.assertEqual(ret, 1) + self.assertIn(expected_out, mock_stdout.getvalue()) + + @mock.patch( + 'openstack_requirements.cmds.check_exists.read_requirements_file', + mock_read_requirements_file) + def test_project_multiple_missing_from_uc_and_gr(self): + self.useFixture(common.project_fixture) + orig_mocked_read_req = check_exists.read_requirements_file + read_req_path = ('openstack_requirements.cmds.check_exists.' + 'read_requirements_file') + + def remove_req_read_reqs_file(filename): + if filename == 'upper-constraints.txt': + upper_cons = common.upper_constraints.copy() + upper_cons.pop('lxml') + return upper_cons + + return orig_mocked_read_req(filename) + + new_reqs = '>1.10.0\nsomerandommodule\n' + + # lets change the six requirement not include the u-c version + proj_read = project.read(common.project_fixture.root) + proj_read['requirements']['requirements.txt'] = \ + proj_read['requirements']['requirements.txt'][:-1] + new_reqs + proj_read['requirements']['test-requirements.txt'] = \ + proj_read['requirements']['test-requirements.txt'] + \ + 'anotherrandommodule\n' + + expected_outs = [ + 'lxml from requirements.txt not found in upper-constraints', + 'somerandommodule from requirements.txt not found in ' + 'global-requirements', + 'anotherrandommodule from test-requirements.txt not found in ' + 'global-requirements', + 'six must be <= 1.10.0 from upper-constraints and include the ' + 'upper-constraints version'] + + # Start capturing some output + mock_stdout = io.StringIO() + with mock.patch('openstack_requirements.project.read', + return_value=proj_read), \ + mock.patch('sys.stdout', mock_stdout), \ + mock.patch(read_req_path, remove_req_read_reqs_file): + ret = check_exists.main([common.project_fixture.root]) + self.assertEqual(ret, 1) + for expected in expected_outs: + self.assertIn(expected, mock_stdout.getvalue()) + + @mock.patch( + 'openstack_requirements.cmds.check_exists.read_requirements_file', + mock_read_requirements_file) + def test_project_req_bigger_then_uc(self): + self.useFixture(common.project_fixture) + + # lets change the six requirement not include the u-c version + proj_read = project.read(common.project_fixture.root) + proj_read['requirements']['requirements.txt'] = \ + proj_read['requirements']['requirements.txt'][:-1] + '>1.10.0\n' + expected_out = ('six must be <= 1.10.0 from upper-constraints and ' + 'include the upper-constraints version') + + # Start capturing some output + mock_stdout = io.StringIO() + with mock.patch('openstack_requirements.project.read', + return_value=proj_read), \ + mock.patch('sys.stdout', mock_stdout): + ret = check_exists.main([common.project_fixture.root]) + self.assertEqual(ret, 1) + self.assertIn(expected_out, mock_stdout.getvalue()) + + @mock.patch( + 'openstack_requirements.cmds.check_exists.read_requirements_file', + mock_read_requirements_file) + def test_project_req_not_include_uc_version(self): + self.useFixture(common.project_fixture) + + # lets change the six requirement not include the u-c version + proj_read = project.read(common.project_fixture.root) + proj_read['requirements']['requirements.txt'] = \ + proj_read['requirements']['requirements.txt'][:-1] + \ + '<1.10.0,>1.10.0\n' + expected_out = ('six must be <= 1.10.0 from upper-constraints and ' + 'include the upper-constraints version') + + # Start capturing some output + mock_stdout = io.StringIO() + with mock.patch('openstack_requirements.project.read', + return_value=proj_read), \ + mock.patch('sys.stdout', mock_stdout): + ret = check_exists.main([common.project_fixture.root]) + self.assertEqual(ret, 1) + self.assertIn(expected_out, mock_stdout.getvalue()) diff --git a/openstack_requirements/tests/test_update.py b/openstack_requirements/tests/test_update.py index cd5d8d3b20..3fb00be833 100644 --- a/openstack_requirements/tests/test_update.py +++ b/openstack_requirements/tests/test_update.py @@ -52,7 +52,7 @@ class SmokeTest(testtools.TestCase): expected = ('Version change for: greenlet, SQLAlchemy, eventlet, PasteDeploy, routes, WebOb, wsgiref, boto, kombu, pycrypto, 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,<=0.7.99 -> SQLAlchemy<=0.7.99,>=0.7 + 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 @@ -61,14 +61,14 @@ class SmokeTest(testtools.TestCase): boto -> boto>=2.4.0 kombu>2.4.7 -> kombu>=2.4.8 pycrypto>=2.1.0alpha1 -> pycrypto>=2.6 - python-swiftclient>=1.2,<2 -> python-swiftclient>=1.2 + 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.7.3 -> mox3>=0.7.0 + 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) @@ -157,7 +157,7 @@ class UpdateTest(testtools.TestCase): expected = ('Version change for: greenlet, SQLAlchemy, eventlet, PasteDeploy, routes, WebOb, wsgiref, boto, kombu, pycrypto, 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,<=0.7.99 -> SQLAlchemy<=0.7.99,>=0.7 + 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 @@ -166,14 +166,14 @@ class UpdateTest(testtools.TestCase): boto -> boto>=2.4.0 kombu>2.4.7 -> kombu>=2.4.8 pycrypto>=2.1.0alpha1 -> pycrypto>=2.6 - python-swiftclient>=1.2,<2 -> python-swiftclient>=1.2 + 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.7.3 -> mox3>=0.7.0 + 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']) @@ -190,7 +190,7 @@ Updated %(project)s/test-requirements.txt: Version change for: greenlet, SQLAlchemy, eventlet, PasteDeploy, routes, WebOb, wsgiref, boto, kombu, pycrypto, 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,<=0.7.99 -> SQLAlchemy<=0.7.99,>=0.7 + 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 @@ -199,7 +199,7 @@ Version change for: greenlet, SQLAlchemy, eventlet, PasteDeploy, routes, WebOb, boto -> boto>=2.4.0 kombu>2.4.7 -> kombu>=2.4.8 pycrypto>=2.1.0alpha1 -> pycrypto>=2.6 - python-swiftclient>=1.2,<2 -> python-swiftclient>=1.2 + 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 @@ -207,7 +207,7 @@ 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.7.3 -> mox3>=0.7.0 + 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 diff --git a/setup.cfg b/setup.cfg index 7a0925546d..0d6d45da80 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,4 +39,5 @@ console_scripts = validate-constraints = openstack_requirements.cmds.validate:main validate-projects = openstack_requirements.cmds.validate_projects:main normalize-requirements = openstack_requirements.cmds.normalize_requirements:main - check-python2-support = openstack_requirements.cmds.check_py2:main \ No newline at end of file + check-python2-support = openstack_requirements.cmds.check_py2:main + check-constraints = openstack_requirements.cmds.check_exists:main