Thursday, July 21, 2011

Surgical Unit Testing

I'm making an unusual detour from my typical just kind of blog as I go along approach to make a note here at the beginning based on what I discovered during testing. I have developed a few date-manipulation routines in the past few days, and found that one of the bugs was pointing to a particular date routine. But, instead of running the whole program do get to that point, I broke my Junit testing project out of mothballs to test and debug that routine. It was really effective, and saved me time as well. Amazing. I didn't have to set up the input date, except as hardcoded input to the test. I was also able to walk into the routine with the debugger very easily and rapidly. So, my conclusion is that when the situation warrants it, unit testing can be a real-time saver - a kind of surgical approach.

Ok, so, where we left off yesterday, we were trying to figure out why we had some funky dates. We're going to reset the database, this time focusing only on one, word zoo. It should first go to 7/21 and 7/21 on the prev and sched date once we get it wrong the first time. Let's try it.

Ok, that's good, here are the scheduled rows.

no last reviewed scheduled
1 2011-07-21 2011-07-21
627 2011-07-21 2011-07-21

Now we will get koto, #1, wrong and zoo, 627, right fast.

Lets' see what happens.

Ok, I just did zoo, within 5 second. Let's see what the date is...

627 2011-07-21 2011-07-24

Ok, as if it were slow, not fast.

Let's examine the log.

/UpdateSRSUtil( 1545): ========================== SRS ===========================
D/UpdateSRSUtil( 1545): Secs Remaining: 6
D/DateUtils( 1545): ===================================== addDaysToDate ================
D/DateUtils( 1545): inDate : 2011-07-21 daysToAdd: 0
D/DateUtils( 1545): >>>>>>>>>>>>>>>>>>>>>>> date1 is: 2011-07-21
D/DateUtils( 1545): s>>>>>>>>>>>>>>>>>>>>>> startDate is: 2011-07-21
D/DateUtils( 1545): >>>>>>>>>>>>>>>>>>>>>>>>> date is: 2011-07-21
D/DateUtils( 1545): date after loop is: 2011-07-21
D/DateUtils( 1545): ===================================== adDaysToDate ================
D/DateUtils( 1545): inDate : 2011-07-21 daysToAdd: 3
D/DateUtils( 1545): >>>>>>>>>>>>>>>>>>>>>>> date1 is: 2011-07-21
D/DateUtils( 1545): s>>>>>>>>>>>>>>>>>>>>>> startDate is: 2011-07-21
D/DateUtils( 1545): >>>>>>>>>>>>>>>>>>>>>>>>> date is: 2011-07-21
D/DateUtils( 1545): >>>>>>>>>>>>>>>>>>>>> date in loop is: 2011-07-22
D/DateUtils( 1545): >>>>>>>>>>>>>>>>>>>>> date in loop is: 2011-07-23
D/DateUtils( 1545): >>>>>>>>>>>>>>>>>>>>> date in loop is: 2011-07-24
D/DateUtils( 1545): date after loop is: 2011-07-24
D/Init

Ok, so clearly it's not properly calculating the days to add.

Here's the culprit:

