Archived

How to create a continuous integration

From Joomla! Documentation

Revision as of 09:44, 21 July 2010 by Rudy (talk | contribs) (→‎HOW ?)
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

This page has been archived. This page contains information for an unsupported Joomla! version or is no longer relevant. It exists only as a historical reference, it will not be improved and its content may be incomplete and/or contain broken links.

You want to know how to develop a continuous integration? This tutorial aims to guide you to achieve a functional result. If you need advice feel free to contact me: Rudy BRUNEAU.

Introduction[edit]

Continuous integration is a way to check the daily functioning of an application.

Tools and Environment[edit]

Purpose of this operation[edit]

Continuous integration[edit]

Formalization by Martin Fowler and Kent Beck, 2000 « Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. »

« Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible. »

Indeed the continuous integration process will help to automate any changes made but also run unit tests. This process must be executed as frequently as possible in order to detect problems to solve them faster.

Unit Tests[edit]

The purpose of unit testing is to automate regression testing. When developing software, you can easily make modifications without realizing that they introduce bugs in some cases. This is called regression bugs, in that it introduces new bugs as a result of a functional outcome. Since it is not humanly possible to systematically test all possible use cases, these bugs can be found deployed in production, with all the problems that entails.

Web Tests[edit]

The tests are part of the web unit tests, but they do not check the non-regression of the code. They will check any changes directly on the web pages during the execution of continuous integration. Using Selenium IDE (Mozilla Firefox add-on) we can create and launch our web tests.


HOW ?[edit]

Before start, read this documentation. Using this material that I could build my continuous integration.
Phing docs

Now start the xml file :

  • define property (variable) to simplify notation after
<?xml version="1.0" encoding="UTF-8"?>
<project name="hopscore_website" default="joomla" basedir="../">
<property name="report.dir" value="${basedir}/source/ic"/>
<property name="utils" value="${basedir}/source/utils"/>
<property name="Jinstall" value="${report.dir}/JoomlaInstall"/>
<property name="Jtest" value="${report.dir}/JoomlaTest"/>
<property name="sql_file" value="${Jinstall}/dump.sql"/> 
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
  • Define all targets which will be executed during continuous integration
<target name="joomla" depends="uninstall,update, zip, install, data, extension, build" />
<target name="extension" depends="unzip, all" />
  <!--
  The default build target for this project. It simply depends on all sub tasks
  that perform the project build. The sub targets are executed in the listed
  order.

  1. 'clean' Clean old project build artifacts
  2. 'update' Update project working copy
  3. 'phpunit' Execute unit tests, generate metrics, coverage etc.
  -->
<target name="build" depends="clear,clean,phpunit,stopseleniumrc" />

All Targets will run in the order defined by the target parent. The first task is : Joomla defined in Project. Then all targets in 'depends' attributs will be executed.

Step 1: Install a blank Joomla 1.5.x[edit]

Copy the files inside the archive Joomla except the Setup folder that is deleted from the end of an manual installation.

  • Create a folder with your Blank Joomla.
  • Copy all directories and files into a new folder except Installation directory.

xml file :

<target name="uninstall">
		<delete dir="${Jtest}"/>
        <exec executable="php">
		<arg line="${Jinstall}/drop_db.php"/>
        </exec>
	</target>
	
	<target name="install">
		<mkdir dir="${Jtest}"/>
		<copy todir="${Jtest}">
			<fileset dir="${Jinstall}">
				<exclude name="dump.sql"/>
				<exclude name="install_extensions.php"/>
				<exclude name="install_extensions.xml"/>
				<exclude name="create_db.php"/>
				<exclude name="autoloadeditadd.php"/>
				<exclude name="autoloadeditremove.php"/>
				<exclude name="zip_com_products.php"/>
				<exclude name="unzip.php"/>
				<exclude name="drop_db.php"/>
				<exclude name="add_sql.php"/>
			</fileset>
		</copy>

Step 2: Automatically install the database Joomla tables[edit]

To install the database I chose to run php scripts for several things:

  • Creation of the database with mysql language
  • Adding Joomla tables and data


Xml File:

<target name="install">
		<!-- Step 1... -->
        <exec command="php ${Jinstall}/create_db.php" />
        <exec command="php ${Jinstall}/add_sql.php" />
