← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~googol/openlp/jenkins-script into lp:openlp

 

Andreas Preikschat has proposed merging lp:~googol/openlp/jenkins-script into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~googol/openlp/jenkins-script/+merge/215739

Hello,

+Print repository name and revision number when using script (especially the revno is interesting for devs)
+Tell jenkins who triggered the build
+Added a blacklist for outdated tokens
+Use colours to indicate success/failure
+fixed job names

Tests need a close look as it uses a lock object. Please double check.

lp:~googol/openlp/jenkins-script (revision 2363)
[SUCCESS] http://ci.openlp.org/job/Branch-01-Pull/317/
[SUCCESS] http://ci.openlp.org/job/Branch-02-Functional-Tests/276/
[SUCCESS] http://ci.openlp.org/job/Branch-03-Interface-Tests/224/
[FAILURE] http://ci.openlp.org/job/Branch-04-Windows_Tests/181/
[SUCCESS] http://ci.openlp.org/job/Branch-05a-Code_Analysis/130/
[FAILURE] http://ci.openlp.org/job/Branch-05b-Test_Coverage/2/

Tests are failing because bzr is not installed in wine and the coverage test is missing for another strange reasons (but it is not the branch mistake, the job setup causes problems)
-- 
https://code.launchpad.net/~googol/openlp/jenkins-script/+merge/215739
Your team OpenLP Core is requested to review the proposed merge of lp:~googol/openlp/jenkins-script into lp:openlp.
=== modified file 'scripts/jenkins_script.py'
--- scripts/jenkins_script.py	2014-03-31 18:10:14 +0000
+++ scripts/jenkins_script.py	2014-04-14 19:30:32 +0000
@@ -40,6 +40,7 @@
 """
 
 from optparse import OptionParser
+import re
 from requests.exceptions import HTTPError
 from subprocess import Popen, PIPE
 import sys
@@ -49,6 +50,9 @@
 
 
 JENKINS_URL = 'http://ci.openlp.org/'
+REPO_REGEX = r'(.*/+)(~.*)'
+# Allows us to black list token. So when we change the token, we can display a proper message to the user.
+OLD_TOKENS = []
 
 
 class OpenLPJobs(object):
@@ -59,9 +63,20 @@
     Branch_Functional = 'Branch-02-Functional-Tests'
     Branch_Interface = 'Branch-03-Interface-Tests'
     Branch_Windows = 'Branch-04-Windows_Tests'
-    Branch_PEP = 'Branch-05-Code-Analysis'
-
-    Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP]
+    Branch_PEP = 'Branch-05a-Code_Analysis'
+    Branch_Coverage = 'Branch-05b-Test_Coverage'
+
+    Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP, Branch_Coverage]
+
+
+class Colour(object):
+    """
+    This class holds values which can be used to print coloured text.
+    """
+    RED_START = '\033[1;31m'
+    RED_END = '\033[1;m'
+    GREEN_START = '\033[1;32m'
+    GREEN_END = '\033[1;m'
 
 
 class JenkinsTrigger(object):
@@ -79,14 +94,25 @@
         """
         Ask our jenkins server to build the "Branch-01-Pull" job.
         """
-        self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build({'BRANCH_NAME': self.repo_name}, token=self.token)
+        bzr = Popen(('bzr', 'whoami'), stdout=PIPE, stderr=PIPE)
+        raw_output, error = bzr.communicate()
+        # We just want the name (not the email).
+        name = ' '.join(raw_output.decode().split()[:-1])
+        cause = 'Build triggered by %s (%s)' % (name, self.repo_name)
+        self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build(
+            {'BRANCH_NAME': self.repo_name, 'cause': cause}, token=self.token)
 
     def print_output(self):
         """
         Print the status information of the build tirggered.
         """
-        print("Add this to your merge proposal:")
-        print("--------------------------------")
+        print('Add this to your merge proposal:')
+        print('--------------------------------')
+        bzr = Popen(('bzr', 'revno'), stdout=PIPE, stderr=PIPE)
+        raw_output, error = bzr.communicate()
+        revno = raw_output.decode().strip()
+        print('%s (revision %s)' % (get_repo_name(), revno))
+
         for job in OpenLPJobs.Jobs:
             self.__print_build_info(job)
 
@@ -107,17 +133,17 @@
         """
         job = self.jenkins_instance.job(job_name)
         while job.info['inQueue']:
