testing: fixup test-autoland-try and test-import-pullrequest (bug 1180818) r=mdoglio
authorDan Minor <dminor@mozilla.com>
Thu, 30 Jul 2015 22:36:32 -0400
changeset 2857 750e446144349a502196d80e2a759756b16217d2
parent 2856 0482431406ba118b8c2c5ffc2022018b8c0a864b
child 2858 b208d030ecdaddbb215e0c9973378ac1ef7948c9
push id1021
push userdminor@mozilla.com
push dateThu, 06 Aug 2015 13:10:25 +0000
treeherderversion-control-tools@750e44614434 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmdoglio
bugs1180818
testing: fixup test-autoland-try and test-import-pullrequest (bug 1180818) r=mdoglio This modifies the autoland tests to work with successful autoland requests. Previously due to deficiencies in our testing infrastructure, the requests would always fail.
pylib/mozreview/mozreview/autoland/resources.py
pylib/mozreview/mozreview/tests/test-autoland-try.py
pylib/mozreview/mozreview/tests/test-import-pullrequest.py
--- a/pylib/mozreview/mozreview/autoland/resources.py
+++ b/pylib/mozreview/mozreview/autoland/resources.py
@@ -11,16 +11,17 @@ from djblets.webapi.decorators import (w
 from djblets.webapi.errors import (DOES_NOT_EXIST,
                                    INVALID_FORM_DATA,
                                    NOT_LOGGED_IN,
                                    PERMISSION_DENIED)
 import requests
 from reviewboard.changedescs.models import ChangeDescription
 from reviewboard.extensions.base import get_extension_manager
 from reviewboard.reviews.models import ReviewRequest
+from reviewboard.scmtools.models import Repository
 from reviewboard.site.urlresolvers import local_site_reverse
 from reviewboard.webapi.resources import WebAPIResource
 
 from mozreview.autoland.models import (AutolandEventLogEntry,
                                        AutolandRequest,
                                        ImportPullRequestRequest)
 from mozreview.autoland.errors import (AUTOLAND_ERROR,
                                        AUTOLAND_TIMEOUT,
@@ -98,28 +99,19 @@ class TryAutolandTriggerResource(WebAPIR
                                'build',
             },
             'try_syntax': {
                 'type': six.text_type,
                 'description': 'The TryChooser syntax for the builds that '
                                'will be kicked off',
             },
         },
-        optional={
-            'autoland_request_id_for_testing': {
-                'type': int,
-                'description': 'For testing only. If the MozReview extension '
-                               'is in testing mode, this skips the request to '
-                               'Try Autoland and just uses this request id.'
-            }
-        }
     )
     @transaction.atomic
