Continuous Integration for Cocoa

Outdated: See Continuous Integration for Cocoa Reloaded

Using GHUnit and Jenkins, it is quite straight-forward to set up continuous integration environment for Cocoa—also iOS—projects.

  • Set up GHUnit and write some awesome tests
  • Makefile (put this in the project root folder)
    default:
    	# Set default make action here
    	# xcodebuild -target Tests -configuration MyMainTarget -sdk macosx build	
    
    clean:
    	-rm -rf build/*
    
    test:
    	GHUNIT_CLI=1 GHUNIT_AUTORUN=1 GHUNIT_AUTOEXIT=1 xcodebuild -target NAME_OF_TEST_TARGET -configuration Debug -sdk macosx build
    
  • Put RunTests.sh of GHUnit in the project root folder
  • I modified main.mto the following to be compatible with ARC and to make sure that the tests are run parallelly
    //
    //  GHUnitTestMain.m
    //  GHUnit
    //
    //  Created by Gabriel Handford on 2/22/09.
    //  Copyright 2009. All rights reserved.
    //
    //  Permission is hereby granted, free of charge, to any person
    //  obtaining a copy of this software and associated documentation
    //  files (the "Software"), to deal in the Software without
    //  restriction, including without limitation the rights to use,
    //  copy, modify, merge, publish, distribute, sublicense, and/or sell
    //  copies of the Software, and to permit persons to whom the
    //  Software is furnished to do so, subject to the following
    //  conditions:
    //
    //  The above copyright notice and this permission notice shall be
    //  included in all copies or substantial portions of the Software.
    //
    //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    //  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    //  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    //  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    //  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    //  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    //  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    //  OTHER DEALINGS IN THE SOFTWARE.
    //
    
    #import <GHUnit/GHUnit.h>
    #import <GHUnit/GHTestApp.h>
    
    // Default exception handler
    void exceptionHandler(NSException *exception) { 
        NSLog(@"%@\n%@", [exception reason], GHUStackTraceFromException(exception));
    }
    
    int main(int argc, char *argv[]) {
        
        /*!
         Default:   Set to:
         NSDebugEnabled                        NO       "YES"
         NSZombieEnabled                       NO       "YES"
         NSDeallocateZombies                   NO       "YES"
         NSHangOnUncaughtException             NO       "YES"
         
         NSEnableAutoreleasePool              YES       "NO"
         NSAutoreleaseFreedObjectCheckEnabled  NO       "YES"
         NSAutoreleaseHighWaterMark             0       non-negative integer
         NSAutoreleaseHighWaterResolution       0       non-negative integer
         
         For info on these varaiables see NSDebug.h; http://theshadow.uw.hu/iPhoneSDKdoc/Foundation.framework/NSDebug.h.html
         
         For malloc debugging see: http://developer.apple.com/mac/library/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
         */
        NSSetUncaughtExceptionHandler(&exceptionHandler);
        int retVal = 0;
    
        @autoreleasepool {
            // Register any special test case classes
            //[[GHTesting sharedInstance] registerClassName:@"GHSpecialTestCase"];  
            
            // If GHUNIT_CLI is set we are using the command line interface and run the tests
            // Otherwise load the GUI app
            if (getenv("GHUNIT_CLI")) {
                //        retVal = [GHTestRunner run];
                GHTestRunner *testRunner = [GHTestRunner runnerFromEnv];
                testRunner.inParallel = YES;
                [testRunner runTests];
                retVal = (int)testRunner.stats.failureCount;
            } else {
                // To run all tests (from ENV)
                GHTestApp *app = [[GHTestApp alloc] init];
                // To run a different test suite:
                //GHTestSuite *suite = [GHTestSuite suiteWithTestFilter:@"GHSlowTest,GHAsyncTestCaseTest"];
                //GHTestApp *app = [[GHTestApp alloc] initWithSuite:suite];
                // Or set global:
                //GHUnitTest = @"GHSlowTest";
                [NSApp run];
            }
    
        }
        
        return retVal;
    }
  • Create a new Jenkins free-style job with the following command
    make clean && WRITE_JUNIT_XML=YES make test
    and
    image
     
Tags: jenkins cocoa