-            # Give other processes the possibility to take over. Like Thread.yield().
-            time.sleep(0)
+            time.sleep(1)
         build = job.last_build
         build.wait()
-        result_string = build.info['result']
+        if build.info['result'] == 'SUCCESS':
+            # Make 'SUCCESS' green.
+            result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END)
+        else:
+            # Make 'FAILURE' red.
+            result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END)
         url = build.info['url']
         print('[%s] %s' % (result_string, url))
-        # On failure open the browser.
-        #if result_string == "FAILURE":
-        #    url += 'console'
-        #    Popen(('xdg-open', url), stderr=PIPE)
 
 
 def get_repo_name():
@@ -139,46 +165,41 @@
     for line in output_list:
         # Check if it is remote branch.
         if 'push branch' in line:
-            repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
-            break
+            match = re.match(REPO_REGEX, line)
+            if match:
+                repo_name = 'lp:%s' % match.group(2)
+                break
         elif 'checkout of branch' in line:
-            repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
-            break
-    repo_name = repo_name.strip('/')
-
-    # Did we find the branch name?
-    if not repo_name:
-        for line in output_list:
-            # Check if the branch was pushed.
-            if 'Shared repository with trees (format: 2a)' in line:
-                print('Not a branch. cd to a branch.')
-                return
-        print('Not a branch. Have you pushed it to launchpad?')
-        return
-    return repo_name
+            match = re.match(REPO_REGEX, line)
+            if match:
+                repo_name = 'lp:%s' % match.group(2)
+                break
+    return repo_name.strip('/')
 
 
 def main():
     usage = 'Usage: python %prog TOKEN [options]'
 
     parser = OptionParser(usage=usage)
-    parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True,
+    parser.add_option('-d', '--disable-output', dest='enable_output', action='store_false', default=True,
                       help='Disable output.')
-    parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False,
+    parser.add_option('-b', '--open-browser', dest='open_browser', action='store_true', default=False,
                       help='Opens the jenkins page in your browser.')
-    #parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true",
-    #                  default=False, help='Opens the jenkins page in your browser in case a test fails.')
     options, args = parser.parse_args(sys.argv)
 
     if len(args) == 2:
         if not get_repo_name():
+            print('Not a branch. Have you pushed it to launchpad? Did you cd to the branch?')
             return
         token = args[-1]
+        if token in OLD_TOKENS:
+            print('Your token is not valid anymore. Get the most recent one.')
+            return
         jenkins_trigger = JenkinsTrigger(token)
         try:
             jenkins_trigger.trigger_build()
         except HTTPError as e:
-            print("Wrong token.")
+            print('Wrong token.')
             return
         # Open the browser before printing the output.
         if options.open_browser:

=== modified file 'tests/functional/openlp_core_lib/test_image_manager.py'
--- tests/functional/openlp_core_lib/test_image_manager.py	2014-03-14 22:08:44 +0000
+++ tests/functional/openlp_core_lib/test_image_manager.py	2014-04-14 19:30:32 +0000
@@ -30,12 +30,16 @@
 Package to test the openlp.core.ui package.
 """
 import os
+import time
+from threading import Lock
 
 from unittest import TestCase
 from PyQt4 import QtGui
 
 from openlp.core.common import Registry
 from openlp.core.lib import ImageManager, ScreenList
+from openlp.core.lib.imagemanager import Priority
+from tests.functional import patch
 from tests.helpers.testmixin import TestMixin
 
 TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
@@ -51,6 +55,8 @@
         self.get_application()
         ScreenList.create(self.app.desktop())
         self.image_manager = ImageManager()
+        self.lock = Lock()
+        self.sleep_time = 0.1
 
     def tearDown(self):
         """
@@ -82,3 +88,84 @@
         with self.assertRaises(KeyError) as context:
             self.image_manager.get_image(TEST_PATH, 'church1.jpg')
         self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image')
