Saturday, September 14, 2013

Creating an excel (*.xls) file with apache.poi library

Apache poi provides a very clean api to create and manipulate microsoft documents.
Here an example of creating an xls file from a String having of format like below:
"column1";"column2";"column3" \n"line1value1";"line1value2";"line1value3"\n"line2value1";"line2value2";"line2value3"
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;

public class StringToExcelFileConverter {

    public static File convert(String csvString) {

        try {

            File xlsTempFile = File.createTempFile("temp" + System.nanoTime(), ".xls");
            FileOutputStream fileOut = new FileOutputStream(xlsTempFile);
            HSSFWorkbook workbook = new HSSFWorkbook();
            HSSFSheet worksheet = workbook.createSheet("detail");
            String separator = getSeparator();//like ";"  in above string
            String[] rowStrings = csvString.split("\n");
            for (int i = 0; i < rowStrings.length; i++) {
                HSSFRow row1 = worksheet.createRow((short) i);
                String[] columns = rowStrings[i].split(separator);
                for (int j = 0; j < columns.length; j++) {
                    HSSFCell cell = row1.createCell((short) j);
                    cell.setCellValue(columns[j].replace("\"", ""));
                    HSSFCellStyle cellStyle = workbook.createCellStyle();
                    cellStyle.setFillForegroundColor(HSSFColor.WHITE.index);
                    cell.setCellStyle(cellStyle);
                }
            }

            workbook.write(fileOut);
            fileOut.flush();
            fileOut.close();
            return xlsTempFile;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
 return null;
    }
}
Now you can open that file with following line if you have any application associated with the xls extension

Desktop.getDesktop().open(xlsFile);

Thursday, September 5, 2013

How to show stacktrace, standard output and standard error of each test failure in jenkins mail

Email-ext plugin in jenkins allows customizing jenkins build mail at a programmer's will.


Find you jelly script and go to JUnit TEMPLATE portion of the script.

Here's an example how you can change the script to show stacktrace, standard output and standard error.


 <!-- JUnit TEMPLATE -->  
 <j:set var="junitResultList" value="${it.JUnitTestResult}" />  
 <j:if test="${junitResultList.isEmpty()!=true}">  
  <TABLE width="100%">  
   <TR><TD class="bg1" colspan="2"><B>JUnit Tests</B></TD></TR>  
   <j:forEach var="junitResult" items="${it.JUnitTestResult}">  
    <j:forEach var="packageResult" items="${junitResult.getChildren()}">  
     <TR><TD class="bg2" colspan="2"> Name: ${packageResult.getName()} Failed: ${packageResult.getFailCount()} test(s), Passed: ${packageResult.getPassCount()} test(s), Skipped: ${packageResult.getSkipCount()} test(s), Total: ${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()} test(s)</TD></TR>  
     <j:forEach var="failed_test" items="${packageResult.getFailedTests()}">  
      <TR bgcolor="white">  
        <TD class="test_failed" colspan="1">  
          <B><li><h2>Failed: ${failed_test.getFullName()} </h2></li></B>  
        </TD>  
      </TR>  
      <TR>  
         <TD class="test_failed_log" colspan="2">  
          <li><h4>Stacktrace</h4></li><li>${failed_test.getErrorStackTrace()} </li>  
          <li><h4>Standard Output</h4></li><li>${failed_test.getStdout()} </li>  
          <li><h4>Standard Error</h4></li><li>${failed_test.getStderr()} </li>  
        </TD>  
      </TR>  
     </j:forEach>  
    </j:forEach>  
   </j:forEach>  
  </TABLE>  
 <BR/>  
 </j:if>  

You can further change the visual effects through css classes.
To know more options of the ${failed_test} object see CaseResult

Where are the Jelly and Groovy template scripts located in jenkins?

The Email-ext plugin page says the templates are located in $JENKINS_HOME_\email-templates_.
I searched a lot to find the html.jelly script as I wanted to make changes to the default.
But didn't find them with any search until I unzipped classes.jar in $JENKINS_HOME/plugins/email-ext/WEB-INF/.

Now to test your changes you have to change the jelly file and jar classes folder again and put in /lib folder.

There is an easier way. Move the classes.jar to WEB-INF folder of email-ext plugin and unzip it. Now you can make changes in the files and test them right away!

You can find a template in my other post How to show stacktrace, standard output and standard error of each test failure in jenkins mail

Thursday, August 29, 2013

Create Test Suite for Tycho after login

Sometimes tests fail when they are not actually running. This typically happens in case of integration-tests. When something goes wrong(like login failed) the consequent tests all get failed and the build server starts flooding with the infamous All tests failed! mails!

What to do in such case?
Should we run the tests if the login failed even if they were supposed to run after a successful login?

We faced this recently with our RCP application which connects to a remote server to properly function.
We have around 50 SWTBot ui-tests for testing the features. The tests are run in our CI server.
SWTBot first logs onto the server using a login dialog.
But when the login fails we don't want to run the tests at all, here's what we did:

We created a TestSuite which returns all tests only when login succeeds.
Of course it can be customized further to give more logic to load each test case (like different test cases for different platforms!)

package package.of.test.suite;

import junit.framework.JUnit4TestAdapter;
import junit.framework.TestSuite;

import org.apache.log4j.Logger;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;

/*
 * A test suite which creates tests depending on whether the login succeeded or not.
 * Return all tests if login succeeds
 * Else return a suite containing no tests.
 */
public class AllTests extends TestSuite {

