Monday, July 23, 2007

Testing BIRT

I'm a huge fan of TDD. Recently, I had to write tests for BIRT, specifically for a bug we've stumbled upon in BIRT 2.1 that has been fixed in 2.2: Page breaks in tables.

The first step was to setup BIRT so I can run it from my tests.

public IReportEngine getEngine () throws BirtException
    EngineConfig config = new EngineConfig();
    config.setLogConfig("/tmp/birt-log", Level.FINEST);
    // Path to the directory which contains "platform"
    PlatformConfig pc = new PlatformConfig ();
    PlatformFileContext context = new PlatformFileContext(pc);
    IReportEngineFactory factory = (IReportEngineFactory) Platform
    if (factory == null)
 throw new RuntimeException ("Couldn't create factory");
    return factory.createReportEngine(config);

My main problems here: Find all the parts necessary to install BIRT, copy them to the right places and find out how to setup EngineConfig (especially the platform part).

public void renderPDF (OutputStream out, File reportDir,
        String reportFile, Map reportParam) throws EngineException
    File f = new File (reportDir, reportFile);
    final IReportRunnable design = birtReportEngine
    //create task to run and render report
    final IRunAndRenderTask task = birtReportEngine
    // Set parameters for report
    //set output options
    final HTMLRenderOption options = new HTMLRenderOption();
    //run report;

I'm using HTMLRenderOption here so I could use the same code to generate HTML and PDF.

In my test case, I just write the output to a file:

public void testPageBreak () throws Exception
    Map params = new HashMap (20);
    File dir = new File ("tmp");
    if (!dir.exists()) dir.mkdirs();
    File f = new File (dir, "pagebreak.pdf");
    if (f.exists())
 if (!f.delete())
     fail ("Can't delete "+f.getAbsolutePath()
         + "\nMaybe it's locked by AcrobatReader?");
    FileOutputStream out = new FileOutputStream (f);
    ReportGenerator gen = new ReportGenerator();
    File basePath = new File ("../webapp/src/main/webapp/reports");
    gen.generateToStream(out, basePath, "sewingAtelier.rptdesign"
        , params);
    if (!f.exists())
 fail ("File wasn't written. Please check the BIRT logfile!");

Now, this is no test. It's only a test when it can verify that the output is correct. To do this, I use PDFBox:

    PDDocument doc = PDDocument.load(new File ("tmp", "pagebreak.pdf"));
    // Check number of pages
    assertEquals (6, doc.getPageCount());
    assertEquals ("Error on page 1",
            "...\n" + 
            "...\n" +
            , getText (doc, 1));

The meat is in getText():

private String getText (PDDocument doc, int page) throws IOException
    PDFTextStripper textStripper = new PDFTextStripper ();
    String s = textStripper.getText(doc).trim();
    Pattern DATE_TIME_PATTERN = Pattern.compile("^\\d\\d\\.\\d\\d\\.\\d\\d\\d\\d \\d\\d:\\d\\d Page (\\d+) of (\\d+)$", Pattern.MULTILINE);
    Matcher m = DATE_TIME_PATTERN.matcher(s);
    s = m.replaceAll("23.07.2007 14:02 Page $1 of $2");
    return fixCRLF (s);

I'm using several tricks here: I'm replacing a date/time string with a constant, I stabilize line ends (fixCRLF() contains String.replaceAll("\r\n", "\n");) and do this page by page to check the whole document.

Of course, since getText() just returns the text of a page as a String, you can use all the other operations to check that everything is where or as it should be.

Note that I'm using MockEJB and JNDI to hand a datasource to BIRT. The DB itself is Derby running in embedded mode. This allows me to connect to directly a Derby 10.2 database even though BIRT comes with Derby 10.1 (and saves me the hazzle to fix the classpath which OSGi builds for BIRT).

protected void setUp () throws Exception
    Context ctx = new InitialContext();
    EmbeddedDataSource ds = new EmbeddedDataSource ();

    ctx.bind("java:comp/env/jdbc/DB", ds);

protected void tearDown () throws Exception


No comments: