diff options
Diffstat (limited to '')
-rw-r--r-- | src/arrow/dev/test_merge_arrow_pr.py | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/src/arrow/dev/test_merge_arrow_pr.py b/src/arrow/dev/test_merge_arrow_pr.py new file mode 100644 index 000000000..8fe188350 --- /dev/null +++ b/src/arrow/dev/test_merge_arrow_pr.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 collections import namedtuple + +import pytest + +import merge_arrow_pr + + +FakeIssue = namedtuple('issue', ['fields']) +FakeFields = namedtuple('fields', ['status', 'summary', 'assignee', + 'components', 'fixVersions']) +FakeAssignee = namedtuple('assignee', ['displayName']) +FakeStatus = namedtuple('status', ['name']) +FakeComponent = namedtuple('component', ['name']) +FakeVersion = namedtuple('version', ['name', 'raw']) + +RAW_VERSION_JSON = [ + {'name': 'JS-0.4.0', 'released': False}, + {'name': '0.11.0', 'released': False}, + {'name': '0.12.0', 'released': False}, + {'name': '0.10.0', 'released': True}, + {'name': '0.9.0', 'released': True} +] + + +SOURCE_VERSIONS = [FakeVersion(raw['name'], raw) + for raw in RAW_VERSION_JSON] + +TRANSITIONS = [{'name': 'Resolve Issue', 'id': 1}] + +jira_id = 'ARROW-1234' +status = FakeStatus('In Progress') +fields = FakeFields(status, 'issue summary', FakeAssignee('groundhog'), + [FakeComponent('C++'), FakeComponent('Format')], + []) +FAKE_ISSUE_1 = FakeIssue(fields) + + +class FakeJIRA: + + def __init__(self, issue=None, project_versions=None, transitions=None, + current_fix_versions=None): + self._issue = issue + self._project_versions = project_versions + self._transitions = transitions + + def issue(self, jira_id): + return self._issue + + def transitions(self, jira_id): + return self._transitions + + def transition_issue(self, jira_id, transition_id, comment=None, + fixVersions=None): + self.captured_transition = { + 'jira_id': jira_id, + 'transition_id': transition_id, + 'comment': comment, + 'fixVersions': fixVersions + } + + def get_candidate_fix_versions(self): + return SOURCE_VERSIONS, ['0.12.0'] + + def project_versions(self, project): + return self._project_versions + + +class FakeCLI: + + def __init__(self, responses=()): + self.responses = responses + self.position = 0 + + def prompt(self, prompt): + response = self.responses[self.position] + self.position += 1 + return response + + def fail(self, msg): + raise Exception(msg) + + +def test_jira_fix_versions(): + jira = FakeJIRA(project_versions=SOURCE_VERSIONS, + transitions=TRANSITIONS) + + issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + all_versions, default_versions = issue.get_candidate_fix_versions() + assert all_versions == SOURCE_VERSIONS + assert default_versions == ['0.11.0'] + + +def test_jira_no_suggest_patch_release(): + versions_json = [ + {'name': '0.11.1', 'released': False}, + {'name': '0.12.0', 'released': False}, + ] + + versions = [FakeVersion(raw['name'], raw) for raw in versions_json] + + jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS) + issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + all_versions, default_versions = issue.get_candidate_fix_versions() + assert all_versions == versions + assert default_versions == ['0.12.0'] + + +def test_jira_parquet_no_suggest_non_cpp(): + # ARROW-7351 + versions_json = [ + {'name': 'cpp-1.5.0', 'released': True}, + {'name': 'cpp-1.6.0', 'released': False}, + {'name': 'cpp-1.7.0', 'released': False}, + {'name': '1.11.0', 'released': False}, + {'name': '1.12.0', 'released': False} + ] + + versions = [FakeVersion(raw['name'], raw) + for raw in versions_json] + + jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS) + issue = merge_arrow_pr.JiraIssue(jira, 'PARQUET-1713', 'PARQUET', + FakeCLI()) + all_versions, default_versions = issue.get_candidate_fix_versions() + assert all_versions == versions + assert default_versions == ['cpp-1.6.0'] + + +def test_jira_invalid_issue(): + class Mock: + + def issue(self, jira_id): + raise Exception("not found") + + with pytest.raises(Exception): + merge_arrow_pr.JiraIssue(Mock(), 'ARROW-1234', 'ARROW', FakeCLI()) + + +def test_jira_resolve(): + jira = FakeJIRA(issue=FAKE_ISSUE_1, + project_versions=SOURCE_VERSIONS, + transitions=TRANSITIONS) + + my_comment = 'my comment' + fix_versions = [SOURCE_VERSIONS[1].raw] + + issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + issue.resolve(fix_versions, my_comment) + + assert jira.captured_transition == { + 'jira_id': 'ARROW-1234', + 'transition_id': 1, + 'comment': my_comment, + 'fixVersions': fix_versions + } + + +def test_jira_resolve_non_mainline(): + jira = FakeJIRA(issue=FAKE_ISSUE_1, + project_versions=SOURCE_VERSIONS, + transitions=TRANSITIONS) + + my_comment = 'my comment' + fix_versions = [SOURCE_VERSIONS[0].raw] + + issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + issue.resolve(fix_versions, my_comment) + + assert jira.captured_transition == { + 'jira_id': 'ARROW-1234', + 'transition_id': 1, + 'comment': my_comment, + 'fixVersions': fix_versions + } + + +def test_jira_resolve_released_fix_version(): + # ARROW-5083 + jira = FakeJIRA(issue=FAKE_ISSUE_1, + project_versions=SOURCE_VERSIONS, + transitions=TRANSITIONS) + + cmd = FakeCLI(responses=['0.9.0']) + fix_versions_json = merge_arrow_pr.prompt_for_fix_version(cmd, jira) + assert fix_versions_json == [RAW_VERSION_JSON[-1]] + + +def test_multiple_authors_bad_input(): + a0 = 'Jimbob Crawfish <jimbob.crawfish@gmail.com>' + a1 = 'Jarvis McCratchett <jarvis.mccratchett@hotmail.com>' + a2 = 'Hank Miller <hank.miller@protonmail.com>' + distinct_authors = [a0, a1] + + cmd = FakeCLI(responses=['']) + primary_author, new_distinct_authors = merge_arrow_pr.get_primary_author( + cmd, distinct_authors) + assert primary_author == a0 + assert new_distinct_authors == [a0, a1] + + cmd = FakeCLI(responses=['oops', a1]) + primary_author, new_distinct_authors = merge_arrow_pr.get_primary_author( + cmd, distinct_authors) + assert primary_author == a1 + assert new_distinct_authors == [a1, a0] + + cmd = FakeCLI(responses=[a2]) + primary_author, new_distinct_authors = merge_arrow_pr.get_primary_author( + cmd, distinct_authors) + assert primary_author == a2 + assert new_distinct_authors == [a2, a0, a1] + + +def test_jira_already_resolved(): + status = FakeStatus('Resolved') + fields = FakeFields(status, 'issue summary', FakeAssignee('groundhog'), + [FakeComponent('Java')], []) + issue = FakeIssue(fields) + + jira = FakeJIRA(issue=issue, + project_versions=SOURCE_VERSIONS, + transitions=TRANSITIONS) + + fix_versions = [SOURCE_VERSIONS[0].raw] + issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + + with pytest.raises(Exception, + match="ARROW-1234 already has status 'Resolved'"): + issue.resolve(fix_versions, "") + + +def test_no_unset_point_release_fix_version(): + # ARROW-6915: We have had the problem of issues marked with a point release + # having their fix versions overwritten by the merge tool. This verifies + # that existing patch release versions are carried over + status = FakeStatus('In Progress') + + versions_json = { + '0.14.2': {'name': '0.14.2', 'id': 1}, + '0.15.1': {'name': '0.15.1', 'id': 2}, + '0.16.0': {'name': '0.16.0', 'id': 3}, + '0.17.0': {'name': '0.17.0', 'id': 4} + } + + fields = FakeFields(status, 'summary', FakeAssignee('someone'), + [FakeComponent('Java')], + [FakeVersion(v, versions_json[v]) + for v in ['0.17.0', '0.15.1', '0.14.2']]) + issue = FakeIssue(fields) + + jira = FakeJIRA(issue=issue, project_versions=SOURCE_VERSIONS, + transitions=TRANSITIONS) + + issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + issue.resolve([versions_json['0.16.0']], "a comment") + + assert jira.captured_transition == { + 'jira_id': 'ARROW-1234', + 'transition_id': 1, + 'comment': 'a comment', + 'fixVersions': [versions_json[v] + for v in ['0.16.0', '0.15.1', '0.14.2']] + } + + issue.resolve([versions_json['0.15.1']], "a comment") + + assert jira.captured_transition == { + 'jira_id': 'ARROW-1234', + 'transition_id': 1, + 'comment': 'a comment', + 'fixVersions': [versions_json[v] for v in ['0.15.1', '0.14.2']] + } + + +def test_jira_output_no_components(): + # ARROW-5472 + status = 'Interesting work' + components = [] + output = merge_arrow_pr.format_jira_output( + 'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'), + components) + + assert output == """=== JIRA ARROW-1234 === +Summary\t\tInteresting work +Assignee\tFoo Bar +Components\tNO COMPONENTS!!! +Status\t\tResolved +URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234""" + + output = merge_arrow_pr.format_jira_output( + 'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'), + [FakeComponent('C++'), FakeComponent('Python')]) + + assert output == """=== JIRA ARROW-1234 === +Summary\t\tInteresting work +Assignee\tFoo Bar +Components\tC++, Python +Status\t\tResolved +URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234""" |