    private static boolean loggedIn = initializeProductForTesting();
    private static Logger  mLogger  = Logger.getLogger(AllTests.class);
    private static SWTWorkbenchBot bot;
    public static boolean initializeProductForTesting() {
        System.out.println("in Beforeclass method before all tests!");
        Utils.login();
        return Utils.isLoggedIn();
    }

    public static TestSuite suite() {
        TestSuite suite = new TestSuite();
        if (loggedIn) {
            suite.addTest(new JUnit4TestAdapter(Test1.class));
            suite.addTest(new JUnit4TestAdapter(Test2.class));
            suite.addTest(new JUnit4TestAdapter(Test3.class));
        }
        else {
            mLogger.debug("Can't log into server!");
            mLogger.debug("No tests will run !");
            Display.getDefault().close();
            System.exit(0);
        }
        return suite;
    }
}

We configure tycho-surefire plugin to run this suite
<configuration>
                    ...
                    <testSuite>bundle.symbolic.name.of.test.plugin</testSuite>
                    <testClass>package.of.test.suite.AllTests</testClass>
                    ...
</configuration>

Tuesday, August 27, 2013

How to stop massive logging by Birt Chart

While config for birt report gives direct api(EngineConfig) to change logging level birt chart doesn't.
Here's an alternative
            PlatformConfig config = new PlatformConfig( );
            config.setProperty(PluginSettings.PROP_LOGGING_DIR, null);
            config.setProperty(PluginSettings.PROP_LOGGING_LEVEL, Level.SEVERE);

This should stop mass logging by birt chart.

Tuesday, August 20, 2013

How to cancel an SWT key event?

KeyEvent in SWT has a doit member variable.
Setting it to false cancels any further handling of the event.

public void keyPressed(KeyEvent e) {
              handleKeyPress(e);
              e.doit = false;// cancels the event
}

Sunday, August 18, 2013

SWTUtils doesn't support invoking methods with parameter

SWTUtils.invokeMethod allows you to invoke only methods with no parameters.

Here's a customized version of the method that allows you to invokes methods on objects with parameters

    public static Object invokeMethod(final Object object, String methodName,
            final Object[] params) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        Class[] paramTypes = getParamTypes(params);
        final Method method = object.getClass().getMethod(methodName,
                paramTypes);
        Widget widget = null;
        final Object result;
        if (object instanceof Widget) {
            widget = (Widget) object;
            result = UIThreadRunnable.syncExec(widget.getDisplay(),
                    new Result<Object>() {
                        @Override
                        public Object run() {
                            try {
                                return method.invoke(object, params);
                            } catch (Exception niceTry) {
                            }
                            return null;
                        }
                    });
        } else {
            result = method.invoke(object, params);
        }

        return result;
    }

Still this method only supports Object[] parameter, it doesn't support any primitive parameter like int, boolean.