Actions

Archived

Difference between revisions of "How to create a continuous integration"

From Joomla! Documentation

(New page: 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: [mailto:rudy.bruneau@h...)
 
 
(7 intermediate revisions by 3 users not shown)
Line 14: Line 14:
 
* [http://en.wikipedia.org/wiki/LAMP_%28software_bundle%29 LAMP]
 
* [http://en.wikipedia.org/wiki/LAMP_%28software_bundle%29 LAMP]
 
* [[Joomla]] 1.5.x
 
* [[Joomla]] 1.5.x
* [http://en.wikipedia.org/wiki/Selenium_%28software%29 Selenium] Server 1.0.2 and [http://en.wikipedia.org/wiki/Selenium_%28software%29 Selenium] Remote Control 1.3
+
* [http://en.wikipedia.org/wiki/Selenium_%28software%29 Selenium] Server 1.0.2 and Selenium Remote Control 1.3
 
* Server : [http://en.wikipedia.org/wiki/Linux Linux] [http://en.wikipedia.org/wiki/Ubuntu_%28operating_system%29 Ubuntu]
 
* Server : [http://en.wikipedia.org/wiki/Linux Linux] [http://en.wikipedia.org/wiki/Ubuntu_%28operating_system%29 Ubuntu]
 
* PhpUnit
 
* PhpUnit
* Extension for [http://en.wikipedia.org/wiki/PHP Php] : phpZip
+
* Extension for Php : phpZip
* [http://phing.info/trac/ Phing] : a [http://en.wikipedia.org/wiki/PHP Php] equivalent to 'make'
+
* [http://phing.info/trac/ Phing] : a Php equivalent to 'make'
  
 
==Purpose of this operation==
 
==Purpose of this operation==
Line 38: Line 38:
  
 
==HOW ?==
 
==HOW ?==
Before start read this documentation. Using this material that I could build my continuous integration.<br />
+
Before start, read this documentation. Using this material that I could build my continuous integration.<br />
 
[http://phing.info/docs/guide/stable/ Phing docs]
 
[http://phing.info/docs/guide/stable/ Phing docs]
  
Line 54: Line 54:
 
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
 
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
 
</source>
 
</source>
* Define all target which will be executed during continuous integration
+
* Define all targets which will be executed during continuous integration
 
<source lang="xml">
 
<source lang="xml">
 
<target name="joomla" depends="uninstall,update, zip, install, data, extension, build" />
 
<target name="joomla" depends="uninstall,update, zip, install, data, extension, build" />
Line 150: Line 150:
 
===Step 3: Automatically install components / modules / plugins in blank Joomla===
 
===Step 3: Automatically install components / modules / plugins in blank Joomla===
 
Reproduce a component installation as in Joomla administration.
 
Reproduce a component installation as in Joomla administration.
* Get the last versionned file from [[SVN]]
+
* Get the last versionned file from [http://en.wikipedia.org/wiki/Apache_Subversion SVN]
 
'''Xml File:'''
 
'''Xml File:'''
 
<source lang="xml">
 
<source lang="xml">
Line 417: Line 417:
 
==See Also==
 
==See Also==
 
[http://www.geek-directeur-technique.com/post/2009/03/18/Tests-unitaires-et-integration-continue Continuous Integration]
 
[http://www.geek-directeur-technique.com/post/2009/03/18/Tests-unitaires-et-integration-continue Continuous Integration]
 +
 +
 +
[[Category:Archived pages]]

Latest revision as of 14:41, 30 November 2013

Replacement filing cabinet.png
This page has been archived - Please Do Not Edit or Create Pages placed in this namespace. The pages in the Archived namespace exist only as a historical reference, it will not be improved and its content may be incomplete.

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.

Contents

Introduction

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

Tools and Environment

Purpose of this operation

Continuous integration

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

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

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 ?

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

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

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

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

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

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

Step 6: Make a report

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

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

Continuous Integration