diff --git a/openstack_requirements/check.py b/openstack_requirements/check.py index 4fdc216a4f..9d32a675fc 100644 --- a/openstack_requirements/check.py +++ b/openstack_requirements/check.py @@ -101,6 +101,48 @@ def get_global_reqs(content): return global_reqs +def _validate_one(name, reqs, branch_reqs, blacklist, global_reqs): + "Returns True if there is a failure." + print(name, reqs, branch_reqs, blacklist, global_reqs) + if (name in branch_reqs.reqs and + reqs == branch_reqs.reqs[name]): + # Unchanged [or a change that preserves a current value] + return False + if name in blacklist: + # Blacklisted items are not synced and are managed + # by project teams as they see fit, so no further + # testing is needed. + return False + if name not in global_reqs: + print("Requirement %s not in openstack/requirements" % + str(reqs)) + return True + if reqs == global_reqs[name]: + return False + counts = {} + for req in reqs: + if req.extras: + for extra in req.extras: + counts[extra] = counts.get(extra, 0) + 1 + else: + counts[''] = counts.get('', 0) + 1 + if not _is_requirement_in_global_reqs( + req, global_reqs[name]): + print("Requirement for package %s : %s does " + "not match openstack/requirements value : %s" % ( + name, str(req), str(global_reqs[name]))) + return True + for extra, count in counts.items(): + if count != len(global_reqs[name]): + print("Package %s%s requirement does not match " + "number of lines (%d) in " + "openstack/requirements" % ( + name, + ('[%s]' % extra) if extra else '', + len(global_reqs[name]))) + return True + + def validate(head_reqs, branch_reqs, blacklist, global_reqs): failed = False # iterate through the changing entries and see if they match the global @@ -108,42 +150,15 @@ def validate(head_reqs, branch_reqs, blacklist, global_reqs): for fname, freqs in head_reqs.reqs_by_file.items(): print("Validating %(fname)s" % {'fname': fname}) for name, reqs in freqs.items(): - counts = {} - if (name in branch_reqs.reqs and - reqs == branch_reqs.reqs[name]): - # Unchanged [or a change that preserves a current value] - continue - if name in blacklist: - # Blacklisted items are not synced and are managed - # by project teams as they see fit, so no further - # testing is needed. - continue - if name not in global_reqs: - failed = True - print("Requirement %s not in openstack/requirements" % - str(reqs)) - continue - if reqs == global_reqs[name]: - continue - for req in reqs: - if req.extras: - for extra in req.extras: - counts[extra] = counts.get(extra, 0) + 1 - else: - counts[''] = counts.get('', 0) + 1 - if not _is_requirement_in_global_reqs( - req, global_reqs[name]): - failed = True - print("Requirement for package %s : %s does " - "not match openstack/requirements value : %s" % ( - name, str(req), str(global_reqs[name]))) - for extra, count in counts.items(): - if count != len(global_reqs[name]): - failed = True - print("Package %s%s requirement does not match " - "number of lines (%d) in " - "openstack/requirements" % ( - name, - ('[%s]' % extra) if extra else '', - len(global_reqs[name]))) + failed = ( + _validate_one( + name, + reqs, + branch_reqs, + blacklist, + global_reqs, + ) + or failed + ) + return failed diff --git a/openstack_requirements/tests/test_check.py b/openstack_requirements/tests/test_check.py index 95cf1d07ee..fe912f5796 100644 --- a/openstack_requirements/tests/test_check.py +++ b/openstack_requirements/tests/test_check.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import textwrap + from openstack_requirements import check from openstack_requirements import requirement @@ -26,7 +28,11 @@ class TestIsReqInGlobalReqs(testtools.TestCase): self.stdout = self.useFixture(self._stdout_fixture).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.stdout)) - self.global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.global_reqs = check.get_global_reqs(textwrap.dedent(""" + name>=1.2,!=1.4 + withmarker>=1.5;python_version=='3.5' + withmarker>=1.2,!=1.4;python_version=='2.7' + """)) print('global_reqs', self.global_reqs) def test_match(self): @@ -38,6 +44,17 @@ class TestIsReqInGlobalReqs(testtools.TestCase): ) ) + def test_match_with_markers(self): + req = requirement.parse(textwrap.dedent(""" + withmarker>=1.5;python_version=='3.5' + """))['withmarker'][0][0] + self.assertTrue( + check._is_requirement_in_global_reqs( + req, + self.global_reqs['withmarker'], + ) + ) + def test_name_mismatch(self): req = requirement.parse('wrongname>=1.2,!=1.4')['wrongname'][0][0] self.assertFalse( @@ -73,3 +90,239 @@ class TestIsReqInGlobalReqs(testtools.TestCase): self.global_reqs['name'], ) ) + + +class TestValidateOne(testtools.TestCase): + + def setUp(self): + super(TestValidateOne, self).setUp() + self._stdout_fixture = fixtures.StringStream('stdout') + self.stdout = self.useFixture(self._stdout_fixture).stream + self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.stdout)) + + def test_unchanged(self): + # If the line matches the value in the branch list everything + # is OK. + reqs = [ + r + for r, line in requirement.parse('name>=1.2,!=1.4')['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': 'name>=1.2,!=1.4'}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse(''), + global_reqs=global_reqs, + ) + ) + + def test_blacklisted(self): + # If the package is blacklisted, everything is OK. + reqs = [ + r + for r, line in requirement.parse('name>=1.2,!=1.4')['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': 'name>=1.2,!=1.4'}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse('name'), + global_reqs=global_reqs, + ) + ) + + def test_blacklisted_mismatch(self): + # If the package is blacklisted, it doesn't matter if the + # version matches. + reqs = [ + r + for r, line in requirement.parse('name>=1.5')['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': 'name>=1.2,!=1.4'}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse('name'), + global_reqs=global_reqs, + ) + ) + + def test_not_in_global_list(self): + # If the package is not in the global list, that is an error. + reqs = [ + r + for r, line in requirement.parse('name>=1.2,!=1.4')['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': 'name>=1.2,!=1.4'}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs('') + self.assertTrue( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse(''), + global_reqs=global_reqs, + ) + ) + + def test_new_item_matches_global_list(self): + # If the new item matches the global list exactly that is OK. + reqs = [ + r + for r, line in requirement.parse('name>=1.2,!=1.4')['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': ''}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse(''), + global_reqs=global_reqs, + ) + ) + + def test_new_item_mismatches_global_list(self): + # If the new item does not match the global value, that is an + # error. + reqs = [ + r + for r, line in requirement.parse('name>=1.2,!=1.4,!=1.5')['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': ''}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.assertTrue( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse(''), + global_reqs=global_reqs, + ) + ) + + def test_new_item_matches_global_list_with_extra(self): + # If the global list has multiple entries for an item with + # different "extra" specifiers, the values must all be in the + # requirements file. + r_content = textwrap.dedent(""" + name>=1.5;python_version=='3.5' + name>=1.2,!=1.4;python_version=='2.6' + """) + reqs = [ + r + for r, line in requirement.parse(r_content)['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': ''}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs(textwrap.dedent(""" + name>=1.5;python_version=='3.5' + name>=1.2,!=1.4;python_version=='2.6' + """)) + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse(''), + global_reqs=global_reqs, + ) + ) + + def test_new_item_missing_extra_line(self): + # If the global list has multiple entries for an item with + # different "extra" specifiers, the values must all be in the + # requirements file. + r_content = textwrap.dedent(""" + name>=1.2,!=1.4;python_version=='2.6' + """) + reqs = [ + r + for r, line in requirement.parse(r_content)['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': ''}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs(textwrap.dedent(""" + name>=1.5;python_version=='3.5' + name>=1.2,!=1.4;python_version=='2.6' + """)) + self.assertTrue( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse(''), + global_reqs=global_reqs, + ) + ) + + def test_new_item_mismatches_global_list_with_extra(self): + # If the global list has multiple entries for an item with + # different "extra" specifiers, the values must all be in the + # requirements file. + r_content = textwrap.dedent(""" + name>=1.5;python_version=='3.6' + name>=1.2,!=1.4;python_version=='2.6' + """) + reqs = [ + r + for r, line in requirement.parse(r_content)['name'] + ] + branch_reqs = check.RequirementsList( + 'testproj', + {'requirements': {'requirements.txt': ''}}, + ) + branch_reqs.process(False) + global_reqs = check.get_global_reqs(textwrap.dedent(""" + name>=1.5;python_version=='3.5' + name>=1.2,!=1.4;python_version=='2.6' + """)) + self.assertTrue( + check._validate_one( + 'name', + reqs=reqs, + branch_reqs=branch_reqs, + blacklist=requirement.parse(''), + global_reqs=global_reqs, + ) + )