Shooting for the Foot — and Missing

Today’s plan was to write the acceptance test for another story, and make it run. The best-laid plans …

The Next Story and Test

We began the session with a discussion of the previous article, especially the notion that we are good, for now, with the image processing side, and that we should proceed with the output and analysis aspects of the product. Not surprisingly, we’re on the same page on that.

So we needed to pick a new story. Chet wanted to start on reporting the shot density in a grid. He mentioned that the articles he has read show both a rectangular grid and a sectored grid, the latter apparently set up with equal-area wedges. We reflected that there were a couple of TDD’d spikes for both rectangular grids and wedges, and talked about what would make a fair test.

Chet had the paper target, so we looked at it, deciding that about a four-inch square was interesting, and we concluded (of course) that we’d have to manually grid the BMP file and count it there, not on the paper, since the BMP doesn’t match the paper. (It will, Kelly, it will. ;->)

We decided that we’d start with a rectangular grid of 4×4 inch squares, filling the 40 inch paper. We spoke briefly of using a TableFixture but Chet won the day with his theory that if something can’t be done with a ColumnFixture, it’s not worth doing.

We used our existing CreatePatternFromFile fixture, and a new fixture, RectangularDensity, with columns row, column, and count(), reflecting a cell in the grid and the number of hits found there. We’d count a few cells and make sure that the program agreed with our count. The fixture looks like this:

imageThe wiki code looks like this, of course:

!|com.hendricksonxp.patterning.fitnesse.CreatePatternFromFile|
|fileName|doIt()|
|pb270011.bmp| true|

!|com.hendricksonxp.patterning.fitnesse.RectangularDensity|
| row | column | count() |
| 0 | 0 |0 |
| 0 | 1 | 0 |
| 0 | 2 | 0 |
| 0 | 3 | 0 |
| 0 | 4 | 0 |
| 0 | 5 | 0 |
| 0 | 6 | 0 |
| 0 | 7 | 0 |

We have this new Fixture:

public class RectangularDensity extends ColumnFixture {
    public int row;
    public int column;

    public int index() {
        return row*8+column;
    }

    public int count() {
        int[] densities = CreatePatternFromFile.pattern.analyzeDensity(256,192);
        return densities[index()];
    }
}

But All Did Not Go As Planned …

I’ll not trouble you with all the details, but we got into quite a bit of trouble. The analyzeDensity method looks like this:

   public int[] analyzeDensity(int width, int height) {
        int[] result;
        int numX = this.width / width;
        int numY = this.height / height;
        result = new int[numX*numY];
        Rectangle rect = new Rectangle(width-1, height-1);
        int location = 0;
        for (int top = 0; top < this.height; top+= height) {
            for (int left = 0; left < this.width; left+=width) {
                rect.setLocation(left, top);
                result[location] = density(rect);
                location++;
            }
        }
        return result;
    }

This code was just a spike, you’ll recall, and it does pass its tests. But it isn’t very robust at all. In particular, if the width and height don’t evenly divide the picture’s width and height, it goes wild and gets exceptions trying to store into result locations that it hasn’t allocated. That’s why we have the test going for 256 and 192, so everything will come out even. But that’s not all. Look at the result:

imageWhat’s up with that? The upper left corner has data in it, and we’d expect little or none, and then the right side has nothing. Weird.

Until you remember that one of the clever things we did to the ShotPattern was to change its origin to the middle, because Chet thought he wanted it that way. So when we look for hits around (0,0), we’re really looking at hits in the upper right quadrant of the picture, near the center, not near the top. And then the search falls off the edge of the picture, after the first four squares, and there’s nothing there, so the second four are zero.

We wrote a JUnit test for the analyzeDensity, to help us figure out what was going on. Even then we actually entered the debugger once. The test looks like this:

   @Test
    public void density00() {
        pattern = new ShotPattern(folder + "pb270011.bmp");
        int[] densities = pattern.analyzeDensity(256, 1536/8);
        assertEquals(64, densities.length);
    }

Because of the odd rounding and so on, this test initially was using different inputs and looking for 100 density cells, since Chet wanted 10×10 4 inch squares. When we finally figured out that we needed an equal division, we verified it with this test and then adjusted the RectangularDensity fixture to match. Then we got the result you see in the FitNesse test, and realized what else was happening.

Assessment and Plan

Our spike code proves to us technically that we can calculate densities both rectangular and in sectors. But it’s not robust enough yet to support our real needs. Plus, we have sent ourselves some mixed messages about whether the grids should start in the upper left, or at the center.

We need to provide a gridded picture of the BMP file, so that the customer can count the cells and provide real values for our test. We deferred that today to get the framework in place, and we learned plenty — not much of it good — from doing that.

Someone posted somewhere recently about “Shibumi”, which they defined as “Effortless Perfection”. Not only is the book by Trevanian wonderful, but it’s a state of being that I’ve always wanted to attain. Today we felt confused too much of the time. So we definitely didn’t attain anything like “efforless perfection” today.

But then, after all, Shibumi is a place one is walking toward, not a place where one resides. So we observe that we were a bit clumsy today, reflect on what happened, and work to improve. Almost certainly, that will involve a return to basics, because that’s where you always start, back at the beginning, as well as practicing whatever move didn’t go well.

In the case of software the way we do it, that means clean code, well tested. So we have some cleanup and refactoring to do, in order to make our current customer test run and run well.

See you tomorrow!

Posted on:

Written by: Ron Jeffries

Categorization: Articles

Recent Articles