+
+    def process_cache_test(self):
+        """
+        Test the process_cache method
+        """
+        with patch('openlp.core.lib.imagemanager.resize_image') as mocked_resize_image, \
+                patch('openlp.core.lib.imagemanager.image_to_byte') as mocked_image_to_byte:
+            # GIVEN: Mocked functions
+            mocked_resize_image.side_effect = self.mocked_resize_image
+            mocked_image_to_byte.side_effect = self.mocked_image_to_byte
+            image1 = 'church.jpg'
+            image2 = 'church2.jpg'
+            image3 = 'church3.jpg'
+            image4 = 'church4.jpg'
+
+            # WHEN: Add the images. Then get the lock (=queue can not be processed).
+            self.lock.acquire()
+            self.image_manager.add_image(TEST_PATH, image1, None)
+            self.image_manager.add_image(TEST_PATH, image2, None)
+
+            # THEN: All images have been added to the queue, and only the first image is not be in the list anymore, but
+            #  is being processed (see mocked methods/functions).
+            # Note: Priority.Normal means, that the resize_image() was not completed yet (because afterwards the #
+            # priority is adjusted to Priority.Lowest).
+            assert self.get_image_priority(image1) == Priority.Normal, "image1's priority should be 'Priority.Normal'"
+            assert self.get_image_priority(image2) == Priority.Normal, "image2's priority should be 'Priority.Normal'"
+
+            # WHEN: Add more images.
+            self.image_manager.add_image(TEST_PATH, image3, None)
+            self.image_manager.add_image(TEST_PATH, image4, None)
+            # Allow the queue to process.
+            self.lock.release()
+            # Request some "data".
+            image_bytes = self.image_manager.get_image_bytes(TEST_PATH, image4)
+            image_object = self.image_manager.get_image(TEST_PATH, image3)
+            # Now the mocked methods/functions do not have to sleep anymore.
+            self.sleep_time = 0
+            # Wait for the queue to finish.
+            while not self.image_manager._conversion_queue.empty():
+                time.sleep(0.1)
+            # Because empty() is not reliable, wait a litte; just to make sure.
+            time.sleep(0.1)
+            # THEN: The images' priority reflect how they were processed.
+            assert self.image_manager._conversion_queue.qsize() == 0, "The queue should be empty."
+            assert self.get_image_priority(image1) == Priority.Lowest, \
+                "The image should have not been requested (=Lowest)"
+            assert self.get_image_priority(image2) == Priority.Lowest, \
+                "The image should have not been requested (=Lowest)"
+            assert self.get_image_priority(image3) == Priority.Low, "Only the QImage should have been requested (=Low)."
+            assert self.get_image_priority(image4) == Priority.Urgent, \
+                "The image bytes should have been requested (=Urgent)."
+
+    def get_image_priority(self, image):
+        """
+        This is a help method to get the priority of the given image out of the image_manager's cache.
+
+        NOTE: This requires, that the image has been added to the image manager using the *TEST_PATH*.
+
+        :param image: The name of the image. E. g. ``image1``
+        """
+        return self.image_manager._cache[(TEST_PATH, image)].priority
+
+    def mocked_resize_image(self, *args):
+        """
+        This is a mocked method, so that we can control the work flow of the image manager.
+        """
+        self.lock.acquire()
+        self.lock.release()
+        # The sleep time is adjusted in the test case.
+        time.sleep(self.sleep_time)
+        return QtGui.QImage()
+
+    def mocked_image_to_byte(self, *args):
+        """
+        This is a mocked method, so that we can control the work flow of the image manager.
+        """
+        self.lock.acquire()
+        self.lock.release()
+        # The sleep time is adjusted in the test case.
+        time.sleep(self.sleep_time)
+        return ''
\ No newline at end of file

=== added file 'tests/resources/church2.jpg'
Binary files tests/resources/church2.jpg	1970-01-01 00:00:00 +0000 and tests/resources/church2.jpg	2014-04-14 19:30:32 +0000 differ
=== added file 'tests/resources/church3.jpg'
Binary files tests/resources/church3.jpg	1970-01-01 00:00:00 +0000 and tests/resources/church3.jpg	2014-04-14 19:30:32 +0000 differ
=== added file 'tests/resources/church4.jpg'
Binary files tests/resources/church4.jpg	1970-01-01 00:00:00 +0000 and tests/resources/church4.jpg	2014-04-14 19:30:32 +0000 differ

References