Wednesday, November 20, 2013

SonarQube analysis with Jacoco Ant task and Sonar Ant task

SonarQube is a popular static code analysis tool. It has PMD, Findbugs and Checkstyle integrated by default. For code coverage, it uses Jacoco as the default. Most of the time in large projects which uses Ant as the build tool, It is easier to run SonarQube as a Ant task. Here I am going to describe the steps you need follow to get it done.

SonarQube can be configured to use many database system. Here I have used MySQL. If you want to use another database system such as Oracle you can follow the same procedure. 

First you need to install MySQL. Installation instructions can be found on http://dev.mysql.com/doc/refman/5.5/en/installing.html

Log on to mysql

mysql -u root -p

Enter password if there is a one, if you didn't set up a password during the installation just press enter.

You may need to delete the empty user first if you haven't done so yet.

DELETE FROM mysql.user WHERE User='';

Create a database to be used for Sonar. You can use any suitable name. I have used the name "SONARDB" here.

CREATE DATABASE SONARDB CHARACTER SET utf8 COLLATE utf8_general_ci;

Create a new database user SONARDB with password PASSWORD and grant all the privileges. Again you can choose any valid user name.

grant all privileges on SONARDB.* to 'SONARDB'@'%' identified by 'PASSWORD';


flush privileges;