</target>

Php Files:

  • Create database
$link = mysql_connect("localhost","name","password");
$mydb="db_name";
mysql_query("CREATE DATABASE IF NOT EXISTS $mydb",$link) or die (mysql_error()); 
mysql_close();
  • Import data
$file = file_get_contents(dirname(__FILE__)."/dump.sql");
 
  if($file === false){
    die('Fichier erroné : '.$url);
  }
 
  $requetes = explode(';', $file);

  $link = mysql_connect("localhost","name","password");
  $mydb="db_name";
  mysql_select_db($mydb) or die (mysql_error());
  foreach($requetes as $requete){
    mysql_query($requete.';', $link) or die(mysql_error());
  }
  mysql_close();

Step 3: Automatically install components / modules / plugins in blank Joomla[edit]

Reproduce a component installation as in Joomla administration.

  • Get the last versionned file from SVN

Xml File:

<target name="zip">
        <exec executable="php" dir="${Jinstall}">
            <arg line="zip_com_products.php 
            ${basedir}/source/products/components/com_products 
            ${JoomlaInstall}/Joomla/utils/components/" />
        </exec>
</target>

Php File:

  • zip_com_products.php contains two methods which Zip a not empty folder.
/*
go trough the path of the file to zip
*/
Recurs_zip($src,&$zip,$path_lenght)
/*
add all files and directories in an archive
*/
compress($src,$dir)
  • Provide a file .zip input

Xml File:

<target name="unzip">
        <echo taskname="Unzip" message="Unzip all components/modules/plugins" />
        <exec command="php ${Jinstall}/unzip.php"/>
        <echo taskname="Unzip" message="End of Unzip Task" />
    </target>

Php File:

if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
$path = dirname(__FILE__).DS.'Joomla'.DS.'utils';

function ScanDirectory($Directory) {

$MyDirectory = opendir($Directory) or die('Erreur');
while($Entry = @readdir($MyDirectory)) {
    if (is_dir($Directory.DS.$Entry) && $Entry != '.' && $Entry != '..') {
        ScanDirectory($Directory.'/'.$Entry);
    } else {
        $zip = new ZipArchive;
        if ($zip->open($Directory.DS.$Entry) === TRUE) {
            $name = str_replace(".zip", "", $Entry);
            $zip->extractTo($Directory.DS);
            $zip->close();
        }
    }
			
}
closedir($MyDirectory);	
}

ScanDirectory($path);
  • Run the installation file component / modules / plugins

Xml File:

<target name="all">
        <echo taskname="Install extensions" message="Start of Install all Joomla extensions" />
		<exec command="php ${Jinstall}/install_extensions.php" dir="${Jinstall}"/>
        <echo taskname="Install extensions" message="End of Install Extensions" />
</target>

Php File:

under construction...

Step 4: How to run Unit Tests[edit]

To write and run UnitTest it is easier to look at this documentation.
PhpUnit docs

Go to the "build" Target in xml file :

<target name="build" depends="clear,clean,phpunit,stopseleniumrc" />
  • clear : Delete all report files.
<!-- 
  delete phpunit.xml & phpunit-noframe.html files
  -->
  <target name="clear">
        <delete file="${report.dir}/phpunit.xml"/>
        <delete file="${report.dir}/phpunit2-noframes.html"/>
  </target>
  • clean : Clean old project build artifacts.
  <!--
  The clean target is used to remove build artifacts of previous builds. Otherwise
  CruiseControl will present old, maybe successful result, even if your build
  process fails.
  -->
  <target name="clean">
        <!-- Remove old log files -->
    <delete>
      <fileset dir="${basedir}/build/logs" includes="**.*" />
    </delete>
        <!-- Remove old documentation -->
    <delete>
      <fileset dir="${basedir}/build/doc" includes="**.*" />
    </delete>
        <!-- Remove old coverage report -->
    <delete>
      <fileset dir="${basedir}/build/coverage" includes="**.*" />
    </delete>
  </target>
  • phpunit : start UnitTests, webTests, and make all report
