Added by Simon Brandhof, last edited by Simon Brandhof on Jul 01, 2009  (view change)

Labels:

Enter labels to add to this page:
Wait Image 
Looking for a label? Just start typing.

Best practices

Developers of Sonar core should :

  • build with maven before commiting to subversion
  • commit frequently into subversion
  • never commit incomplete changes (Subversion is not a backup system)
  • use meaningful comments when doing a subversion commit, always with the JIRA key and title (e.g. "SONAR-6 Apply license LGPL")
  • subscribe to the mailing list dev@sonar.codehaus.org
  • overuse unit tests (JUnit 4), with the help of hamcrest matchers and Mockito(mock objects)
  • test on MySql AND Derby
  • think IoC (Inversion of Control pattern)
  • overuse functional tests (Selenium). Tests are written in the Selenese format (HTML).
  • don't reinvent the wheel : use standard libraries like commons-lang, commons-io and google-collections (commons-collections is deprecated from Sonar 1.10)
  • use comments and javadocs only when necessary
  • do not forget the license header on all Java and Ruby files
  • respect the code formatting described below

Code formatting

  • replace tabulations by 2 whitespaces
  • right margin at 140 columns
  • classes with acronyms are CamelCase : for example prefer PmdPlugin to PMDPlugin, or JavaNcssHandler to JavaNCSSHandler
  • follow this template :
    if (i == 0) { // whitespace around operators
        doThis();
    } else {
        doThat();
        andThat();
    }
  • replace :
    if (i == 0) doThis();

    by :

if (i == 0) {
  doThis();
}

Tips and tricks

Run the Sonar Server with Jetty in dev mode

Run this command on the console, at the root of the sonar-web module:

mvn jetty:run

If you want to force Derby as datastore:

mvn jetty:run -Psonar-derby

To run Sonar on a clean Derby instance:

mvn clean jetty:run -Psonar-derby

When the Sonar server is running in dev mode, in order to collect metrics you need to execute the following command (change the plugin version with your current version)

mvn org.codehaus.sonar:sonar-maven-plugin:1.5-SNAPSHOT:sonar -Dsonar.host.url=http://localhost:9000/dev

Connect to Derby from command line

  • download derby(the same version as used by sonar)
  • set the DERBY_HOME property
  • execute the command $DERBY_HOME/bin/ij
  • connect to the database :
    ij version 10.3
    ij> connect 'jdbc:derby://localhost:1527/sonar;user=sonar;password=sonar';
    ij> show tables;

Compare XML files in JUnit

Use XMLUnit. The dependency is already defined into maven.

import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;

XMLUnit.setIgnoreWhitespace(true);
 Diff diff = XMLUnit.compareXML(xml1, xml2);
assertTrue(diff.similar());

Warning : There's a bug on the similar() method. Contrary to the documentation, it fails if nodes are not in the same order.

Override Object.equals(), hashCode() and toString()

You can use EqualsBuilder and HashCodeBuilder from commons-lang to simplify. Just define the fields to compare. Example on the Rule class :

public boolean equals(Object obj) {
    if (!(obj instanceof Rule)) {
      return false;
    }
    if (this == obj) {
      return true;
    }
    Rule other = (Rule) obj;
    return new EqualsBuilder()
        .append(pluginName, other.getPluginName())
        .append(key, other.getKey())
        .isEquals();
  }

  public int hashCode() {
    return new HashCodeBuilder(17, 37).
        .append(pluginName)
        .append(key)
        .toHashCode();
  }

public String toString() {
    return new ToStringBuilder(this)
        .append("id", id)
        .append("name", name)
        .append("key", key)
        .append("configKey", configKey)
        .append("categ", rulesCategory)
        .append("plugin", pluginName)
        .append("params", params)
        .toString();
  }

Debug the Maven plugin from an IDE

  • Two different ways :

1. Execute Maven debug command line : mvnDebug

$ mvnDebug org.codehaus.sonar:sonar-maven-plugin::sonar
Preparing to Execute Maven in Debug Mode
Listening for transport dt_socket at address: 8000

2. Or edit $MAVEN_OPTS with :

export MAVEN_OPTS="-Xmx768m -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"

    Then execute Maven. It should block on :

$ mvn org.codehaus.sonar:sonar-maven-plugin::sonar
Listening for transport dt_socket at address: 8000
  • Attach your IDE to the remote process (the listening port is 8000).

Example on Intellij Idea : Run -> Edit configurations -> Add new configuration -> Remote -> port 8000.

Execute Selenium tests

  • From firefox :
    • install the SeleniumIDE 1.0b2 extension
    • open the Selenium sidebar (View >> Sidebar >> SeleniumIDE)
    • uncheck the option "Remember base URL" from Selenium IDE >> Options >> Options
    • browse to Sonar (for example http://localhost:9000)
    • open a selenese test from tests/integration/src/it/selenium/
    • run it
  • Or from maven :
    • cd tests/integration
    • mvn install -P[browser]                # with [browser] = firefox or ie or safari

Use JUnit4 advanced features

  • assertThat() over assertTrue() :
    assertThat(x, is(3));
    assertThat(x, is(not(4)));
    assertThat(responseString, either(containsString("color")).or(containsString("colour")));
    assertThat(myList, hasItem("3"));

    Stacktraces are more readable :

    assertThat(responseString, anyOf(containsString("color"), containsString("colour")));
    
    // ==> failure message:
    // java.lang.AssertionError:
    // Expected: (a string containing "color" or a string containing "colour")
    // got: "Please choose a font"

    instead of  :

    assertTrue(responseString.contains("color") || responseString.contains("colour"));
    // ==> failure message:
    // java.lang.AssertionError
  • use hamcrest extensions :
    assertThat(someInteger, lessThan(500));
    assertThat(someInteger, greaterThanOrEqualTo(500));
    assertThat(someString, equalToIgnoringCase("toto"));

REST API

  • Ressources url is : http://\[SERVER\]/api/\[RESOURCE\]/\[ID\]/\[RESOURCE\]/...
    Exemple : http://\[SERVER\]/api/projects/org.sonar.samples:main-sample/measures
  • Parameters are :
    • written in lowercase
    • don't contains "-", "_"
    • the boolean use "true", "false"
  • In Xml, lists are implied.
    Exemple for project links:
    ...
    <link>
       <type>scm</type>
       <name></name>
    <url>[http://svn.sonar.codehaus.org]</url>
    </link>
    <link>
       <type>scm_dev</type>
       <name></name>
       <url>scm:svn:[http://svn.codehaus.org/sonar/trunk/samples/main-sample]</url>
    </link>
    ...
  • In Json, all content is between "[]", and lists are between "[]"
    Exemple :
    [
    	{
    		"key": "org.sonar.samples:main-sample",
    		"name": "Sonar main sample",
    		"qualifier": "JAV",
    		"links": [
    			{
    				"type": "scm",
    				"name": "scm",
    				"url": "http://svn.sonar.codehaus.org"
    			},
    			{
    				"type": "scm_dev",
    				"name": "scm_dev",
    				"url": "scm:svn:http://svn.codehaus.org/sonar/trunk/samples/main-sample"
    			}
    		]
    	}
    ]