You can download SonarQube using the following link. For commercial applications it will be better to use a LTS version. The version I have used here is 3.5.1 ( http://dist.sonar.codehaus.org/sonar-3.5.1.zip). But this procedure should work for newer version also (At least up to 3.7, I haven't tested for later versions).

http://www.sonarqube.org/downloads/


Go to conf folder and open sonar.properties file. Only thing we need to configure here is the database connection information.

Set sonar database username and password properties.

sonar.jdbc.username:                       SONARDB
sonar.jdbc.password:                       PASSWORD


Uncomment the following line and change the db name form sonar to SONARDB

sonar.jdbc.url:                            jdbc:mysql://localhost:3306/SONARDB?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true

Go to bin folder. You can see several folders with several platform names. Select the one most suitable for your platform. ( For CentOS 6.4 64bit, Linux 32 bit Sonar worked without a problem )

Start Sonar using following command.

            sh sonar.sh start

When the project is complex with several modules, it is easier to define sonar related configuration in separate files. Anyway for small projects you can define all the properties in the same build.xml. 

First create a file named sonar-main.xml. This file will be the root of all sonar related configuration files.

Download sonar-ant-task jar file using the following link and put it into the lib folder in your ant installation. sonar-ant-task-2.1.jar

Download jacoco-ant-task jar file using the following link and put it into the lib folder in your ant installation. http://www.eclemma.org/jacoco/

sonar-main.xml

<?xml version="1.0" encoding="US-ASCII"?>

<project name="sonar" default="sonar" xmlns:sonar="antlib:org.sonar.ant" >

    <property name="sonar.project.key" value="org.myproject"/>
    <property name="sonar.project.name" value="My Project"/>
    <property name="sonar.project.java.version" value="1.6"/>
    <property name="sonar.dir" value="${basedir}/target/sonar"/>
    <property name="sonar.host.url" value="http://localhost:9000"/>
    <property name="sonar.jdbc.url" value="jdbc:mysql://localhost:3306/SONARDB?useUnicode=true&amp;characterEncoding=utf8"/>
     <property name="sonar.jdbc.driverClassName" value="com.mysql.jdbc.Driver"/>
     <property name="sonar.jdbc.username" value="SONARDB"/>
      <property name="sonar.jdbc.password" value="PASSWORD"/>
     

    <target name="sonar" >

        <taskdef uri="antlib:org.sonar.ant" resource="org/jacoco/ant/antlib.xml">
            <!--&lt;!&ndash; Update the following line, or put the "jacocoant.jar" file in your "$HOME/.ant/lib" folder &ndash;&gt;-->
            <classpath path="path/to/sonar/ant/task/lib/sonar-ant-task.jar" />
        </taskdef>

        <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
            <!--&lt;!&ndash; Update the following line, or put the "jacocoant.jar" file in your "$HOME/.ant/lib" folder &ndash;&gt;-->
            <classpath path="path/to/jacoco/ant/task/lib/jacocoant.jar" />
        </taskdef>

        <property name="sonar.modules"
                  value="sdk/oranges/sonar-oranges.xml,sdk/apples/sonar-apples.xml"/>

        <!-- The workDir directory is used by Sonar to store temporary files -->
        <sonar:sonar key="${sonar.project.key}" version="${target.version}" xmlns:sonar="antlib:org.sonar.ant">

            <!-- SERVER ON A REMOTE HOST -->
            <property key="sonar.host.url" value="${sonar.host.url}"/>
            <property key="sonar.jdbc.url" value="${sonar.jdbc.url}"/>
            <property key="sonar.jdbc.driverClassName" value="${sonar.jdbc.driverClassName}"/>
            <property key="sonar.jdbc.username" value="${sonar.jdbc.username}"/>
            <property key="sonar.jdbc.password" value="${sonar.jdbc.password}"/>

            <property key="sonar.java.source" value="${sonar.project.java.version}"/>
            <property key="sonar.java.target" value="${sonar.project.java.version}"/>

            <property key="sonar.build-stability.url" value="${sonar.host.url}"/>

            <property key="sonar.projectName" value="${sonar.project.name}"/>

            <property key="sonar.core.codeCoveragePlugin" value="jacoco" />
            <property key="sonar.jacoco.reportPath" value="target/jacoco.exec" />

            <property key="sonar.dynamicAnalysis" value="reuseReports"/>

        </sonar:sonar>
    </target>

</project>


In each module, a xml file should be created with details about module name, locations of sources, binaries, test reports and other required properties.

File name : sonar-oranges.xml
Path  : sdk/oranges/sonar-oranges.xml

<?xml version="1.0" encoding="US-ASCII"?>

<project name="Oranges" default="all" basedir=".">

    <property name="sonar.java.source" value="1.6"/>
    <property name="sonar.java.target" value="1.6"/>
    <property name="sonar.projectKey" value="org.myproject"/>
    <property name="sonar.projectName" value="Oranges"/>
    <property name="sonar.sources" value="src/share"/>
    <property name="sonar.binaries" value="target/layout/WEB-INF/classes"/>
    <property name="sonar.tests" value="src/test"/>
    <property name="sonar.surefire.reportsPath" value="target/test/report" />
    <path id="lib.path">
        <fileset dir="../../target/layout/core/lib">
            <include name="**/*.jar"/>
        </fileset>
        <fileset dir="../../target/layout/tomcat/lib">
            <include name="**/*.jar"/>
        </fileset>
    </path>
    <pathconvert property="sonar.libraries" pathsep="," refid="lib.path" targetos="unix"/>

</project>


sonar.sources property should point to the directory where source files belonging to the module located.

sonar.binaries should point to the directory where .class files belonging to the module located.

sonar.tests should point to the directory where Junit test files located.

sonar.surefire.reportsPath property should point to the directory where Junit xml test reports are created after executing the test cases. Sonar uses these reports to display details of test execution in the dashboard.

A target should be defined in the main build.xml file pointing to  sonar-main.xml file.

<target name="run-sonar" depends="sonar-clean">
    <ant antfile="sonar-main.xml" target="sonar"/>
</target>



To analyse code coverage with Jacoco, it is required to run Unit tests with Jacoco agent. There are two methods to add Jacoco agent to the unit tests. 

Method 1: 

If you plan to run Jacoco agent every time units tests are run, this method can be used. 

<!-- Run your unit tests, adding the JaCoCo agent -->
<jacoco:coverage destfile="target/jacoco.exec" xmlns:jacoco="antlib:org.jacoco.ant">
    <junit fork="yes" dir="${basedir}" failureProperty="test.failed">
        <classpath location="${classes.dir}" />
        <classpath refid="classpath" />
        <formatter type="xml" />
        <batchtest todir="${reports.junit.xml.dir}">
            <fileset dir="${test.dir}">
                <include name="**/*Test.java" />
            </fileset>
        </batchtest>
    </junit>
</jacoco:coverage> 


Method 2: 

If you do not want to run Jacoco agent every time the tests are running, you can use this method. This method will be more useful than the earlier method, because running Jacoco agent will increase the running time of test cases.


First you use the value of run-coverage property to decide whether or not to pass Jacoco agent as a JVM argument.

<if>
    <equals arg1="${run-coverage}" arg2="true"/>
    <then>
        <jacoco:agent property="agentvmparam" destfile="target/jacoco.exec"
                              xmlns:jacoco="antlib:org.jacoco.ant"/>
    </then>
    <else>
        <property name="agentvmparam" value=""/>
    </else>
</if>


If the run-coverage property is set to true, Jacoco agent will be passed as a JVM argument. Otherwise it will pass a empty value for agentvmparam.


<junit fork="yes" dir="${basedir}" failureProperty="test.failed">
                       
    <!-- Run unit tests, adding the JaCoCo agent -->
    <jvmarg line="${agentvmparam}"/>
    <classpath location="${classes.dir}" />
    <classpath refid="classpath" />
    <formatter type="xml" />
    <batchtest todir="${reports.junit.xml.dir}">
        <fileset dir="${test.dir}">
            <include name="**/*Test.java" />
        </fileset>
    </batchtest>
</junit>

You can set the parameter value only when running test cases to collect coverage data by using "antcall".

<target name="run-coverage">             
    <antcall target="test">
        <param name="run-coverage" value="true"/>
    </antcall>
</target>



Wednesday, November 13, 2013

Jenkins Testlink Integration

What is Jenkins

Jenkins is a popular open source continuous integration tool written in Java. You can find more information form http://jenkins-ci.org/.



What is TestLink

TestLink is an open source test management system written in PHP. If you need you can find more information at http://testlink.org/.


Jenkins TestLink Integration

We can use Jenkins TestLink plugin to integrate Jenkins and TestLink  products. If we run unit tests in a Jenkins build job we can use the plugin to publish unit test results in TestLink. Following tutorial explain how to do that https://wiki.jenkins-ci.org/download/attachments/753702/jenkins.pdf

But sometimes it is not practical to run unit tests in a Jenkins build. In those situations we enter the test execution data manually to TestLink and then configure the plugin to read those information form there. We can configure Jenkins to pass, fail or mark the build as unstable depending on the test results read from TestLink.

First you have to install Jenkins TestLink plugin. To do that

1) Go to Manage Jenkins > Manage Plugins
2) Click the Available tab
3) Select Jenkins TestLink Plugin
4) Click Install without restart button.

Then you have to configure Build Job to fetch test results form Testlink.

1) Click on the Job name you want to configure and go to Configure.
2) Under Build, click Add action button and select Invoke TestLink action.



3) Fill the fields according to your Testlink configuration. You have to specify the Build Name you need to fetch test results from.
4) Click Advanced button under Test Execution. Here you can configure whether it is required to fail the build when test cases are failed or not.


You can find out how to configure Testlink by reading Jenkins Testlink Plugin Tutorial ( https://wiki.jenkins-ci.org/download/attachments/753702/jenkins.pdf.)  I have used following Jenkins and Testlink versions in this post.

Jenkins Version   : 1.536
TestLink Version : 1.9.8
Jenkins Testlink Plugin Version : 3.7