<target name="phpunit">
   <antcall target="startseleniumrc"></antcall>
    <exec executable="phpunit" dir="${basedir}/source" failonerror="on" resultproperty="test-failed">
      <arg line="--log-xml ${basedir}/build/logs/phpunit.xml
                 --log-pmd ${basedir}/build/logs/phpunit.pmd.xml
                 --log-metrics ${basedir}/build/logs/phpunit.metrics.xml
                 --coverage-xml  ${basedir}/build/logs/phpunit.coverage.xml
                 --coverage-html ${basedir}/build/coverage
                 hopscorecomAllTests AllTests.php" />
    </exec>
    <antcall target="stopseleniumrc"></antcall>
  </target>
  • stopseleniumrc : kill processus selenium
<target name="stopseleniumrc">
    <get taskname="selenium-shutdown" 
         src="http://localhost:4444/selenium-server/driver/?cmd=shutDown"	
         dest="result.txt" ignoreerrors="true" />
    <echo taskname="selenium-shutdown" message="DGF Errors during shutdown are expected" />
  </target>

Step 5: How to run Web Tests[edit]

Web tests will be run using a php file called during Unit Tests.

Step 6: Make a report[edit]

The report is a xml file which be created after all tests to show errors, failures, and success. With Cruise Control and PhpUnderControl you will see all details of your continuous integration.


Php files to execute testSuite[edit]

AllTest.php
called UnitTests file & WebTests file

if (defined('PHPUnit_MAIN_METHOD') === false) {
    define('PHPUnit_MAIN_METHOD', 'hopscorecomAllTests::main');
}

require_once 'PHPUnit/Framework/TestSuite.php';
require_once 'PHPUnit/TextUI/TestRunner.php';

require_once dirname(__FILE__) . '/AllWebTests.php';
require_once dirname(__FILE__) . '/ProductsAllTests.php';
/**
 * Main test suite for phpUnderControl.
 */
class hopscorecomAllTests
{
    /**
     * Test suite main method.
     *
     * @return void
     */
    public static function main()
    {
        PHPUnit_TextUI_TestRunner::run(self::suite());	
    }
    
    /**
     * Creates the phpunit test suite for this package.
     *
     * @return PHPUnit_Framework_TestSuite
     */
    public static function suite()
    {
        $suite = new PHPUnit_Framework_TestSuite('HopScore.com - AllTests ');
        // Unit Test
	$suite->addTestSuite('hopscorecomProductsAllTests');
        // WebTest : include WebTest to display all in report
        $suite->addTestSuite('hopscorecomAllWebTests');
        return $suite;
    }
}

if (PHPUnit_MAIN_METHOD === 'hopscorecomAllTests::main') {
    phpucAllTests::main();
}

ProductsAllTest.php

 
....

require_once dirname(__FILE__) . '/UnitTests/bashtest.php';
require_once dirname(__FILE__) . '/UnitTests/fusiontest.php';
require_once dirname(__FILE__) . '/UnitTests/transformtest.php';

/**
 * Main test suite for HopScore API package.
 */
class hopscorecomProductsAllTests
{
    /* same main() */

    /**
     * Creates the phpunit test suite for this package.
     * @return PHPUnit_Framework_TestSuite
     */
    public static function suite()
    {
        $suite = new PHPUnit_Framework_TestSuite('HopScore.com - ProductsAllTests');
        $suite->addTestSuite('BashTest');
        $suite->addTestSuite('FusionTest');
	$suite->addTestSuite('TransformTest');
        return $suite;
    }
}

if (PHPUnit_MAIN_METHOD === 'hopscorecomProductsAllTests::main') {
    hopscoreProductsAllTests::main();
}

AllWebTests.php

....
/* require .php file from Selenium */
require_once dirname(__FILE__) . '/WebTests/*****.php';

/**
 * Main test suite for HopScore.Com Web Tests
 */
class hopscorecomAllWebTests
{
    /* Same Main() */
    /**
     * Creates the phpunit test suite for this package.
     * @return PHPUnit_Framework_TestSuite
     */
    public static function suite()
    {
        $suite = new PHPUnit_Framework_TestSuite('HopScorecom - AllWebTests');
        // Name of class of php webTest file
        $suite->addTestSuite('name_of_class');
        return $suite;
    }
}

if (PHPUnit_MAIN_METHOD === 'hopscorecomAllWebTests::main') {
    hopscoreAPIAllTests::main();
}

See Also[edit]

Continuous Integration