Home | About Us | Stelligent  

TestEarly Weblog

Brothers


Stelligent Incorporated
Code Metrics and /Brothers22 May 2008 09:46 am

GSparklineTest_test1.png
Everyone loves sparklines, and if you want to build them using Groovy, here’s some code that I took from a fabulous example in JRuby, and modified:

package com.stelligent.gsparkyimport org.jfree.chart.JFreeChart
import org.jfree.chart.axis.NumberAxis
import org.jfree.chart.plot.XYPlot
import org.jfree.chart.renderer.xy.StandardXYItemRenderer
import org.jfree.data.general.Dataset
import org.jfree.data.xy.XYSeries
import org.jfree.data.xy.XYSeriesCollection
import org.jfree.chart.ChartUtilities

class GSparky {

    def DEFAULT_HEIGHT = 30
    def DEFAULT_WIDTH  = 150

    boolean build( def data, def imgPath, def height = DEFAULT_HEIGHT, def width = DEFAULT_WIDTH ) {

        def chart = buildChartFromData(data)
        return buildImageFromChart( chart: chart, height: height, width: width, path: imgPath)
    }

    private JFreeChart buildChartFromData( data ) {

        def dataset = generateDataset(data)

        def plot = new XYPlot()
        plot.dataset = dataset

        plot.domainAxis =  minimalAxis()
        plot.rangeAxis = minimalAxis()

        plot.domainGridlinesVisible = false
        plot.domainCrosshairVisible = false
        plot.rangeGridlinesVisible = false
        plot.rangeCrosshairVisible = false
        plot.outlinePaint = null
        plot.renderer = new StandardXYItemRenderer(StandardXYItemRenderer.LINES)
        plot.insets = new RectangleInsets(-1, -1, 0, 0)

        def chart = new JFreeChart(null, JFreeChart.DEFAULT_TITLE_FONT, plot, false)
        chart.borderVisible = false

        return chart
    }

    private boolean buildImageFromChart( args ) {

        ChartUtilities.saveChartAsPNG( new File(args.path), args.chart, args.width, args.height )

        return true
    }

    private Object minimalAxis() {

      def a = new NumberAxis()
      a.tickLabelsVisible = false
      a.tickMarksVisible = false
      a.axisLineVisible = false
      a.negativeArrowVisible = false
      a.positiveArrowVisible = false
      a.visible = false;

      return a
    }

    private Dataset generateDataset(def data) {

        def series = new XYSeries("Sparkline")

        def i = 0
        data.each { y -> series.add(i++, y)  }

        def dataset = new XYSeriesCollection()
        dataset.addSeries(series)

        return dataset

    }
}

And.. a test to demonstrate how it works:

package com.stelligent.gsparky

class GSparklineTest extends GroovyTestCase {

    public void testBuildSparklineImage() {

        def gs = new GSparky()

        def data = [20]

        def r = new Random( new Date().getTime() )

        (0..99).each { x ->
           def y = data.get(x) + (x/2 - r.nextInt(x + 1))
           data << y
        }

        assert gs.build( data, "out/GSparklineTest_test1.png")

    }

}

*Update* - added a line to remove the grey border from around the graph.

Code Complexity and Publications and Agile and /Brothers12 May 2008 02:55 pm

I don’t always agree with Mr. Yegge, but this is a great presentation/script on Dynamic Languages with some very interesting ideas and discussion topics.

One point that he never actually got to, but one that I think is worth more discussion - how do you maintain a million-line codebase w/out static types?

My answer is that with a good dynamic language, you have some seriously elegant features and patterns that allow you to keep the size of your codebase small.  Essentially, you don’t maintain a million-line codebase - because it doesn’t take a million lines to write your application.

Which fits in with some of the agile concepts as well - if you are building a project in an agile style, the constant refactoring and removal of technical debt will keep the application smaller and lighter.  The unit tests ensure a clean level of separation of concerns, the DRY and YAGNI principles reduce bloat.

Developer Testing and /Brothers30 Apr 2008 02:21 pm

We had a proxy problem with Selenium RC today. The site-under-test is behind a proxy, and has the same domain name as the production site (let’s call it shop.mystore.com). Normally, developers set their proxy server in their browser, which then causes requests for shop.mystore.com to go to the proxy server which then sends the request to the developer site-under-test, instead of going to the production site.

We want to set up Selenium so the developers can created automated tests around visiting the site-under-test.

Selenium does have the ability to do a “double proxy”, using system properties (proxyPort elided for length):

java -Dhttp.proxyHost=proxy.server.com -Dhttp.proxyPort=3128 -jar selenium-server.jar

But it turns out, unfortunately, that this doesn’t seem to work in practice. After some investigation of my own, I discovered that the proxy configuration file (proxy.pac) in Selenium is not generated properly.  My own tests, and a timely visit to this discussion of the proxy chaining issue, it appears that there’s a simple workaround:

