« June 2008 | Main | August 2008 »
July 31, 2008
SchemeUnit 3: A New Approach to Testing
SchemeUnit 3 has been released. Although the interface remains compatible with version 2 the underlying philosophy of SchemeUnit has changed in a significant way. The following is extract from the SchemeUnit manual, describing the new approach.
SchemeUnit is designed to allow tests to evolve in step with the evolution of the program under testing. SchemeUnit scales from the unstructed checks suitable for simple programs to the complex structure necessary for large projects.
Simple programs, such as those in How to Design Programs, are generally purely functional with no setup required to obtain a context in which the function may operate. Therefore the tests for these programs are extremely simple: the test expressions are single checks, usually for equality, and there are no dependencies between expressions. For example, a HtDP student may be writing simple list functions such as length, and the properties they are checking are of the form:
(equal? (length null) 0) (equal? (length '(a)) 1) (equal? (length '(a b)) 2)
SchemeUnit directly supports this style of testing. A check on its own is a valid test. So the above examples may be written in SchemeUnit as:
(check-equal? (length null) 0) (check-equal? (length '(a)) 1) (check-equal? (length '(a b)) 2)
Simple programs now get all the benefits of SchemeUnit with very little overhead.
There are limitations to this style of testing that more complex programs will expose. For example, there might be dependencies between expressions, caused by state, so that it does not make sense to evaluate some expressions if earlier ones have failed. This type of program needs a way to group expressions so that a failure in one group causes evaluation of that group to stop and immediately proceed to the next group. In SchemeUnit all that is required is to wrap a test-begin expression around a group of expressions:
(test-begin (setup-some-state!) (check-equal? (foo! 1) 'expected-value-1) (check-equal? (foo! 2) 'expected-value-2))
Now if any expression within the test-begin expression fails no further expressions in that group will be evaluated.
Notice that all the previous tests written in the simple style are still valid. Introducing grouping is a local change only. This is a key feature of SchemeUnit’s support for the evolution of the program.
The programmer may wish to name a group of tests. This is done using the test-case expression, a simple variant on test-begin:
(test-case "The name" ... test expressions ...)
Most programs will stick with this style. However, programmers writing very complex programs may wish to maintain separate groups of tests for different parts of the program, or run their tests in different ways to the normal SchemeUnit manner (for example, test results may be logged for the purpose of improving software quality, or they may be displayed on a website to indicate service quality). For these programmers it is necessary to delay the execution of tests so they can processed in the programmer’s chosen manner. To do this, the programmer simply wraps a test-suite around their tests:
(test-suite "Suite name" (check ...) (test-begin ...) (test-case ...))
The tests now change from expressions that are immediately evaluated to objects that may be programmatically manipulated. Note again this is a local change. Tests outside the suite continue to evaluate as before.
2.1 Historical Context
Most testing frameworks, including earlier versions of SchemeUnit, support only the final form of testing. This is likely due to the influence of the SUnit testing framework, which is the ancestor of SchemeUnit and the most widely used frameworks in Java, .Net, Python, and Ruby, and many other languages. That this is insufficient for all users is apparent if one considers the proliferation of "simpler" testing frameworks in Scheme such as SRFI-78, or the the practice of beginner programmers. Unfortunately these simpler methods are inadequate for testing larger systems. To the best of my knowledge SchemeUnit is the only testing framework that makes a conscious effort to support the testing style of all levels of programmer.
Posted by Noel at 03:42 PM | Comments (1)
July 24, 2008
Undeleting Files on the Mac
I spent a good portion of last week attempting to recover about 30GB of movies that had been deleted from a Mac with a 60GB hard disk. When a file is deleted its normally left intact on the hard disk except for a marker saying its space can be reused. This means that deleted files can be fairly reliably recovered, so long as the space hasn't since been used for other purposes. We found the movies were missing only a few days after they were deleted, and they took up half the hard disk, so I was fairly confident they could be in part recovered.
Of course that's great in theory but in practice how I was I going to recover those files? A quick bit of Googling discovered three programs that will attempt to recover deleted files on the Mac: Boomerang, FileSalvage, and Data Rescue II. I downloaded a trial copy of each and set to work. Here's how they performed:
- Boomerang ran very quickly but only found some 29MB of the missing 30GB of movies. Of the three programs I tested it is the easiest to use, with only a few options for the most common problems. It also does a better job of sticking to the Mac interface conventions than the other two.
- FileSalvage took many hours to search the hard disk. It found lots of files, but it didn't identify many as fragments of movies. Additionally the interface is very clunky. It doesn't use the standard Mac widgets and selecting file types in Expert mode is a real pain.
- Data Rescue II ran fairly quickly and found almost all the lost data. Success! It is fairly easy to use. The guided standard mode does a good job of leading you through the recovery process, and the many options in Expert mode are explained well. Like the other programs it uses non-standard widgets, and this needlessly detracts from its usability.
So in my testing Data Rescue II was the clear winner. Don't read too much into this, as I was only looking for movie data; one of the other programs might work better for a different type of file. However, if you've deleted some files that you want to recover I would start with Data Rescue II, then try Boomerang, and only then try FileSalvage (and go to bed while it's running). Finally, if you have two Macs a firewire cable and target disk mode will make the whole recovery process a bit simpler.
Now what I want to know is: why would a Mac developer invent their own user interface widgets unless they really want that amateur feel to their product? Is there something about Cocoa programming that makes it easier to create, say, your own tab component than use the system one?
Posted by Noel at 04:15 PM | Comments (0)