if (secsRemaining > 5) {
return DateUtils.addDaysToDate(DateUtils.getTodayYYYYMMDD(), SLOW_RESPONSE_INTERVAL_DAYS);

It should obviously be:

if (secsRemaining < 5) {
return DateUtils.addDaysToDate(DateUtils.getTodayYYYYMMDD(), SLOW_RESPONSE_INTERVAL_DAYS);

Ok, let's try it for koto and see if it works.

Ah, that's better:

no last reviewed scheduled
627 2011-07-21 2011-07-24
1 2011-07-21 2011-07-26


Now, let's try a trick of putting the phone in airplane mode, so the date doesn't get updated whatever does that. Then, set the date to the 26. If I get zoo right slow, it should add 1.5 * 3 = 4.5 days truncated to 4 = 7/30. And if i get koto (1) right fast, it should add 5 * 2 = 10 and come up with 8/5 as the next date. Let's try see which come up first.

koto.

Well, I've killed the SQLRazor - not sure how - I can't bring it down. Let's look up how to kill a process on the macbook - it should be some variation kill -9 pid. Actually, you can just do it from the apple menu.

Ok, so, it only added 5 days. Why?

Actually, I see a dateDiff = 0, although it should have been 5. So, there's where to focus on.

This might be a good time to break out the old AndroidJunit. I had a series of test set up before, and I can just set up a test for this little method and see what happens. That's the beauty of unit testing - you can run a simple test without having to get all the infrastructure set up all around it.

Try this:

public void testDateDiff() {
int diff = DateUtils.getDateDiff("2011-07-21", "2011-07-22");
Assert.assertEquals(1, diff);
}

Well, it passed. Hmmm, how about


public void testDateDiff() {
int diff = DateUtils.getDateDiff("2011-07-21", "2011-07-23");
Assert.assertEquals(1, diff);
}

Flunked. How about:


public void testDateDiff() {
int diff = DateUtils.getDateDiff("2011-07-21", "2011-07-26");
Assert.assertEquals(5, diff);
}

Flunked again. And the display statement shows zero.

Can I debug into it? Set the breakpoint - debug as junit test - yes!

And, quickly I find the same problem that I had on the other routine, doing this:

Date date1 = stringToDate(earlierDate);
Date date2 = stringToDate(laterDate);

Calendar startDate = Calendar.getInstance();
Calendar endDate = Calendar.getInstance();

// Bzzzt - Wrong!
startDate.set(date1.getYear(), date1.getMonth(), date1.getDay());
endDate.set(date2.getYear(), date2.getMonth(), date2.getDay());

Instead of:

// Ahh - right :)
startDate.setTime(date1);
endDate.setTime(date2);

Ok, let's run our unit test again.

Me likey. All green bars.

Ok, let's take a look at where the data is:

627 2011-07-21 2011-07-24
1 2011-07-26 2011-07-31


Here's what I was trying:

Now, let's try a trick of putting the phone in airplane mode, so the date doesn't get updated whatever does that. Then, set the date to the 26. If I get zoo right slow, it should add 1.5 * 3 = 4.5 days truncated to 4 = 7/26 + 4 = 7/30. And if i get koto (1) right fast, it should add 5 * 2 = 10 and come up with 8/5 as the next date. Let's try see which come up first.

Ok, so, zoo, 627, still hasn't been updated. So, let's do zoo right slow, and see if it ends up at 7/30.

Ok, yes. Sweet.

Ok, now let's go back and take a looks and see if all the conditions we wanted to cover have been covered:


Let's see:

1) last review date null, answer right, slow
2) last review date null, answer right, fast
3) last review date null, answer wrong
4) last review date null, timeout

5) last review date not null, answer right, slow
6) last review date not null, answer right, fast
8) last review date not null, answer wrong
9) last review date not null, timeout

10) - make sure that a scheduled review date doesn't show up until it's supposed


10 - check.

5 - check
3 - check

So, I'd like to get a feel of how far it move dates out when it's been through a couple of reviews.

Here's where we're at right now:

627 2011-07-26 2011-07-30
1 2011-07-26 2011-07-31


So, say I change the date to 8/1.

If I get zoo/627 right slow, it should add 1.5 * 4 = 6 days to 8/1 = 8/1 + 6 = 8/7. And if i get koto/ right fast, it should add 2 * 5 = 10 and come up with 8/11 as the next date. Let's try see which come up first.

Oh, yeah; I keep forgetting to put code in to handle when no rows are returned. It is possible when you reset range.

Here we go - it addresses the primary case where it would happen.

if (appState.quizSequence.size() == 0 ){
Toast.makeText(this, "no questions available; try increasing the quiz range", Toast.LENGTH_LONG);
finish();
}

Ok, let's change the date to 8/1 and try our test.

Ok, koto came up first. Yes, it worked - 8/11 is the date.
Ok, and doubutsu-en came up for 8/7.

Houston, we have long-term-memory review.

Ok, let's test that error condition. Hmmm...although I call finish, it's blowing right through it and crashing.

Ok, I forgot to do the ".show" on the toast. I'm not sure if that made a difference, because in the end I put the check in the start activity where it belongs.

Ok, this post has gotten long enough. I'm going to wrap it up for now, and pick it up at finalizing the testing of all the conditions.

No comments:

Post a Comment