java -Dhttp.proxyHost=proxy.server.com ... -jar selenium-server.jar -avoidProxy

Setting -avoidProxy causes the proxy configuration (proxy.pac) to work properly, sending the selenium requests to the developer site-under-test.

*Update*

I have discovered there are additional steps that need to occur in some situations, especially when you want to use Chrome and/or SSL. Hopefully this will be fixed in the 1.0 release of Selenium.  Here are the additional steps for using SSL:

  1. Use the  chrome browser
  2. Start the Selenium Server programatically, instead of at the command line.  Yes, that means write code that calls the Selenium server and initializes the proxies, etc before you call the .start() method.
  3. BUT - There’s a bug in 0.92 (and possibly later) that means you can’t set the proxyHost and proxyPort programatically ( ! )

Here’s what you can do. I realize this is complicated.

  1. When you start up firefox or chrome via Selenium, it uses a special temporary profile directory to manage the proxy files and so forth.
  2. Start up Selenium from the command line, with the proxyHost and proxyPort values set.  Then find this profile directory.  Copy it to some other safe, easy-to-remember location.  We’ll call it “/var/tmp/SeleniumProxyHack
  3. Go back to your programmatic launcher for Selenium, and tell it to use /var/tmp/SeleniumProxyHack  as the Firefox Template directory.
  4. From my possibly faulty human recollection - you also need to modify another file in that directory, to tell it to look at the proxy.pac file in /var/tmp/SeleniumProxyHack, instead of wherever it looked before.

Here’s a snippet of code that I use to programatically start Selenium

// these don't work in *chrome in Selenium 0.9.2
System.setProperty("http.proxyHost", "proxy.dev.server.com");
System.setProperty("http.proxyPort", "3128");

seleniumServer = new SeleniumServer(portToUse);
seleniumServer.setProxyInjectionMode(false);
seleniumServer.setAvoidProxy(true);
seleniumServer.setReusingBrowserSessions(true);

String firefoxTemplateLocaton = "/var/tmp/seleniumProxyHack";

SeleniumServer.setFirefoxProfileTemplate(new File(firefoxTemplateLocation)); 
seleniumServer.start();

Yes, I wish there were an easier way as well. And no, I can’t guarantee this will work. But it did work properly for me in a fairly hairy case involving SSL and a fairly well-secured system.

Code Coverage and /Brothers23 Apr 2008 02:43 pm

This was discovered with Clover 2.2.1

We have a build.xml file with the following pseudo-flow:

  • clover-setup
    • // this causes all future compilations to use the clover compiler to instrument the files.
  • compile
    • javac _all_source_
  • test_A
    • javac _unit_test_source_ // yes, this is a subset of all source
    • junit _unit_tests_

  • test_B
    • javac _unit_test_source_ // yes, this is a subset of all source, and redundant with test_A
    • junit _integration_tests_
  • clover-report
    • NO DATA!

By inserting < clover-log / > at the end of test_A and test_B, I figured out what was going on.

  1. after test_A, we have a nice coverage level
  2. but at the end of test_B, we have been reset back to 0

My hypothesis is that there is a bug in Clover that causes it to:

a) wipe out all of the coverage data thus far when new code is compiled.

b) prevent new code from updating the coverage database.

And sure enough, by removing the javac compilation step from test_B, we have an accurate combined coverage report.

Moral of the story:

  1. Do all of your compiling before you run any of your automated tests
  2. < clover-log / > is your friend.
Build Management and Continuous Integration and /Brothers17 Apr 2008 12:23 pm

Let’s say you have a fairly complicated build structure, with multiple batch files that fire off different aspects of the build (even if, at the end of the day, they just call ant).

And let’s assume that for various historical reasons, these batch files encapsulate their environment variables using SETLOCAL and ENDLOCAL.

At first glance, this is a pretty cool way to scope out your environment variables. But then, you find out that Hudson determines if a build has succeeded or failed by looking at the %ERRORLEVEL% environment variable. If it is not 0, the build has failed.

Uh oh. Your build is failing, but the moment ENDLOCAL is called, %ERRORLEVEL% is set back to its previous value, which, most likely, will be 0.

Given this situation, how do you get the %ERRORLEVEL% back up to Hudson? Well, don’t call ENDLOCAL. Instead use:

EXIT %ERRORLEVEL%

at the end of your batch file. This works because there’s an implicit ENDLOCAL at the end of your batch file, and EXIT will return the ERRORLEVEL as the exit value.

Developer Testing and /Brothers11 Apr 2008 09:48 am

So you want to build a set of automated tests using Selenium against your webapp. I’m going to assume you have already figured out how to launch your app and set the database into a known state.

Now, you want to write some tests that mimic visiting the website and interacting with the UI in various ways.

