← 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:
  Raoul Snyman (raoul-snyman)
  Tim Bentley (trb143)

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

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

The failure of the code analyse is fixed in the duplicate speed branch.

lp:~googol/openlp/jenkins-script (revision 2366)
[SUCCESS] http://ci.openlp.org/job/Branch-01-Pull/338/
[SUCCESS] http://ci.openlp.org/job/Branch-02-Functional-Tests/295/
[SUCCESS] http://ci.openlp.org/job/Branch-03-Interface-Tests/243/
[SUCCESS] http://ci.openlp.org/job/Branch-04-Windows_Tests/205/
[FAILURE] http://ci.openlp.org/job/Branch-05a-Code_Analysis/139/
[SUCCESS] http://ci.openlp.org/job/Branch-05b-Test_Coverage/14/


-- 
https://code.launchpad.net/~googol/openlp/jenkins-script/+merge/215950
Your team OpenLP Core is subscribed to branch 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-15 20:38:31 +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-15 20:38:31 +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,87 @@
         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).
+            self.assertEqual(self.get_image_priority(image1), Priority.Normal,
+                             "image1's priority should be 'Priority.Normal'")
+            self.assertEqual(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.
+            self.assertEqual(self.image_manager._conversion_queue.qsize(), 0, "The queue should be empty.")
+            self.assertEqual(self.get_image_priority(image1), Priority.Lowest,
+                             "The image should have not been requested (=Lowest)")
+            self.assertEqual(self.get_image_priority(image2), Priority.Lowest,
+                             "The image should have not been requested (=Lowest)")
+            self.assertEqual(self.get_image_priority(image3), Priority.Low,
+                             "Only the QImage should have been requested (=Low).")
+            self.assertEqual(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-15 20:38:31 +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-15 20:38:31 +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-15 20:38:31 +0000 differ

Follow ups