-    def create(self, request, review_request_id, try_syntax,
-               autoland_request_id_for_testing=None, *args, **kwargs):
+    def create(self, request, review_request_id, try_syntax, *args, **kwargs):
         try:
             rr = ReviewRequest.objects.get(pk=review_request_id)
         except ReviewRequest.DoesNotExist:
             return DOES_NOT_EXIST
 
         if not is_pushed(rr) or not is_parent(rr):
             logging.error('Failed triggering Autoland because the review '
                           'request is not pushed, or not the parent review '
@@ -131,78 +123,80 @@ class TryAutolandTriggerResource(WebAPIR
 
         last_revision = json.loads(rr.extra_data.get('p2rb.commits'))[-1][0]
 
         ext = get_extension_manager().get_enabled_extension(
             'mozreview.extension.MozReviewExtension')
 
         testing = ext.settings.get('autoland_testing', False)
 
-        if testing:
-            logging.info('In testing mode - storing autoland request id %s'
-                         % autoland_request_id_for_testing)
-            autoland_request_id = autoland_request_id_for_testing
-        else:
-            logging.info('Submitting a request to Autoland for review request '
-                         'ID %s for revision %s '
-                         % (review_request_id, last_revision))
+        logging.info('Submitting a request to Autoland for review request '
+                     'ID %s for revision %s '
+                     % (review_request_id, last_revision))
+
+        autoland_url = ext.settings.get('autoland_url')
+        if not autoland_url:
+            return BAD_AUTOLAND_URL
+
+        autoland_user = ext.settings.get('autoland_user')
+        autoland_password = ext.settings.get('autoland_password')
 
-            autoland_url = ext.settings.get('autoland_url')
-            if not autoland_url:
-                return BAD_AUTOLAND_URL
+        if not autoland_user or not autoland_password:
+            return BAD_AUTOLAND_CREDENTIALS
 
-            autoland_user = ext.settings.get('autoland_user')
-            autoland_password = ext.settings.get('autoland_password')
+        pingback_url = autoland_request_update_resource.get_uri(request)
 
-            if not autoland_user or not autoland_password:
-                return BAD_AUTOLAND_CREDENTIALS
+        logging.info('Telling Autoland to give status updates to %s'
+                     % pingback_url)
 
-            pingback_url = autoland_request_update_resource.get_uri(request)
-
-            logging.info('Telling Autoland to give status updates to %s'
-                         % pingback_url)
+        # TODO: Ideally we would just pass the repository name here and
+        #       the mapping in Autoland would take care of things for us.
+        #       See Bug 1188542.
+        try_autoland_tree = TRY_AUTOLAND_TREE
+        if testing:
+            try_autoland_tree = rr.repository.name
 
-            try:
-                response = requests.post(autoland_url + '/autoland',
-                    data=json.dumps({
-                    'tree': TRY_AUTOLAND_TREE,
-                    'pingback_url': pingback_url,
-                    'rev': last_revision,
-                    'destination': TRY_AUTOLAND_DESTINATION,
-                    'trysyntax': try_syntax,
-                }), headers={
-                    'content-type': 'application/json',
-                },
-                    timeout=AUTOLAND_REQUEST_TIMEOUT,
-                    auth=(autoland_user, autoland_password))
-            except requests.exceptions.RequestException:
-                logging.error('We hit a RequestException when submitting a '
-                              'request to Autoland')
-                return AUTOLAND_ERROR
-            except requests.exceptions.Timeout:
-                logging.error('We timed out when submitting a request to '
-                              'Autoland')
-                return AUTOLAND_TIMEOUT
+        try:
+            response = requests.post(autoland_url + '/autoland',
+                data=json.dumps({
+                'tree': try_autoland_tree,
+                'pingback_url': pingback_url,
+                'rev': last_revision,
+                'destination': TRY_AUTOLAND_DESTINATION,
+                'trysyntax': try_syntax,
+            }), headers={
+                'content-type': 'application/json',
+            },
+                timeout=AUTOLAND_REQUEST_TIMEOUT,
+                auth=(autoland_user, autoland_password))
+        except requests.exceptions.RequestException:
+            logging.error('We hit a RequestException when submitting a '
+                          'request to Autoland')
+            return AUTOLAND_ERROR
+        except requests.exceptions.Timeout:
+            logging.error('We timed out when submitting a request to '
+                          'Autoland')
+            return AUTOLAND_TIMEOUT
 
-            if response.status_code != 200:
+        if response.status_code != 200:
+            return AUTOLAND_ERROR, {
+                'status_code': response.status_code,
+                'message': response.json().get('error'),
+            }
+
+        # We succeeded in scheduling the job.
+        try:
+            autoland_request_id = int(response.json().get('request_id', 0))
+        finally:
+            if autoland_request_id is None:
                 return AUTOLAND_ERROR, {
                     'status_code': response.status_code,
-                    'message': response.json().get('error'),
+                    'request_id': autoland_request_id,
                 }
 
-            # We succeeded in scheduling the job.
-            try:
-                autoland_request_id = int(response.json().get('request_id', 0))
-            finally:
-                if autoland_request_id is None:
-                    return AUTOLAND_ERROR, {
-                        'status_code': response.status_code,
-                        'request_id': autoland_request_id,
-                    }
-
         autoland_request = AutolandRequest.objects.create(
             autoland_id=autoland_request_id,
             push_revision=last_revision,
             review_request_id=rr.id,
             user_id=request.user.id,
             extra_data=json.dumps({
                 'try_syntax': try_syntax
             })
@@ -416,16 +410,18 @@ class ImportPullRequestTriggerResource(W
     )
     @transaction.atomic
     def create(self, request, github_user, github_repo, pullrequest, *args,
                **kwargs):
 
         ext = get_extension_manager().get_enabled_extension(
             'mozreview.extension.MozReviewExtension')
 
+        testing = ext.settings.get('autoland_testing', False)
+
         autoland_url = ext.settings.get('autoland_url')
         if not autoland_url:
             return BAD_AUTOLAND_URL
 
         autoland_user = ext.settings.get('autoland_user')
         autoland_password = ext.settings.get('autoland_password')
 
         if not autoland_user or not autoland_password:
@@ -446,23 +442,28 @@ class ImportPullRequestTriggerResource(W
 
         pingback_url = import_pullrequest_update_resource.get_uri(request)
 
         logging.info('Submitting a request to Autoland for pull request'
                      '%s/%s/%s for bug %s with pingback_url %s'
                      % (github_user, github_repo, pullrequest, bugid,
                         pingback_url))
 
+        destination = IMPORT_PULLREQUEST_DESTINATION
+        if testing:
+            # This is just slightly better than hard coding the repo name
+            destination = Repository.objects.all()[0].name
+
         try:
             response = requests.post(autoland_url + '/pullrequest/mozreview',
                 data=json.dumps({
                 'user': github_user,
                 'repo': github_repo,
                 'pullrequest': pullrequest,
-                'destination': IMPORT_PULLREQUEST_DESTINATION,
+                'destination': destination,
                 'bzuserid': request.session['Bugzilla_login'],
                 'bzcookie': request.session['Bugzilla_logincookie'],
                 'bugid': bugid,
                 'pingback_url': pingback_url
             }), headers={
                 'content-type': 'application/json',
             },
                 timeout=AUTOLAND_REQUEST_TIMEOUT,
--- a/pylib/mozreview/mozreview/tests/test-autoland-try.py
+++ b/pylib/mozreview/mozreview/tests/test-autoland-try.py
@@ -1,53 +1,99 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, unicode_literals
 
-import unittest
 import time
 
 import selenium.webdriver.support.expected_conditions as EC
+from selenium.common.exceptions import (NoSuchElementException,
+                                        StaleElementReferenceException)
 from selenium.webdriver.common.by import By
 from selenium.webdriver.common.keys import Keys
 from selenium.webdriver.support.wait import WebDriverWait
 
 from vcttesting.unittest import MozReviewWebDriverTest
 
-@unittest.skip("disabled until bug 1180818 is fixed")
 class AutolandTryTest(MozReviewWebDriverTest):
+    @classmethod
+    def setUpClass(cls):
+        MozReviewWebDriverTest.setUpClass()
+
+        try:
+            self = cls('run')
+            self.create_users([
+                ('jsmith@example.com', 'password1', 'Jeremy Smith [:jsmith]'),
+                ('mjane@example.com', 'password2', 'Mary Jane [:mary]'),
+            ])
+
+            self.create_ldap(b'mjane@example.com', b'mjane', 2001, b'Mary Jane')
+
+            bb = self.user_bugzilla('mjane@example.com')
+            bb.create_bug('TestProduct', 'TestComponent', 'First Bug')
+
+            lr = self.create_basic_repo('mjane@example.com', 'mjane')
+            lr.write('foo', 'first change')
+            lr.run(['commit', '-m', 'Bug 1 - Test try'])
+            lr.run(['push'])
+        except Exception:
+            MozReviewWebDriverTest.tearDownClass()
+            raise
+
     def test_autoland_try(self):
-        self.create_users([
-            ('jsmith@example.com', 'password1', 'Jeremy Smith [:jsmith]'),
-            ('mjane@example.com', 'password2', 'Mary Jane [:mary]'),
-        ])
-        self.create_ldap(b'mjane@example.com', b'mjane', 2001, b'Mary Jane')
-        mjb = self.user_bugzilla('mjane@example.com')
-        mjb.create_bug('TestProduct', 'TestComponent', 'bug1')
-
-        lr = self.create_basic_repo('mjane@example.com', 'mjane')
-        lr.write('foo', 'first change\n')
-        lr.run(['commit', '-m', 'Bug 1 - Test try; r=mary'])
-        lr.run(['push'])
-
         self.reviewboard_login('mjane@example.com', 'password2')
         self.load_rburl('r/1')
 
         # We should not be able to trigger a Try run until the review is
         # published.
+        self.assign_reviewer(0, 'jsmith')
         try_btn = self.browser.find_element_by_id('mozreview-autoland-try-trigger')
         self.assertFalse(try_btn.is_enabled())
         publish_btn = self.browser.find_element_by_id('btn-draft-publish')
         publish_btn.click()
 
         WebDriverWait(self.browser, 10).until(
             EC.invisibility_of_element_located((By.ID, 'draft-banner')))
 
         try_btn = self.browser.find_element_by_id('mozreview-autoland-try-trigger')
         self.assertTrue(try_btn.is_enabled())
 
+        # Clicking the button should display a trychooser dialog
+        try_btn.click()
+        try_text = WebDriverWait(self.browser, 3).until(
+            EC.visibility_of_element_located((By.ID,
+            'mozreview-autoland-try-syntax')))
+        try_text.send_keys('try: stuff')
+        try_submit = self.browser.find_element_by_xpath('//input[@value="Submit"]')
+
+        # clicking the Submit button should display an activity indicator
+        try_submit.click()
+
+        element = WebDriverWait(self.browser, 3).until(
+            EC.visibility_of_element_located((By.ID, 'activity-indicator'))
+        )
+        try:
+            self.assertTrue('A server error occurred' not in element.text)
+        except StaleElementReferenceException:
+            # The activity indicator may already have disappeared by the time
+            # we check the text, but we want to be able to catch the server
+            # error if it shows up.
+            pass
+
+        # the try job should eventually create a new change description
+        WebDriverWait(self.browser, 10).until(
+            EC.visibility_of_element_located((By.CLASS_NAME, 'changedesc'))
+        )
+
+        time.sleep(5)
+        self.browser.refresh()
+        changedesc = self.browser.find_element_by_class_name('changedesc')
+        iframe = changedesc.find_element_by_tag_name('iframe')
+        self.assertTrue('https://treeherder.mozilla.org/'
+            in iframe.get_attribute('src'))
+
         # We should not be able to trigger a Try run for another user.
         self.reviewboard_login('jsmith@example.com', 'password1')
         self.load_rburl('r/1')
-        try_btn = self.browser.find_element_by_id('mozreview-autoland-try-trigger')
-        self.assertFalse(try_btn.is_enabled())
+        with self.assertRaises(NoSuchElementException):
+            self.browser.find_element_by_id('mozreview-autoland-try-trigger')
--- a/pylib/mozreview/mozreview/tests/test-import-pullrequest.py
+++ b/pylib/mozreview/mozreview/tests/test-import-pullrequest.py
@@ -1,44 +1,75 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, unicode_literals
 
 import unittest
-import time
 
 import selenium.webdriver.support.expected_conditions as EC
 from selenium.webdriver.common.by import By
 from selenium.webdriver.common.keys import Keys
 from selenium.webdriver.support.wait import WebDriverWait
 
 from vcttesting.unittest import MozReviewWebDriverTest
 
 
-@unittest.skip("disabled until bug 1180818 is fixed") 
 class ImportPullrequestTest(MozReviewWebDriverTest):
+    @classmethod
+    def setUpClass(cls):
+        MozReviewWebDriverTest.setUpClass()
+
+        try:
+            self = cls('run')
+            self.create_users([
+                ('mjane@example.com', 'password2', 'Mary Jane [:mary]'),
+            ])
+
+            self.create_ldap(b'mjane@example.com', b'mjane', 2001, b'Mary Jane')
+
+            lr = self.create_basic_repo('mjane@example.com', 'mjane')
+        except Exception:
+            MozReviewWebDriverTest.tearDownClass()
+            raise
+
     def test_import_pullrequest(self):
-        self.create_users([
-            ('mjane@example.com', 'password2', 'Mary Jane [:mary]'),
-        ])
-        self.create_ldap(b'mjane@example.com', b'mjane', 2001, b'Mary Jane')
-
         self.reviewboard_login('mjane@example.com', 'password2')
 
         self.load_rburl('import-pullrequest/dminor/gecko-dev/1')
 
         btn = self.browser.find_element_by_id('mozreview-import-pullrequest-trigger');
         btn.click()
 
+        # Clicking the button should display the activity indicator
         element = WebDriverWait(self.browser, 3).until(
             EC.visibility_of_element_located((By.ID, "activity-indicator"))
         )
+        try:
+            self.assertTrue('Error' not in element.text)
+        except StaleElementReferenceException:
+            # The activity indicator may already have disappeared by the time
+            # we check the text, but we want to be able to catch the error
+            # if it shows up.
+            pass
 
-        # This should be present regardless of whether or not the request has
-        # completed.
-        self.assertTrue('Importing pull request' in element.text)
+        # If this succeeds, we should be redirected to a review for the pull
+        # request.
+        WebDriverWait(self.browser, 10).until(
+            lambda x: 'bz://1/' in self.browser.title)
 
-        # We need more work on the testing infrastructure for this to succeed,
-        # but this does indicate that the request made it to Autoland.
-        WebDriverWait(self.browser, 3).until(
-            lambda x: "failed" in element.text)
+        # Autoland should create the bug for us.
+        bug = self.bugzilla().client.get(1)
+        self.assertTrue('A pullrequest' in bug.summary)
+
+        # It is possible to import a pull request again.
+        self.load_rburl('import-pullrequest/dminor/gecko-dev/1')
+
+        btn = self.browser.find_element_by_id('mozreview-import-pullrequest-trigger');
+        btn.click()
+
+        WebDriverWait(self.browser, 10).until(
+            lambda x: 'bz://1/' in self.browser.title)
+
+        # Autoland should reuse the existing bug for this pull request.
+        with self.assertRaises(KeyError):
+            self.bugzilla().client.get(2)