I’ve been playing with Selenium quite a bit recently, and I’ve assembled a short list of things that will hopefully make other people’s lives easier.

Building your Tests : Navigation

  • Use Selenium IDE (Firefox Plugin) to develop the navigation flow for you. The last thing you want to be doing is debugging your navigation steps in your test case so you can get to the target. With Selenium IDE, you get your app running, and record the clicks and actions you take to get to your destination.
    • When you finish recording, you can export your test as a java program, or in several other languages. Take advantage of this!
  • To click on a link - if you assume you have a link like: < a xhref=" Your Text Here < / a > you can get the Selenium browser object to activate the link by issuing browser.click(”Your Text Here“);

Building your Tests: Validating Results

Selenium has XPath support, and XPath is a very powerful tool for processing HTML. But the syntax of XPath is quite arcane, and I spent waaaaaay too much time trying to decipher what was wrong with my XPath query.

  • Use XPath Checker (firefox plugin) - go to the results page, and control-click to bring up the XPath Checker browser. Then you can experiment with various XPath expressions until you get the one that works for your particular page and content.
  • To find if text appears anywhere on the page (also known as the quick-and-dirty method) - use assertTrue(browser.isTextPresent(”Your Text Here“));
  • To find if text appears in a specific sub-location, the easiest way I have found is:
    • assertEquals(”Text“, browser.getText(”//table[@id=’whatever’]//tr[2]/td[1]”));
    • Selenium is smart about pulling all of the text out of a set of cells, subtags, whatever, so even if that text was nested inside a div, a span and a link, it would still find it.

Running Your Tests

  • You can’t use Selenium inside your tests unless the Selenium RC Server is running - launch it before you start your tests, like you would Cargo, your database, etc.
  • It’s a proxy, so you have to make sure that the port you use in your code (default: 4444) is the same as the port that Selenium server is running on.

Hopefully this will help some future person get up to speed faster.

Continuous Integration and /Brothers24 Mar 2008 01:00 pm

Hello!

Andy has graciously invited me to post here at testearly.com, and I thought I would start off with some of my experiences with configuring Hudson. This is a crosspost from my blog, but I suspect it will get a lot more traffic here.

Let’s say you’re using Hudson as your build/Continuous Integration tool. And let’s assume you have some jobs running inside Hudson that you want to keep running, even if the build machine blows up. You probably want to maintain:

  • Hudson itself
  • All the plugins
  • The overall configuration
  • The per-job configuration

Naturally, then, your thoughts should turn to “How do I put the Hudson configuration into source control?” Here’s what you do:

  1. Make sure your builds are configured and working to your satisfaction, in a directory that I will from now on refer to as HUDSON_HOME.
  2. Copy the entire HUDSON_HOME directory tree to a temporary location called “versioned_build”
    1. In the versioned_build directory, you’ll find the jobs directory, and under that, a directory for each job.
    2. Inside each job directory, you’ll find configuration .xml files and other miscellaneous files, and you’ll find two subdirectories:
      • workspace
      • builds
    3. Empty those two subdirectories of all files, but do not delete the subdirectories.
  3. Repeat this “clean out” process for each job.
  4. import the entire “versioned_build” directory tree into source management.

Now, you have your Hudson configuration in source control. You can start it up, and assuming HUDSON_HOME is set right (see below), you should see your dashboard, and your jobs listed, and properly configured. Issues

  • You may have to manually kick off your jobs to “prime the pump”
  • Your build number will not start at 0 unless you do not archive the nextBuildNumber file
  • Your HUDSON_HOME environment variable may be incorrect for your machine (see below)

HUDSON_HOME Portability For ease of checkout and maintenance, I like the following directory setup: $HUDSON_HOME/

  • hudson/
    • hudson.war
  • jobs/
    • Your Hudson Jobs Here
  • plugins/
    • Your Hudson Plugins Here

Using this configuration, you can create a file in $HUDSON_HOME called, say, hudson.sh, which would look a little something like this:


#!/bin/sh
export HUDSON_HOME=.
export CVS_RSH=/usr/bin/ssh
java -jar hudson/hudson.war

Using this structure, and that hudson.sh script (I presume you can do something similar in Windows) gives you the following benefits:

  1. Your entire Hudson system, including the Hudson war file and the launcher script are all maintained as part of the repository.
  2. You don’t have to set HUDSON_HOME whenever you check the system out of source control - it’s already set by default to the current directory. As long as you run hudson.sh in its own directory, you’ll get the correct value for HUDSON_HOME

Learn from my mistakes!

  • Unless you absolutely must, don’t tell Hudson where to find Ant or the JDK. If they’re on your path, Hudson will find them on its own. If you set them for your build machine, chances are that on the checkout machine they won’t be in exactly the same place

WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1]
SELECT COUNT(DISTINCT ID) FROM