www.BrettDaniel.com

Siebel Center Igloo (Update 2)

Nine friends1 and I built an enormous igloo in the courtyard between Siebel Center and the NCSA building.

Igloo with Siebel Center in the background (Yun Young's picture) Siebel center sculpture and igloo

How enormous? It is nearly eight feet tall and eight and a half feet in diameter. The doorway is four feet high and two wide. All 10 of us could easily fit inside. We estimate that it took over 80 man-hours to complete: we worked from 5 to 8 PM on Friday, then from 4 PM Saturday to 5 AM (!!!) Sunday. In that time, the temperature never got above 15° F.

It was a team effort. Yun Young and Alejandro deserve credit for initiating the construction and for their inspiring enthusiasm. Ellick documented the construction process with over 600 pictures (some of which I have used with his permission here). Nathan, being the tallest, was instrumental in completing the roof. Jeff O., Kevin, and Alejandro formed an efficient snow-brick-making machine. Lucas, Jeff D., and Jeremy provided much-needed reinforcement when they arrived near the end of construction. Finally, I helped for a few hours on the first night, then observed, supported (read: bought pizza), and provided unsolicited engineering advice on the second night.

The construction team admiring the progress (Ellick's picture)

None of us expected it would turn out so big or take so long to complete. Construction was completely unplanned and proceeded organically by trial and error. We eventually converged on the following process: pack snow into bricks, lay the bricks, pack the gaps with snow, smooth the edges, and spray water on the surface to strengthen the walls.

Laying the first bricks to form the wall Nathan and Yun Young mortaring the partially constructed wall with Alejandro and Kevin making bricks

We created bricks by packing snow into two small trash cans. It was difficult to form the dry, powdery snow into solid bricks. Our first attempts did not turn out very well.

The first unsuccessful brick The first unsuccessful brick

However, Alejandro realized he could use one trash can to compress the snow inside the other. After that, he became the brickmaking expert. He could produce solid bricks that slid out of the molds like muffins out of a muffin tin. Later, he found we could alternate the wide and narrow ends of the bricks to make the joints flush.

Using one trash can to compress a snow brick in the other trash can

At the end of the first night, we had three uneven layers of bricks. But there was a problem: they rose straight up, not inward as is needed to form the roof of a proper igloo. We seriously considered simply making an open snow fort. However, after adjusting the pattern of bricks on the second night, the walls started curving inward.

The unfinished spiraling wall near the end of the first day Starting the curve of the roof (Ellick's picture)

The next challenge was the doorway. Compressed snow has zero tensile strength (as we learned through bitter experience), but somehow we were able to make a lintel by forming a rough stairstepped arch.

Kevin and Yun Young inside the unfinished igloo (Ellick's picture)

The walls sloped more and more precariously.

Alejandro inside the unfinished igloo (Ellick's picture) Kevin mortaring the bricks (Ellick's picture) Yun Young smoothing the outside wall (Ellick's picture)

A small opening remained at the peak of the igloo. This was the most difficult part because it was too large for a single brick and the surface was too steep to hold more bricks. Yun Young and Kevin furiously packed handfulls of snow around the edge, spraying many bottlesful of water to make sure the snow stuck. Eventually, the hole shrunk enough that Nathan and Alejandro could jam three bricks together to form a peak at the top of the igloo. They filled the remaining space with rubble.

Plugging the hole in the roof (Ellick's picture) Plugging the hole in the roof (Ellick's picture) Plugging the hole in the roof (Ellick's picture) Plugging the hole in the roof (Ellick's picture)

The igloo was enclosed! The final step was smoothing the inside and outside surfaces and reinforcing joints with snow and water.

Lucas and Jeremy smoothing the roof (Ellick's picture)

Frozen to the bone and exhausted, we collapsed on the couches inside Siebel Center.

Recovering

Here is an overhead view of how the igloo came together.

Work crew (4:38 PM Saturday, January 9, 2010) Progress from above (Ellick's picture. 6:08 PM Saturday, January 9, 2010) Progress from above (Ellick's picture. 8:22 PM Saturday, January 9, 2010) Progress from above (Ellick's picture. 11:18 PM Saturday, January 9, 2010) Progress from above (Ellick's picture. 12:29 AM Sunday, January 10, 2010) Progress from above (Ellick's picture. 1:23 AM Sunday, January 10, 2010) Progress from above (Ellick's picture. 4:03 AM Sunday, January 10, 2010) Progress from above (Ellick's picture. 5:18 AM Sunday, January 10, 2010)

The next morning, Yun Young, Kevin, Ellick, and I visited the igloo in daylight. We met a group of students who complimented us on the project. Throughout the day we observed many other people stopping to look and take pictures.

The igloo is very popular with students walking through the courtyard

We are all incredibly proud with what built and hope that others who pass by Siebel Center will enjoy it for however long it lasts.

Tree and igloo The igloo's room number: 19100 Siebel Center with signatures of the construction crew.

You can see more pictures in the gallery, Yun Young's Facebook album, or Kevin's Picasa album.

Update (January 12, 2010)

The igloo has been extremely popular. We saw a steady stream of visitors all day yesterday, and our guestbook already has several pages of signatures. It even caught the attention of the local news. WILL radio and WCIA both briefly mentioned the igloo, and WICD, the local ABC affiliate, interviewed Alejandro and Yun Young.

Thanks to Jeff O. for posting the video.

Update 2: Aftermath (January 15, 2010)

A full day of above-freezing temperatures and light rain proved too much for the igloo. It had noticeably shrunk, and the sides had been bulging outward all day. It finally collapsed yesterday evening around 6:30 PM.

Aftermath (6:30 PM January 14, 2010)

Judging from the orientation of the debris, it looks like the walls exploded outward and roof fell straight down onto the floor. Only the right side of the door frame remained standing.

Aftermath (6:30 PM January 14, 2010)

Yun Young, Alejandro, and I paid our last respects and rescued the guestbook, which had been buried under the pile of snow.

Aftermath (6:30 PM January 14, 2010)

Here are some of the best quotes from the guestbook.

You've brought joy to many.
—A.C.

Amazing – I'm tempted to skip work today and just chill in your igloo
—A.B.

This is what the Blue Waters building should be. I love it.
Bill

Sweet! will you come to my house & build one for my kids?
—Cynthia

Get back to work!
—Your Advisor

You have a marvelous island of quiet in the busy world & it was great fun watching you build it
—Stuart

I've been trapped outside for the weekend.
Thank you for a shelter from the wild beasts.
The end was near the light shines through.
Godspeed to all who may encounter this oasis of warmth.
—Anon.

Great igloo. How about an ice pane window? We love it.
—Erin and Spencer

So proud to be an alum of UI!
—Dr. Pam

Is this igloo internet accessible too?! [It was! —ed.]
—Jill & Tom

This is a real gift to our community . Many people have stopped to enjoy your creation. Thank you!
—Mark & Teddy

Wow this is one of "the seven wonders of UIUC"
—Kim

Too cool for school
—Rahul

Victoria needs to sleep
—Anon.

YOU HAVE BEEN 4CLOZED!
—THE BANK

I think the best epitaph comes from Rajhans via Facebook:

This igloo is not just a shelter; no sir for that purpose has been thwarted by modern architecture. It is actually an icon of something very very special - the perseverance which grad students show in making something completely useless yet incredibly beautiful. Its a symbol of all the free time grad life provides and the crazy ideas it fills us with; ideas of finding ways of killing that free time. Who invented Ping Pong - I am sure it was a student of Confucius. Who created Olympics - the grad student mentioned somewhere in Illiad. Gustave Eiffel was a grad student too! And so, dear Yum Yum [Yun Young's nickname. —ed.], this Igloo is not anyone's baby - its bigger than a baby or for that matter, any of us. It is indeed one of the foremost exemplar of the primary grad student trait I have so painstakingly just described.

Goodbye, Siebel Center igloo. You were a testament to friendship, teamwork, and determination despite cold and discomfort. While you may have at first appeared completely useless, you brought joy to everyone who passed by.

Notes

  1. Alejandro, Yun Young, Kevin, Nathan, Ellick, Jeff O., Lucas, Jeff D. and Jeremy

Then and Now

Then and now: me proudly holding a Lego model in 1991 and 2009.

Then: Me proudly holding a Lego model in 1991 Now: Me proudly holding a Lego model in 2009

Previously

Related

New Zealand

When I told people I was going to New Zealand to present my ReAssert paper, nearly everyone asked if I was planning to travel around the country. Absolutely. One does not visit New Zealand for a three-day conference, then immediately turn around and go home. I stayed for 10 days. During that time, I attended the conference, toured Auckland, drove all over the south island with Yun Young and Danny, and saw more interesting things than I could possibly fit into one weblog post.

I considered writing a travel report like my Yellowstone or California posts, but the length of this trip made me feel that a different approach was necessary. The map below shows the path I/we took through New Zealand. Clicking it will take you to an annotated, interactive map with descriptions and pictures of the interesting places.

Annotated map of where we traveled. See the interactive version for details.

I posted more pictures in the gallery (see also Yun Young's Facebook albums). Here are a few of my favorites:

Auckland skyline Panorama of Auckland from One Tree Hill. The Sky Tower is to the left and Rangitoto Island is far in the background on the right. River and snow-covered mountain Harrison River with Mt. Pembroke in the background Panorama from the vista point overlooking Lake County and Arrowtown Danny and Yun Young taking pictures on the vista point overlooking Lake County View toward mountains from Gillespie's Beach Punakaiki panorama

explain: Short Documentation for Unix Commands

Here is a scenario that may sound familiar to Linux users: say you want to do something in a terminal but don't know the appropriate command. A quick web search yields a number of possible solutions, but every one is a long, impenetrable command with several unexplained flags. One should avoid running unknown commands that may do something malicious (e.g. "rm -rf ~"), but it is difficult to search through each command's documentation to find the meaning of all of the flags.

I wrote a small utility program that addresses this problem. The utility, called explain, prints short documentation for a given command string, including the documentation for all flags passed to the command.

For example, say you want to copy a file only when it is newer than the destination file. This superuser.com thread suggests the following commands:

cp --update src dest
or
rsync -avz /from/where/ /to/dest/

explain shows what both of these commands actually mean:

$ explain cp --update src dest
cp - copy files and directories
    -u, --update
        copy only when the SOURCE file is  newer  than  the  destination
        file or when the destination file is missing
$ explain rsync -avz /from/where/ /to/dest/
rsync - copying tool
    -a, --archive
        This  is equivalent to -rlptgoD. It is a quick way of saying you
        want recursion and want to preserve almost everything  (with  -H
        being  a  notable  omission).   The  only exception to the above
        equivalence is when --files-from is specified, in which case  -r
        is not implied.
    -v, --verbose
        This  option increases the amount of information the daemon logs
        during its startup phase.  After the client connects,  the  dae‐
        mon’s verbosity level will be controlled by the options that the
        client used and the "max verbosity" setting in the module’s con‐
        fig section.
    -z, --compress
        With  this  option, rsync compresses the file data as it is sent
        to the destination machine, which reduces  the  amount  of  data
        being transmitted — something that is useful over a slow connec‐
        tion.

This documentation is simpler than a command's full man page, provides a more focused view of only the flags that one cares about (including their synonyms), and makes it easier to compare different commands. Thus, explain is complementary to existing Unix utilities like man, whatis and apropos.

To get explain, you can download its source code or clone its Mercurial repository. The bundled INSTALL file contains installation instructions.

Update from the comments: internally explain parses the command's man(1) page and searches for sections that look like flag documentation. This is somewhat challenging because the file contains no semantic markup and there is no "standard" format, just ad-hoc suggestions on what it should contain. These two factors are probably part of the reason why a utility like explain doesn’t already exist.

explain currently handles just one command at a time. I hope to improve the utility by explaining more complex commands with sub-commands, pipes, and redirects. I would also like it to explain non-flag arguments such as the source and destination arguments in the examples above. Finally, certain common commands use nonstandard flags that explain could probably handle as special cases. For example, it would be good if explain could show documentation for commands like "java -Xmx1024M" or "chmod +x". If you have any other feature requests or if you find any bugs, please post them on the issues page.

Over the past week, I have used explain at least once per day. I hope others find it useful, too.

Weekend Road Trips

Even though most of my time is taken up by graduate school and medical appointments, I recently managed to find time to travel. I spent each of the past four weekends visiting friends all over the Midwest. First I helped Charlie celebrate his birthday in Louisville, Kentucky. Then, Michael had a bachelor campout—like a bachelor party with less debauchery and more trees—at Stuart's family's farm in Ohio. The following weekend, several UIUC friends and I visited Josh at his new graduate school home in Nashville, Tennessee. Finally, last weekend Michael and Alice got married in northwestern Indiana.

Fall  road trips

Each trip could fill its own post, but for now I will just describe one particularly noteworthy event from each.

Charlie and the Belly Dancers

Last year I helped Charlie celebrate his birthday in Montana. This year he came to Louisville, Kentucky, where he had spent several years at the University of Louisville. He visited several UofL and high school friends who were in the area. I drove south from Indianapolis to meet them there.

Charlie had his birthday dinner at a restaurant in the blocks-long entertainment district of downtown Louisville. The restaurant was decorated with faux-Greek wall hangings and pottery. The entertainment as well as the food also had a Mediterranean flavor. About halfway through the meal, three belly dancers in sequined dresses began dancing through the aisles, playing castanets and hand drums. It wasn't salacious dancing—it was a family restaurant, after all—just surprising and pleasant entertainment.

Charlie, knowing, it seems, everyone in town, recognized one of the dancers. I mentioned this and the fact that it was Charlie's birthday to the server. She in turn told the dancers, who came to the table and danced around Charlie. Afterward, the head dancer got all the other restaurant patrons to wish Charlie a happy birthday. It is difficult to embarrass Charlie, but I think that experience came close.

Michael's Bachelor Camping Trip

Two weeks prior to his wedding (which I write about below), Michael had his bachelor party. Unlike most bachelor parties, we went camping. Stuart offered his family's farm as the destination. It had a beautiful man-made lake surrounded by woods and cornfields.

Michael by lake Todd stuart michael and lake

We did normal campout activities like sit around the campfire roasting hot dogs, but I also got to do something I had never done before: shoot a gun. Before leaving for the farm, Michael's father gave us a small 22-caliber target rifle and an old mailbox. He urged us to destroy the mailbox. We placed it by an earthen berm next to one bank of the lake and did just that.

Me shooting an old mailbox Holy mailbox

It was deeply satisfying to hear the loud "ping!" when one hit the mailbox. We noticed that some of the bullets left trails along the edge of the mailbox or failed to make it all the way through both walls.

There are more pictures in the gallery.

Visit Josh in Nashville

This picture illustrates the best part about visiting Josh in Nashville: getting to spend time with good friends.

The group: Lucas, Nathan, Jeff, Me, Josh, and Karin

It is a good thing we visited because Josh has been so busy that he has not gotten a chance to enjoy Nashville's famous music district. It was a new experience for all of us. We found several venues with live music, but the best came when we found a jam band playing mid-90s alternative rock. One member of the band soloed on electric violin, which gave them an especially interesting and unique sound. The lead guitarist would also trade his guitar with friends in the audience. That informality and the style of music reminded me strongly of the shows I saw my friends play in high school.

There are more pictures in the gallery and Karin's Facebook album.

Michael's Wedding

Michael finally married Alice, the fellow Purdue alum that he has been dating since right around the time he and I graduated.

Like the trip to Louisville, the wedding gave me a chance to catch up with longtime friends. Of course that includes Michael and Alice, but Stuart, Todd, Matt, Joe, Josh H., Brittany, and Megan also attended. Even though I have been keeping this weblog for over eight years, it is amazing (and discouraging) how much I have forgotten about my time at Purdue. Similarly, despite electronic social networks, I knew less than I would have liked about the lives and activities of the other attendees that I knew from that point in my life. The wedding underscored the importance personal contact with friends.

I am sure this pales in comparison to Michael's experience at his own wedding, so I won't try to overstate anything. Instead, I can simply say that I am deeply honored to have helped him celebrate his marriage, and I wish him and his wife the absolute best in their new life together.

What is this Building?

A small house on top of a pole.  Seen while driving through Knox, Indiana.

Seen north of Knox, Indiana next to an old railroad bed and train station. The building is bigger than it looks in the picture; about 10 feet off the ground and big enough to hold maybe two people. Any guesses on what it was used for?

What to Do When Chemotherapy Stops Working

Chest PET scan animation showing tumors in the lungs.

The animation above shows my most recent PET scan. See the bright yellow sparkles in the pair of otherwise dark areas above the center of the image? Those are the tumors in my lungs. Some appeared since the last set of scans; others are metabolizing more, and thus showing up brighter. For comparison, the left image below is from last November, and the right is from this most recent scan.

PET scan showing my lungs after two rounds of chemotherapy.  The small tumors have decreased in brightness. Chest PET scan

These are not good results. Obviously, the chemotherapy has not prevented tumor growth. Medical literature would say I have reached the regimen's "time to progression", that is, the point after starting treatments at which the disease advances or reappears. Despite the provocative title of this post, that does not necessarily mean that the chemotherapy has stopped working; it just means it is time to try something else.

Unfortunately, my options have become much more limited over the past year. By last March, I had long passed the maximum lifetime dosage of one drug (ifosfamide). Last July, side effects prompted my oncologists to remove another (docetaxel) from my treatments. These latest results show that the remaining drug (gemcitabine) alone has not been effective.

My oncologist searched through the medical literature for other treatments. There is not much, since I have already tried almost everything, and the pool of patients with recurrent, metastatic osteosarcoma is so small as to limit the number of clinical trials for new drugs. Nevertheless, he found one clinical case study in which a combination of gemcitabine and a drug called irinotecan most often used to treat colon cancer was beneficial in a case very similar to mine.

The combination of gemcitabine and irinotecan has shown acceptable toxicity and synergistic activity against many refractory solid tumors. Our case report demonstrates an excellent clinical and radiographic response in a heavily pretreated patient with recurrent osteogenic sarcoma...Due to the absence of a radiographic response to gemcitabine and irinotecan used as single agents, we think that synergism between the 2 drugs makes their combination effective.

So far I have undergone one cycle of treatments with this new pair of drugs. Time will tell if it will work for me.

But truthfully, while I remain optimistic, I don't expect to find the silver bullet. At best, this new regimen will most likely just stabilize tumor growth, and the prospects of experimental treatments or more drastic alternatives like surgery or radiation are not much better. That makes, "try something else and see if it works," an unsatisfying answer to the question posed in the title of this post, but it is the only answer I have for now.

Beware Mutable Data Points (Updated)

In my previous post, I mentioned that one of the benefits of JUnit Theories is that they decouple test inputs (data points) from test implementation (Theories). However, this benefit comes at a price: since data points may be reused across several Theories, the way in which one defines mutable data points can cause surprising unexpected behavior.

To illustrate the problems that can occur with mutable data points, I will reuse the Counter example from the previous post. Counters are obviously mutable because calling increment increases a counter's value by one.

Say I have two Theories: incrementTheory, which is described in the previous post, and equalIncrementTheory, which verifies that two (un)equal counters remain (un)equal after incrementing both. Both theories mutate the counters passed in as arguments.

@Theory
public void incrementTheory(Counter toIncrement) {
    System.out.println("incrementTheory(" + toIncrement + ")");
    int oldValue = toIncrement.getValue();
    toIncrement.increment();
    int newValue = toIncrement.getValue();
    assertEquals(oldValue + 1, newValue);
}
 
@Theory
public void equalIncrementTheory(Counter c1, Counter c2) {
    System.out.println("equalIncrementTheory(" + c1 + ", " + c2 + ")");
    boolean wereEqual = c1.equals(c2);
    c1.increment();		
    c2.increment();
    assertEquals(wereEqual, c1.equals(c2));
}

There are four ways in which I can define data points that JUnit will pass to these Theories. In the previous post, I used the @DataPoints annotation to mark a method that returns an array of Counter objects. Each element of the array is a single data point. I could have alternately used the @DataPoint (no "s") annotation on a method that returns a single counter, or used either annotation to mark static fields in the same manner.

The following list shows each of these four alternatives, followed by the output from the print statements included in the theories shown above. In each case there are two data points of type Counter initialized to the values 0 and 5.

  1. @DataPoint on a field holding a single value
    @DataPoint 
    public static Counter ZERO = new Counter(0);
    @DataPoint 
    public static Counter FIVE = new Counter(5);
    outputs
    incrementTheory(Counter(0))
    incrementTheory(Counter(5))
    equalIncrementTheory(Counter(1), Counter(1))
    equalIncrementTheory(Counter(3), Counter(6))
    equalIncrementTheory(Counter(7), Counter(4))
    equalIncrementTheory(Counter(8), Counter(8))
    
  2. @DataPoints on a field holding an array
    @DataPoints
    public static Counter[] COUNTERS = {
        new Counter(0),
        new Counter(5),
    };
    outputs
    incrementTheory(Counter(0))
    incrementTheory(Counter(5))
    equalIncrementTheory(Counter(1), Counter(1))
    equalIncrementTheory(Counter(3), Counter(6))
    equalIncrementTheory(Counter(7), Counter(4))
    equalIncrementTheory(Counter(8), Counter(8))
    
  3. @DataPoint on a method that returns a single value
    @DataPoint
    public static Counter zero() {
        return new Counter(0);
    }
    @DataPoint
    public static Counter five() {
        return new Counter(5);
    }
    outputs
    incrementTheory(Counter(5))
    incrementTheory(Counter(0))
    equalIncrementTheory(Counter(5), Counter(5))
    equalIncrementTheory(Counter(5), Counter(0))
    equalIncrementTheory(Counter(0), Counter(5))
    equalIncrementTheory(Counter(0), Counter(0))
    
  4. @DataPoints on a method that returns an array
    @DataPoints
    public static Counter[] counters() {
        return new Counter[] {
            new Counter(0),
            new Counter(5),
        };
    }
    outputs
    incrementTheory(Counter(0))
    incrementTheory(Counter(5))
    equalIncrementTheory(Counter(0), Counter(0))
    equalIncrementTheory(Counter(1), Counter(5))
    equalIncrementTheory(Counter(5), Counter(0))
    equalIncrementTheory(Counter(6), Counter(5))
    

Three things are surprising about this output. First, even though the alternatives start with the same data points, their output differs. Second, the data points started with values 0 and 5, but the values 1, 3, 4, 6, 7, and 8 all appear in various places. Indeed, in alternatives 1 and 2, the values 0 and 5 are never found in equalIncrementTheory! Third, alternative 3 is the only one that gives the "expected" output in which both theories are executed with all combinations of 0 and 5.

All of these issues are caused by mutable data points. One Theory execution (meaning a single call to incrementTheory or equalIncrementTheory) can affect later Theory executions.

To understand why this happens, it is necessary to examine the "lifespan" of each data point instance. Fields such as those in alternatives 1 and 2 are declared static and initialized when the class is loaded. Therefore, the data point instances held by the fields live across all theory executions. For alternative 3, JUnit calls the zero and five methods once before each theory execution, so each data point instance lives through only one method call. Alternative 4 produces arrays of data points that live across multiple executions of a single theory but are reinitialized after each element has been used in a theory at least once.

To make these distinctions more clear, the following pseudocode illustrates the entire test run for each alternative. It shows the points at which JUnit creates each data point instance (represented as variable definition), the datapoint values (shown in comments), how they are passed to Theories (represented as method calls), and where they are mutated (the increment calls).

  1. @DataPoint on a field holding a single value. Note that ZERO and FIVE live for the entire test run and are continually mutated.
    ZERO = new Counter(0)
    FIVE = new Counter(5)
    incrementTheory(ZERO) // 0 
        ZERO.increment() // 1
    incrementTheory(FIVE) // 5
        FIVE.increment() // 6
    equalIncrementTheory(ZERO, ZERO) // 1, 1
        ZERO.increment() // 2
        ZERO.increment() // 3
    equalIncrementTheory(ZERO, FIVE) // 3, 6
        ZERO.increment() // 4
        FIVE.increment() // 7
    equalIncrementTheory(FIVE, ZERO) // 7, 4
        FIVE.increment() // 8
        ZERO.increment() // 5
    equalIncrementTheory(FIVE, FIVE) // 8, 8
        FIVE.increment() // 9
        FIVE.increment() // 10
  2. @DataPoints on a field holding an array. Like the previous alternative, the COUNTERS array lives for the entire test run and its elements are continually mutated.
    COUNTERS = new Counter[] { new Counter(0), new Counter(5) }
    incrementTheory(COUNTERS[0]) // 0
        COUNTERS[0].increment() // 1
    incrementTheory(COUNTERS[1]) // 5
        COUNTERS[1].increment() // 6
    equalIncrementTheory(COUNTERS[0], COUNTERS[0]) // 1, 1
        COUNTERS[0].increment() // 2
        COUNTERS[0].increment() // 3
    equalIncrementTheory(COUNTERS[0], COUNTERS[1]) // 3, 6
        COUNTERS[0].increment() // 4
        COUNTERS[1].increment() // 7
    equalIncrementTheory(COUNTERS[1], COUNTERS[0]) // 7, 4
        COUNTERS[1].increment() // 8
        COUNTERS[0].increment() // 5
    equalIncrementTheory(COUNTERS[1], COUNTERS[1]) // 8, 8
        COUNTERS[1].increment() // 9
        COUNTERS[1].increment() // 10
  3. @DataPoint on a method that returns a single value. JUnit calls the zero and five methods once for each Theory argument. This is analogous to continually assigning temporary variables.
    tmp1 = five()
    incrementTheory(tmp1) // 5
        tmp1.increment() // 6
    tmp2 = zero()
    incrementTheory(tmp2) // 0
        tmp2.increment() // 1
    tmp3 = five()
    tmp4 = five()
    equalIncrementTheory(tmp3, tmp4) // 5, 5
        tmp3.increment() // 6
        tmp4.increment() // 6
    tmp5 = five()
    tmp6 = zero()
    equalIncrementTheory(tmp5, tmp6) // 5, 0
        tmp5.increment() // 6
        tmp6.increment() // 1
    tmp7 = zero()
    tmp8 = five()
    equalIncrementTheory(tmp7, tmp8) // 0, 5
        tmp7.increment() // 1
        tmp8.increment() // 6
    tmp9 = zero()
    tmp10 = zero()
    equalIncrementTheory(tmp9, tmp10) // 0, 0
        tmp9.increment() // 1
        tmp10.increment() // 1
  4. @DataPoints on a method that returns an array. JUnit calls the method once for each Theory argument, then loops through all combinations of the returned arrays' values. In the case of equalIncrementTheory, it reuses the left argument across two Theory executions while the right argument changes.
    tmp1 = counters() // { 0, 5 }
    incrementTheory(tmp1[0]) // 0
        tmp1[0].increment() // 1
    incrementTheory(tmp1[1]) // 5
        tmp1[1].increment() // 6
    tmp2 = counters() // { 0, 5 }
    tmp3 = counters() // { 0, 5 }
    equalIncrementTheory(tmp2[0], tmp3[0]) // 0, 0
        tmp2[0].increment() // 1
        tmp3[0].increment() // 1
    equalIncrementTheory(tmp2[0], tmp3[1]) // 1, 5
        tmp2[0].increment() // 2
        tmp3[1].increment() // 6
    tmp4 = counters() // { 0, 5 }
    equalIncrementTheory(tmp2[1], tmp4[0]) // 5, 0
        tmp2[1].increment() // 6
        tmp4[0].increment() // 1
    equalIncrementTheory(tmp2[1], tmp4[1]) // 6, 5
        tmp2[1].increment() // 7
        tmp4[1].increment() // 6

This behavior certainly violates The Principle of Least Astonishment, but is it necessarily undesirable? Theories should test behavior common to many data points, including those that may or may not have been mutated elsewhere, so perhaps it is a good thing that alternatives 1, 2, and 4 "create" data points that the user did not originally plan for. However, common practice dictates that unit tests should be independent of each other and deterministically repeatable. Alternatives 1, 2, and 4 violate both principles: one Theory execution can affect another, and since Theory ordering is nondeterministic, there is no guarantee that re-running a Theory will yield the same result.

Therefore, when writing Theories that operate on mutable data points, I most often use and recommend the third alternative. That way each Theory execution uses new data point instances, making Theories independent of each other and deterministic.

Update (September 29, 2009)

A reader emailed with the observation that the issues surrounding mutable versus immutable objects are not specific to JUnit. Immutable objects make a program easier to reason about since changing a value in one place cannot affect another place that reads the value. The same is true in JUnit: using immutable objects causes all four ways of defining data points to produce equivalent behavior. However, using immutable objects may not be feasible or preferable, so one must be aware of how mutable objects are initialized and used. As the above article shows, this task is more difficult with JUnit Theories since it is not always obvious when data points are (re)initialized and how they flow across multiple Theories.

JUnit Theories

This semester I am overseeing two undergraduate senior theses. The two students are working on a project involving JUnit Theories. Theories are a very useful feature of JUnit, but they have not been widely adopted since they are still experimental and not documented very extensively. The project's short-term goal is to address this problem by writing a suite of Theories for use as benchmarks in testing research. In the longer term, we hope to apply knowledge gained by writing Theories to other research projects and find areas in which Theories can be improved.

This post describes what Theories are and what they do. In future posts, I hope to write about why I find them interesting and how they enable more complex testing tasks.

"Theories in practice: Easy-to-write specifications that catch bugs" defines Theories in the following way1:

[Theories] are partial specifications of program behavior. Theories are written much like like test methods, but are universally quantified: all of the theory’s assertions must hold for all arguments that satisfy the assumptions...A theory can be viewed in several ways. It is a universally quantified ("for-all") assertion, as contrasted to an assertion about a specific datum. It is a generalization of a set of example-based tests. It is a (possibly partial) specification of the method under test.

To understand what this definition means, it is easiest to explain a simple example. Say we have a simple Counter class that allows one to increment an integer value every time the increment method is called.

public class Counter {
    private int value;
 
    public Counter(int init) {
        this.value = init;
    }
 
    public void increment() {
        value = value + 1;
    }
 
    public int getValue() {
        return value;
    }
}

We wish to test that incrementing always increases a counter's value by one. The standard way to test this functionality is to write an example-based unit test that creates a Counter, increments it a few times, and asserts that the incremented values are correct.

@Test 
public void testIncrement()	{
    Counter c = new Counter(3);
    c.increment();
    assertEquals(4, c.getValue());
    c.increment();
    assertEquals(5, c.getValue());
}

This is a useful test, but it only verifies that a single counter initialized to three is incremented correctly. It would be good to test additional counters initialized to many different values. Doing so using example-based testing requires multiple test methods or testing objects in a loop.

Theories provide an elegant alternative that complements example-based tests. From the test writer's point of view, Theories are just like normal unit tests but with one or more parameters.2 To test incremement, one can write a Theory that accepts a Counter object, increments it, then asserts that the value has increased by one. This is more general than an example-based test because it verifies a property common to all counters, regardless of how they were initialized. In the following code, the incrementTheory method implements such a Theory.

@RunWith(Theories.class)
public class CounterTheories {
 
    @DataPoints 
    public static Counter[] data() {
        return new Counter[] {
            new Counter(0),
            new Counter(1),
            new Counter(-1),
            new Counter(Integer.MIN_VALUE),
            new Counter(Integer.MAX_VALUE), // overflows when incremented
        };
    }
 
    @Theory
    public void incrementTheory(Counter toIncrement) {
        int oldValue = toIncrement.getValue();
        assumeTrue(Integer.MAX_VALUE != oldValue);
        toIncrement.increment();
        int newValue = toIncrement.getValue();
        assertEquals(oldValue + 1, newValue);
    }
 
    //... more theories 
}

The @RunWith(Theories.class) annotation tells JUnit that it should run all methods in the class that are annotated with @Theory. The @DataPoints annotation marks methods that return values that JUnit should supply to applicable Theories. At runtime, JUnit matches the values returned from data point methods or fields to appropriate Theory parameters.3 In the example above, it sees that Counter objects are produced by data and consumed by incrementTheory, so it executes incrementTheory once for each element in the array returned from data.

Theories decouple test inputs from test implementation. Data points are automatically reused across multiple theories (even in subclasses), making it easier to write new tests. Adding a data point often provides a value that a test writer may not have originally considered, thus improving all Theories that use the data point.

But certain data points may not be applicable to a particular Theory. The test writer describes what data points apply by using methods provided by the org.junit.Assume class. Assumptions are similar to normal assertions except they cause a Theory to skip certain data points rather than fail. In our example, incrementTheory should not increment a counter whose value is equal to Integer.MAX_VALUE since the value would overflow. Therefore, incrementTheory uses assumeTrue to check for this special case.

This summary and example briefly describes the basics of Theories but glosses over how Theories work internally and lacks practical advice like how to write "good" theories. I hope that the undergrads and future weblog posts can explore these topics and deeper research issues further.

Notes

  1. See also an early paper on Theories and the initial announcement of their inclusion as part of an experimental project called Popper.
  2. .NET offers a similar feature but calls it—perhaps more descriptively—parameterized unit tests. JUnit uses this term to mean test classes that are instantiated with input data.
  3. Internally, JUnit finds data points whose declared types derive from the declared types of Theory parameters. It does not currently box and unbox primitive data points. I submitted a simple patch that fixes the problem, but it has not yet been accepted.

ReAssert at ASE 2009

In my previous post, I wrote about ReAssert, the tool I built to automatically fix broken unit tests. Yesterday I received notification that the paper describing the tool got accepted to ASE 2009.

This is the same paper mentioned in my crunch time analysis and typography request.

Here is the (working) abstract:

Developers often change software in ways that cause tests to fail. When this occurs, developers must determine whether failures are caused by errors in the code under test or in the test code itself. In the latter case, developers must repair failing tests or remove them from the test suite. Fixing tests is time consuming but beneficial, since removing tests reduces a test suite's ability to detect regressions. Fortunately, simple program transformations can repair many failing tests automatically.

We present ReAssert, a novel technique and tool that suggests repairs to failing tests' code which cause the tests to pass. Examples include replacing literal values in tests, changing assertion methods, or replacing one assertion with several. If the developer chooses to apply the repairs, ReAssert modifies the code automatically. Our experiments show that ReAssert can repair many common test failures and that its suggested repairs correspond to developers' expectations.

The conference will be held in Auckland, New Zealand. I am excited to travel overseas to present my work. Vilas, my coauthor, and Yun-Young, my officemate, are both from New Zealand and are eager to visit home.

Update September 8, 2009

I have posted the final version of the paper and updated the ReAssert homepage.

Update (November 30, 2009)

The conference presentation went very well, and I got a great deal of insightful questions and feedback from other attendees. Here are the presentation slides. I told the story of Alice the software developer like in the previous ReAssert post. The presentation starts with a picture of Alice adapted (as per Creative Commons) from xkcd #662.

Alice the software developer. Adapted from xkcd #662 and used in my ReAssert presentation.

I had originally planned to draw a picture in my normal cartoon style, but decided instead to use something simpler. The xkcd picture turned out to be a good choice; it made the audience laugh, and one attendee mentioned Scott McCloud's assertion from Understanding Comics that a simple face causes the audience to identify themselves in a character.

ReAssert: Suggesting Repairs for Broken Unit Tests

For the past year or so, I have been researching how software tests fail and the ways in which developers fix the failures. There are many interesting problems within this general theme, but I have most recently focused on the following familiar scenario:

Alice is a developer a large software company. She works on the company's flagship product and spends over half of her time writing unit tests to verify her code and document her assumptions. She is not alone in this respect; the company requires that functional changes and bugfixes should have corresponding unit tests to prevent regressions. As a result, the product's unit test suite achieves exceptionally high coverage.

One day, the project manager informs Alice that a key requirement has changed. The changed requirement violates many assumptions encoded the test suite, so several dozen tests fail after Alice modifies the software. Now Alice has a choice: should she remove the failing tests since they no longer reflect the correct behavior of the software, or should she attempt to repair the tests, which would require tedious and time-consuming manual editing?

Developers often have to make a similar choice. When tests fail due to problems with test code rather than the system under test, it is undoubtedly beneficial to fix the broken tests, since removing tests reduces a test suite's ability to detect regressions. However, developers may not take the time to fix the broken tests. For example, while working on the refactoring paper, my colleagues and I found many of Eclipse's refactoring tests were either commented out, marked as ignored, or most bizarrely, bypassed using "if (true) return;".

To solve this problem, I have been exploring ways of reducing the effort required to fix broken unit tests. Doing so would make developers less fearful of "deep" changes, allow them to write more detailed tests, and most importantly, provide time for more important work.

As a first step toward this goal, I developed a tool called ReAssert that automatically suggests changes to test code that are sufficient to make tests pass. Earlier this week I released a public beta. I welcome anyone reading this to download it from the ReAssert project homepage and try it out. Please contact me if you have any comments, questions, ideas for improvement, or bug reports.

Side Effects

When discussing most medications, doctors usually list expected benefits first, then possible side effects. For chemotherapy drugs, the discussion is reversed: first doctors list expected side effects, then possible benefits. Each drug has its own unique side effect "signature" and regimen of secondary drugs with their own set of side effects. I have already written about hair loss and strange dreams. I recently experienced a new unexpected side effect: something caused my right leg to swell like an overstuffed sausage.

It is a frustratingly inexplicable side effect. It occurs after the Docetaxel treatments I have gotten every third week for the last few months. The drug's documentation says it occurs "in rare cases" but the root cause is not well known. I got a series of tests to rule out particularly bad possibilities like poor circulation, a blood clot, or problems with my lymphatic system. My surgeon says it does not appear to be related to surgery. That makes it just another random, mildly annoying side effect of chemotherapy. It didn't hurt; I just had to tie my shoe more loosely.

But what to do about it? The doctor prescribed some medication to reduce the swelling. It seems to be working. In the six days between treatments, I lost nine pounds, all from the leg! Of course, the medicine has its own set of side effects. Fatigue, in particular, but that goes away when I take another pill (for unrelated reasons) whose side effects include insomnia.

To test whether the swelling is really caused by the Docetaxel, the doctor omitted it from this week's treatment. She is still deciding whether to reduce the dosage, spread it out, or leave it unchanged. In any case, I am more than happy to accept a sausage leg if the drug takes care of the cancer.

Just got back from canoeing at…

Just got back from canoeing at Kickapoo SP. Beautiful and peaceful float. Barely had to paddle.

Silly Videos

A friend complained that I had not posted any new videos since creating a YouTube account for the JUnit Music Box demonstration. So here are some silly videos from several years ago.

The 2006 Winter Olympics curling event provided a convenient target for Nerf suction cup darts.

My grandmother's and cousin's dogs were just puppies in August 2004.

In October 2004, Eric demonstrated his unicycle skills in the hallway of Purdue's Hillenbrand Hall.

In February 2004, my suitemate Matt hung a rope swing from the loft in his dorm.

At my sister's high school graduation party in June 2003, the pinata did not work quite as expected.

Last night’s thunderstorm knoc…

Last night's thunderstorm knocked out power in half (?) of my apartment and fried my router. Somehow all of my other electronics are fine.
Older Posts