Tuesday, December 13, 2011

Group Programming: Some Adjustment Required

After last week's technical review, Jeff and I swapped projects with the grads group. That is, we took over on the grads' code, and they started working on ours. Both groups added three more commands to the existing codebase that they had just inherited.

This was an interesting experience taking over on an existing software project. Of course, there was some initial overhead getting familiar with the layout of the new project's code. This wasn't too onerous, though, since the overall structure was fairly logical.

Once Jeff and I started working, the work went smoothly. We had already completed one project together. I think Jeff and I communicated well, and each of us made some significant contributions. We agreed on all of the major design issues.

Still, for all of the harmony, I'd say that my most interesting learning experience this week was the old "pick your battles" experience. If you've ever shared a living space with someone as an adult--whether a college roommate or a romantic partner--you probably know what I mean. There's a period of adjustment to the person's habits and quirks. For example, maybe your new roommate already put the silverware in the left drawer when you feel that it is obviously more natural to have it in the right drawer. Or maybe your new lover seems to somehow constantly drop loose change; eventually stray coins lie scattered throughout the house.

For each new quirk you discover, you have to decide whether it bothers you or not. If it does, then you need to figure out if you're willing to just overlook it, constantly clean up after them, or make an issue of it. If you make an issue of every little thing, you can easily become an unpleasant nag. Depending on how it's done, cleaning up behind someone can come across as a passive-aggressive show of disapproval. But, if something really does bother you and you don't speak up, you can find that your living space is not really your own. That's what I mean by picking your battles.

First, Jeff and I had to come to terms with the existing code. As we settled in, we found we had to "move the silverware" in a few places to meet our tastes. In particular, we overhauled the look and behavior of the user interface. We left the reflection-based loading of classes in place, though.

Then there were the minor differences in working style that I noticed between me and Jeff. For the most part, these were so minor as to border on petty: "If I'd done it, I wouldn't have spaced the output that way" or "I usually write one class at a time rather than mock up all the classes and then fill in the details later."

A couple weeks ago, I mentioned the motivating aspect of working in a group. My point this week is that, whenever you work with someone else, they are not going to do things exactly the same way you do. That is to be expected. But, even when you realize this consciously, each specific difference you discover can still cause a short pause: "Oh.. that's not how I would have done that... but that doesn't mean it's wrong... Am I going to accept how this has been done and move on, or do I want to say something and change it?" Overall, I discovered that I could just let these things go. It was just a new experience for me to adjust to someone else's presence in my coding project space, both in terms of the old code and the new.

Jeff and I got everything finished up before the deadline. I think our code is pretty solid, though some of our line-spacing might be a little off between different commands.

Testing code that connects to a server continued to be a pain. I considered making some sort of mock client object, but there proved to be too many methods that would need to be overridden to make it worthwhile.

I also found that, when verifying someone else's code, it's easier to run through a manual test rather than plod through all their JUnits and make sure it covers all the necessary cases. While I think it's good to get some manual testing in there occassionally--especially since that lets you spot things like typos and weird formatting that a JUnit test is not going to catch--I think this is still not quite ideal. Manual testing can find bugs that the person's JUnits missed. But, if the code is correct, manual testing won't reveal that an appropriate JUnit test is missing altogether. This means that changes to the code could cause an undetected failure later. So I guess I should work on using the person's JUnit tests as a loose guide to manual testing in order to spot missing/poor tests as well as any existing defects.

I'm still a fan of Issue-based Project Management. I'm also glad we kept our habit of having the other person mark completed tasks as Verified. It's an extra step, but it's a good feeling knowing that everything has been double-checked by someone else.

This is the last project for my software engineering course, so it may mean a blog hiatus for a while. But I should be back occasionally with news on other new software projects!

Friday, December 2, 2011

Technical Review: hale-aloha-cli-grads

As I mentioned last week, Jeff and I recently finished up a small command line interface program that allows a user to poll a WattDepot server for accumulated energy-use data. Since this was done as part of a software engineering course, there were a number of other groups developing their own versions of the same program.

This week, I'm going to perform a structured technical review of one of those other projects: hale-aloha-cli-grads. This is partly for the practice of reviewing another software project in detail before contributing to it. By comparing the two, this will also hopefully shed some light on what we did well and what we could have done better on our own project. Finally, the feedback might be helpful to the grads group members.

A good technical review needs to be structured by a series of pre-determined issues to examine. More generally, I'll be evaluating whether the grads project meets the Three Prime Directives of open-source software engineering. (This is not the first time I've done this sort of review.) I'll be covering the prime directives (PDs) in the order I encountered them as an interested developer.

PD2: An external user can successfully install and use the system.

The project's homepage at Google Project Hosting was sufficiently detailed, giving a basic overview of what the program does and who wrote it.
It also contains a link to a wiki UserGuide. I liked that the authors included a screenshot there. The UserGuide also documented a feature not obvious from within the program itself: you can poll the physical sources (such as Ilima-04-telco) for energy data, as well as polling the towers and lounges of the dorm.

Since there was only one link available under the project's Downloads tab, downloading was pretty self-explanatory. The brief Installation Guide explained how to extract the necessary executable .jar from the downloaded zip file and then run the program.


PD1: The system successfully accomplishes a useful task.

The program starts with a paragraph-long help message describing the possible commands:

--Available commands are:
energy-since: [tower | lounge] [Start]
Returns the energy used since the date (yyyy-mm-dd) to now.
current-power [tower | lounge]
Returns the current power in kW for the associated tower or lounge.
daily-energy: [tower | lounge] [Date]
Returns the energy in kWh used by the tower or lounge for the specified date (yy
yy-mm-dd).
rank-towers:  [start date] [end date]
Returns a list in sorted order from least to most energy consumed between the [s
tart] and [end] date (yyyy-mm-dd)
--Enter a command (type 'quit' to exit):
 

This was useful, though the help message is a little mangled when viewed in a conventional 80 character-wide console window. (Our own project's output suffered this same issue.) The last line shown here is the prompt for input, though any input entered then appears on the next blank line. This is a little unconventional for a command line prompt.

Although covered in the UserGuide, there was no in-program list of valid tower and lounge names that could be polled for data. I found a work-around for this: The rank-towers command gives the list of towers, and a user could probably remember how to generate lounge names from that list. But it really would be helpful to have this information explicitly available from within the program itself.

Although the help message was useful when first starting the program, it gets printed every turn. This means that the prompt is effectively 12 lines long every input turn, even when using the program correctly! This quickly became annoying, since it clutters the output, which is usually only a line or two for most commands. Also, this behavior did not match UserGuide screenshot mentioned earlier.

The program correctly supported all of the commands requested by the "client" of this software project. The error messages were generally fine, shielding user from any technical details. (One quibble: The program complains "Not enough arguments" when it is given too many arguments.) Most importantly, the program did not crash at any point. (See the transcript at this end of this review for the full details.)

In general, testing this program reminded me how tedious any CLI--including our own project--can be to use.


PD3: An external developer can successfully understand and enhance the system.

Once I knew the program itself worked, it was time to take a look at what it would take to extend or contribute to it.

Developement protocol. The grads project DeveloperGuide wiki page provides a brief summary of the required development practices: the coding standards used by the project, how to follow Issue-Driven Project Management, how to ensure that new code passes verification by all of the QA tools used, and a link to the continuous integration Jenkins server used by the project. This was sufficient for me, but the DeveloperGuide may be a little too brief for someone not already familiar with these terms--such as "Elements of Java Style", "Issue-Driven Project Managements", and "CI".

QA. The quality assurance (QA) script verify.build.xml failed for me after the initial check-out. The JUnit tests intermittently failed due to WattDepot server connection problems. Also, the tests printed a lot of details to the screen, which suggests that perhaps the developers were doing manual verification of all of these tests. Checkstyle initially crashed due to a problem with one of its component classes, but running ant reallyclean and then ant -f verify.build.xml again fixed the problem. So, after repeatedly running verify, it eventually passed with no code changes required. In getting through this, it helped a lot that I was already familiar with this build system, since we used the same setup for our project.

Documentation. The DeveloperGuide did not explicitly mention how to generate JavaDocs. However, this is fairly obvious once one inspects the available *.build.xml files. They are generated automatically by verify.build.xml, and they can easily be generated directly from the code though Eclipse.

Examining the generated JavaDocs, all the packages and methods were documented. However, frequently the descriptions of the methods and parameters--particularly in the processor package--added very little information that wasn't already obvious from the name of the method, parameters, or return type. At least most of these names were fairly descriptive. Some classes--such as FakeCommand and ReallyFakeCommand--were not documented as to why they are needed.

Use of reflection. The project uses reflection to dynamically find the implementations of the various CLI commands. While this "drop in" modularity is very sexy in theory, the execution leaves something to be desired. First of all, reflection is always verbose. This is compounded here because two different approaches are needed to cover two possible situations: the classes are normal files in a filesystem or the classes are packaged in a jar file.

The jar file approach is brittle and will break if the containing jar file is renamed. Also, this approach cannot (easily) be tested by a JUnit test while the project is not yet packaged in a jar.

A JUnit test exists for the filesystem case. However, it is poorly written in that it imports and uses the very class (FakeCommand) that it is trying to find and load through reflection. Thus, if that command class is actually missing, the JUnit test doesn't just fail, it crashes. Also, the class discovery code itself makes assumptions about the state of the filesystem that don't always hold. For example, I am using both SVN and Eclipse. When I compile the source code in the src/ directory, Eclipse also copies the corresponding .svn directories from src/ into the bin/ folder with the generated .class files. Then, when I run the JUnit tests, the command-loading code chokes on the extra .svn directories. (The JUnit tests run fine if executed through Ant because this instead executes the class files in build/classes/, which does not contain any copied .svn folders.)

Finally, special classes, such as FakeCommand and ReallyFakeCommand, were constructed for test purposes. Normally, this is a very good practice. However, these classes are now visible to the entire system, not only to the testing code. Therefore, they then have to be filtered out of the list of available commands at multiple points in the regular code, which is messy and error-prone. (ReallyFakeCommand is never actually used, so it's just clutter.)

I think the grads programmers did fairly well handling all of the technical difficulties brought on by this reflection-based approach. Kudos to them for getting it working at all! I just question whether the approach itself is worth all this clutter and overhead. In our own project, we decided it wasn't worth it.

Code Readability and Maintainability. In general, the code is readable. As mentioned previously, some more descriptive/informative JavaDocs would help though.

Some data could be centralized to a single location. For example, every test class includes its own copy of the URL of the WattDepot server. This would make maintenance a headache if this address changes.

Unlike our program, the grads project does not include a list of towers or lounges. This means they cannot print a list of valid input values for the user. Also, it means any error in a source name must be sent to the server to be discovered, which puts more strain on the server. However, on the plus side, it means the CLI program would not need to be updated as source names change on the server. I think a compromise between these two approaches might be best: have the program query the server for the current list of valid sources at start up, and then use that list to inform the user and to weed out invalid queries before sending them to the server.

Testing.
The code for the commands was somewhat verbose, but it was fairly well-tested with an amount of test code roughly equal to the amount of code being tested (100 to 200 lines per command). Jacoco test coverage results showed that most of the important code was being tested--usually 60 to 80% coverage. Untested code included such things as multiple catch blocks for the different kinds of WattDepot exceptions that might arise. This is fair, since testing all of these catch blocks would be more of a test of WattDepot's exception-throwing than of the CLI program itself.

As a design choice, all commands print directly to the screen when executed. This was the source of all the extra output from the JUnit tests.

Development history.
From the @author tags in the JavaDocs and the Issue history on the project hosting page, it was easy to see who worked on which part of the system. (It's also interesting to see some different programming styles evident in the code too, even though all developers formatted their code the according to the same standards.) The work was evenly divided between the three developers.

The developers also followed their own development protocols. After the first handful of commits, later code commits are clearly linked to the specific issues they resolved. The team practiced continuous integration by committing code regularly (nearly every day). Except for the period during which the WattDepot server was down, there was only one commit that broke the build and this was resolved in less than an hour.


Conclusion

This project satisfies the three prime directives. Excusing the little minor warts and blemishes that crop up in any large project, the program 1) accomplishes the task it was designed to do, 2) is sufficiently easy to download, install, and use, and 3) is documented and structured clearly enough that a new developer could contribute code to it without too much trouble.


Appendix: Testing Transcript

The following is an transcript of some of tests I ran on this program. I cleaned up the output a bit. As mentioned above, the original output prompt included a dozen lines of help message before each prompt.

C:\Files\Downloads>java -jar hale-aloha-cli-grads.jar
Server: http://server.wattdepot.org:8190/wattdepot/

Welcome to hale-aloha-cli-grads
Looking for commands....
Found command: energy-since
Found command: current-power
Found command: daily-energy
Found command: rank-towers

help

--Enter a command (type 'quit' to exit):
current power lounge
'current' is not a command!


--Enter a command (type 'quit' to exit):
current-power

Invalid arguments for current-power.
current-power [tower | lounge]
Returns the current power in kW for the associated tower or lounge.

--Enter a command (type 'quit' to exit):
current-power lounge

lounge is not a valid source name.

--Enter a command (type 'quit' to exit):
current-power Lehua

Lehua's power as of 2011-11-29 11:13:44 was 22.0kW

--Enter a command (type 'quit' to exit):
current-power Lehua with extra arguments

Invalid arguments for current-power.
current-power [tower | lounge]
Returns the current power in kW for the associated tower or lounge.

--Enter a command (type 'quit' to exit):
current-power Mokihana-C

Mokihana-C's power as of 2011-11-29 11:13:44 was 4.0kW

--Enter a command (type 'quit' to exit):
current-power Ilima-04-lounge

Ilima-04-lounge's power as of 2011-11-29 11:13:44 was 2.0kW

--Enter a command (type 'quit' to exit):
current-power Ilima-04-telco

Ilima-04-telco's power as of 2011-11-29 11:18:45 was 1.9kW


--Enter a command (type 'quit' to exit):
rank-towers yesterday today

Argument "yesterday" is invalid.

--Enter a command (type 'quit' to exit):
rank-towers 2011-11-24 2011-11-26

For the interval 2011-11-24 to 2011-11-26, energy consumption by tower was:
Lehua                            985 kWh
Mokihana                         1029 kWh
Ilima                            1098 kWh
Lokelani                         1181 kWh


--Enter a command (type 'quit' to exit):
rank-towers 2011-11-19 2011-11-26

The tower ranking could not be retrieved for the dates given.

--Enter a command (type 'quit' to exit):
rank-towers 2011-11-30 2011-11-26

End date must be greater than start date.

--Enter a command (type 'quit' to exit):
rank-towers 2011-11-24 2011-11-26 and more

Invalid arguments for rank-towers.
rank-towers:  [start date] [end date]
Returns a list in sorted order from least to most energy consumed between the [s
tart] and [end] date (yyyy-mm-dd)


--Enter a command (type 'quit' to exit):
energy-since Lehua-F 2011-11-27

No such source Lehua-F

--Enter a command (type 'quit' to exit):
energy-since Lehua-E 2011-Nov-27

Argument "2011-Nov-27" is invalid :2011-Nov-27

--Enter a command (type 'quit' to exit):
energy-since Lehua-E 2011-11-27

Total energy consumption by Lehua-E from 2011-11-27 00:00:00 to 2011-11-29 11:06
:30 is: 264.1 kWh

--Enter a command (type 'quit' to exit):
energy-since Lehua-E 2011-11-27 more

Not enough arguments.


--Enter a command (type 'quit' to exit):
daily-enegy Mokihana-B 2011-11-25
'daily-enegy' is not a command!

--Enter a command (type 'quit' to exit):
daily-energy Mokihana-B 2011-11-25

Mokihana-B's energy consumption for 2011-11-25 was: 92.1 kWh

--Enter a command (type 'quit' to exit):
daily-energy Mokihana-B

Not enough arguments.

--Enter a command (type 'quit' to exit):
daily-energy Mokihana-B 2011-11-25 more

Not enough arguments.

--Enter a command (type 'quit' to exit):
daily-energy Mokihana-B 25-Nov-2011

Argument "25-Nov-2011" is invalid :25-Nov-2011

--Enter a command (type 'quit' to exit):
daily-energy Mokihana-F 2011-11-25

No such source Mokihana-F


--Enter a command (type 'quit' to exit):
quit
quitting...

Saturday, November 26, 2011

Group Programming and Issue-based Project Management

I spent the last couple weeks doing some group-based programming on a small WattDepot project. The project was to build a command-line interface that lets a user poll the energy data collected from one of the dorms on the University of Hawai'i--Manoa campus. Different commands allow the user to see the current power consumption of a dorm tower or lounge, to rank the different dorm towers by energy consumed over a given time period, to see the energy use of a tower or lounge for a given day, or to see the total energy consumed by a tower or lounge since a given date.

I haven't done any group programming in about 10 years, but I found the return to it quite enjoyable. I find it motivating to know that someone else depends upon and is eagerly awaiting my changes. I found myself prioritizing this project over my other projects to make sure I wasn't holding someone else up.

Our group consisted of only two programmers. We used Google Project Hosting's issue-tracking system to manage the project. We met occasionally to map out the course of the project, and we would create new issues for tasks that needed to be done. Then one of use would accept responsibility for the task and work on it. Once it was done, the other person would look over it and mark the issue as verified. Since the different issue pages allow for threaded discussion, I found this is a good way to track the progress of the project. It was also very easy to link from source code commit logs to the relevant issues and also back from an issue to those commits that solved it.

We happened to worked in shifts, and so we never had to deal with a code merge. I'm a little disappointed I haven't had a chance to experience this yet.

We also used continuous integration. That means we had a Jenkins server setup that independently verified that each commit to the Google repository still compiled successfully and passed all tests and QA checks. This was a comfort. After a commit, I'd always wait a few minutes to make sure I didn't receive an email from Jenkins. When I didn't, I felt more confident signing off for the night.

Our project was plagued with external problems, though. I had to make a emergency 5-day trip back to the mainland for a funeral during the middle of the project. My partner, Jeff, broke his leg around the same time. Besides being on painkillers after that, he wasn't able to drive to campus. So we only had one initial face-to-face meeting, but we compensated with a couple phone meetings and nearly-daily overview emails in addition to the issue-tracking threads. Near the end of the project, the WattDepot server went down, which halted production and then broke all of our test cases. This meant that our build appeared broken for about 3 days while we waited for the new server to generate enough data for us to work with.

All this planning and coordination certainly did come with an overhead. I'd say this project took 2 to 3 times longer than if we'd just done the project independently. But, like I said, I found the group atmosphere quite motivating, and it probably helped to have a couple pairs of eyes look over all the code.

Tuesday, November 8, 2011

WattDepot Katas

So I spent part of this week and all of this evening getting up to speed with WattDepot by completing a few practice katas. Each kata took me about an hour, though for different reasons.

The first couple katas were simple requests from a WattDepot server.
Given the sample code from the WattDepot client tutorial, this required only a big copy-and-paste and the modification of a couple lines.

These still took a couple hours, though, because I did the first couple katas in class on a day that we tried pair-programming. I think this might be a good practice once both programmers are comfortable with the API and understand the problem at hand. As it was, we spent most of the time setting up our Eclipse projects and then reading the assignment and the WattDepot documentation over each other's shoulders. I also switched groups halfway through, and we were also required to swap laptops periodically. This means the second pair I was in ended up starting over again at the beginning of one of the katas on a different machine.

Lulled into a false sense of security that these katas would all be as easy as the first couple, I returned to the project late this afternoon. I first took some time to install the FindBugs plugin in Eclipse. (I've been gradually installing plugins for tools that I have so far been using Ant to run. It's generally tedious and slow to run Ant compared to a plugin. But it's been a slow transition since I like to spend an hour or so reading the documentation and playing around with each plugin before I install the next one.)

After cleaning up what code I threw together in the last few minutes of class to see it work, I finished up kata #2. That kata and the ones after it all require sorting a list of key-value pairs by value. This stumped me a for a little while because this should be very easy... but it's not. I surfed a few 1/2 to 1-page posts of code that sort a Map by value. There were a few nice one-liners, but they required Google Collections and I didn't want to mess with an extra library.

In the end, this is my solution to sorting a key-value list by value. It's conceptually a "two-liner", but all the generics and the anonymous class make this look horrible:

  Map<String, Long> map = ...  //the map to be sorted by value

  //dump map into a list of key-value pairs...
  List<Map.Entry<String, Long>> entryList = 
      new ArrayList<Map.Entry<String, Long>>(map.entrySet());
      
  //...and sort it with a comparator that sorts based on values
  Collections.sort(entryList, new Comparator<Map.Entry<String, Long>>() {
      public int compare(Map.Entry<String, Long> left, 
                         Map.Entry<String, Long> right) {
        return left.getValue().compareTo(right.getValue());
      }        
  });

  //now print the list in value order
  for (Map.Entry<String, Long> entry : entryList) {
    System.out.print(entry.getKey() + ": " + entry.getValue());
  }

This solution also assumes you don't need map-like access to the sorted version, but just need to run through it for a simple task like printing.

The other time-consumer was learning how to use java.util.GregorianCalendar and
javax.xml.datatype.XMLGregorianCalendar. (There's also org.wattdepot.util.tstamp.Tstamp,
though I didn't find this to be as useful as I'd initially hoped.) This was another example
of something that should be easy but isn't. For example: "Give me the date of the most recent
Monday". It seems we have to start with the current time, for which I chose to use a GregorianCalendar object. Then, after a few lines spent modifying that time to the desired relative Monday date,
I had to translate it to an XMLGregorianCalendar. This was fairly long and messy with the various required
factory objects and exception handlers. I still like Java a lot, but I must admit that detractors
have a point about how much grinding tedious boilerplate you have to wade through sometimes.

I also need to take a minute at some point to figure out how to control Eclipse's insertion of try/catches. The quick default just wraps the one line in a try block, which requires so much reformatting that the feature is hardly worth using in most cases.

In the end, I got all the katas done. This was the first time I've written an application that deals with a web connection behind the scenes. It is pretty slow and variable (read: flaky) at times due to network access delays and potential connection time-outs. For my last kata--which took a couple minutes to gather all the data it needed--I added a little "status indicator" by printing a series of dots.

I spot-tested my katas as I went. However, if they were production code, I think I'd like to test them a little bit more to make sure I'm really pulling the right data and that all my calendar manipulations worked correctly. But since they are mainly katas intended to practice solving the problem, I think they went quite well.

Tuesday, November 1, 2011

Energy Consumption in Hawaii

I'll soon be working with WattDepot, so this week I've been learning a bit about energy consumption in Hawaii as background.

First, I had to review bit of high school physics regarding the difference between energy and power. Power is rate at which work is done or energy is converted over time. Power is measured in watts. One watt is equal to one joule/second. So a 60W lightbulb burns 60 joules/second.

Energy is... well, energy--the stuff that makes things move, go, change, or happen. It is force applied over a distance. When related to household electricity, it is usually measured in kilowatt-hours, or kWh. One kWh is equal to 3.6 megajoules.

I also learned that Hawaii is in a pretty unique situation when it some to exploring alternate/green energy sources. Our energy on Oahu costs about 30 cents per kWh, which is 3x what it costs on the mainland. (And on the neighbor islands it costs 6x mainland prices!) This is because we rely predominantly (70+%) on shipped oil, whereas the mainland can also rely on nearby coal and natural gas sources. We have a number of small, discrete electrical grids here, rather than the massive interconnected grid that spans much of the mainland. This means we can't buy, sell, or shift extra energy around to neighboring states (or even neighboring islands) as demand shifts. This is fairly inefficient.

But, on the bright side, this expensive energy means that alternative energy sources may be more attractive here in Hawaii than they would be on the mainland. The Hawaii Clean Energy Initiative has also mandated that 70% of Hawaii's energy come from clean sources by 2030. To our benefit, we have nearly every kind of potential renewable energy source here: wind, solar, waves and tidal motion, hydrothermal, and geothermal sources.

Data about current energy use and how this usage will be affected by alternative sources will be essential. This information can also provide feedback to end-users to help them reduce their consumption, which is also an important goal. There is currently some interesting research going on at the University of Hawaii regarding smart sensors and tracking current use... which brings us back to WattDepot. It is currently being used to track energy use in some of the UH dorms.

I'll be learning more about the specifics of WattDepot this week. Check back again soon for more info!

Thursday, October 20, 2011

Collaborative Review

The students in my software engineering class are reviewing for the midterm.
To contribute to the group study session, here are 5 review questions (and answers)
of my own:



1) Name three features often provided by an IDE.

The most common features include:

  • Language-aware editing (syntax-highlighting)
  • Integrated compilation (and execution)
  • Integrated debugger
  • Project definition facilities

Extra features may also include: wizards (class creation, etc), content assistance, refactoring, diagramming, project file manager, plugins (for tools such as configuration management, QA testing, JUnit, etc).


2) Give an example of an annotation tag commonly used in Java.

The Java compiler recognizes: @Override, @Deprecated, and @SuppressWarnings.

JUnit uses: @Test, @Before, @After, @BeforeClass, @AfterClass, and @Ignore.

Annotations can also take parameters, as in: @Test(expected=UnsupportedOperationException.class).


3) Which two methods in class Object should you override if you suspect that instances of your class might some day be need to be stored in a Collection?

equals and hashCode.


4) How do you use @Override in Java? What does it do?

@Override is an annotation that you should add at the beginning of a method definition if that method is intended to override a method in a superclass or interface. Example:

  @Override
  public String toSting() { ...

Thanks to the @Override tag, I would now get a compile-time warning that toSting() does not actually override a method. (This is because I misspelled String.)

Using @Override when overriding interface methods is only supported with Java 1.6+.


5) Ant is a build system. Name another one.

Maven and Make are also build systems.

Project Hosting: Hosted for the Very First Time

This week added another tool to my programming toolbelt: project hosting and software configuration management.

When working on my dissertation project, I used Subversion (SVN) locally to keep track of different versions of the project's source code. This meant I didn't have to worry about making a big implementation mistake because I could always rollback to a previous verion. Since I was the only programmer, I only committed weekly. The commit notes made for a good log of each week's work.

But this week, I setup a test project at Google Project Hosting for my BusyWeek robocode robot.

Setup went pretty smoothly. Google provides a pretty standard and simple interface to all their services, so nothing was too surprising there.

The built-in wiki and bug-tracking tools are pretty nice. (I haven't had a chance to explore the bug-tracking yet, though.) The wiki markup can be annoying--but isn't that true of every wiki system? Personally, I found the requirement to indent every * list bullet by two spaces rather annoying. And I couldn't seem get my numbered list to continue if I included a code block within one of the points. While all the various wiki markups are supposedly easier, I find I prefer HTML in these cases because then I have clear and explicit control. But this was all pretty minor stuff. The Preview button was a lot of help here, so that I didn't fill the revision history with minor edits.

It's also a little strange that editing a wiki changes the revision number on your software. However, I suppose the contents of the wiki and the contents of the code base are highly related, so this approach probably provides a clearer project history.

I did get side-tracked in selecting a license, though. I still haven't found a simple license that I really like, especially among the OSI licenses. Since my Ant build files and one of my test files is based on a DaCruzer robot project, I figured I had better use the same license. (This is fine, since I like the Apache 2.0 License.) However, neither my project or DaCruzer actually includes any of that license information in the source code itself. I should probably look into that at some point.

Anyway, it's exciting to actually have a project hosted somewhere now! Fun stuff.

Tuesday, October 18, 2011

BusyWeek: Programming Under Pressure

It's been a busy three weeks! I completed two programming projects in that time (as well as a lot of other work). The contrast between how those two projects went provided me some interesting insights on testing and general best-practices.

Three weekends ago, I completed a hefty implementation project for an algorithms course I'm taking. I had to implement and gather runtime data on 4 abstract data types: a sorted doubly-linked list, a skip list, a binary search tree, and a red-black tree. All of the implementations adhered to the same interface. I then had to write a text-based user interface to allow a user to run performance tests as well as call methods across all 4 implementations. All told, the project took me about 30 hours over 3 or 4 weeks--though half of that time was put in over the last two days. But the project was a success. (I had one known bug at the midnight due date, but I was able to fix it and resubmit an hour later.)

I think part of that success was in how I spent my time. I started early and spent that time designing the common interface, implementing the easiest data type (linked list), and writing a single battery of JUnit tests that I could then run across all 4 implementations. When crunch-time came, I was confident that my tests were robust. I could just focus on the implementation. When all my tests passed, I felt I was on pretty solid ground. In short, test-driven development eased that last-minute stress.

My other recent project was to design and test a robocode robot.

My original design for this was named JiggersTheFuzz. (The name is an obscure reference from an old Sierra game named Police Quest.) I planned to use linear targeting, which means anticipating a target bot's location by leading their current position by an amount relative to their velocity. I planned to scan enemies for energy drops that mean they'd just fired and then try to dodge their bullets (the "jigging" of the name). If robocode bots want to "mark" another bot, they often use the getName() method. So I wanted to dig into Robocode and the runtime stack a bit to see whether I could override my own getName() to return a different String each time but in such as way that it didn't mess up the robocode simulation itself. This would be like a radar jammer (the "Fuzz" of the name): other bots would still see me, but they wouldn't be able to identify me between turns.

However, it was not to be.

Two weekends ago, I had a conference in California. While I managed to get caught up on everything else before I left, this robocode project followed me onto the plane. I tried working on it in my hotel at 9:30pm after two sleep-deprived nights and a long conference day. I tried again at 5:30am. It just wasn't happening.

I had a plan sketched out, but the details still involved too many unknowns. In short, I hadn't started early enough to be on solid ground come crunch-time. I started paring away features as the deadline neared. The first to go was the radar "fuzzing" (so I never got to look into that one). The second to go was scanning other bots to track their energy level. (This is actually very hard to do in robocode when extending Robot rather than AdvancedRobot, since a Robot can only do one thing at a time: turn its radar, move, or fire, but not at the same time.) I was down to just moving regularly in the hopes of dodging bullets and generally being unpredictable... but I wasn't handling wall collisions at all. There was little left to pare away and still have a functional bot.

At this point, I called it: This project is going to be late.

So I renamed it BusyWeek. I kept the linear targeting and the naive bullet-dodging, and, once I got back to my normal life, I sat down to do it right. Last weekend, I finally finished my bot.

Movement: BusyWeek assumes 1-on-1 combat. Once I scan an enemy, I turn to be perpendicular to him. Then I move 20px at a time, which is a touch more than the width of my bot. Thus, if he happens to shoot just before I move, I'll be out of the way by time the bullet arrives. Of course, this assume he's at least 140px away and firing small fast shoots. (He can be only 77px away if firing big slow shots.)

To ensure I'm this optimal distance away, my bot tries to stay between 140px and 180px away. (Farther than that, and it's too hard to make my own shots reliably.) If I'm currently outside of this optimal range, I'll turn to 45 degrees (instead of 90) from my enemy's heading and move that way. I handle walls by moving to the opposite side of the enemy's heading to avoid wall collisions. If I've been backed into a corner and can't avoid a wall collision in either direction, then I try to ram the opponent instead.

Targeting: As mentioned, I used a form of linear targeting to lead the enemy based on his current heading, velocity, and distance from me. I decided to include a "fudge factor" of treating his velocity as one less than it actually is. This was because it seems that stops and turns are more common behaviors than continuing along in a straight path.

Firing: BusyWeek shoots bullets with a power proportional to its own current power level. This way I don't shoot to the point of becoming disabled. Since smaller bullets move faster, it also means I hopefully become more accurate as things get more desparate. Also, I don't even aim at an enemy if my gun is still too hot to fire. This saves me some time for scanning and moving instead.

This design worked fairly well against the standard sample bots. Here are my win rates over 100 non-deterministic rounds each:

vs sample.SittingDuck: 100%
vs sample.Tracker: 79%
vs sample.Corners: 85%
vs sample.Fire: 99%
vs sample.Crazy: 100%
vs sample.SpinBot: 67%
vs sample.RamFire: 60%
vs sample.Walls: 76%

Testing this bot proved to be a trial, though. I used a number of computational methods--such as finding a point on the board based on a starting point, a heading, and a distance. Testing these using JUnit tests went fine. I only wish I'd written the tests first. I actually wrote most of these methods for some of my kata bots weeks ago, and so I was already confident they worked. This takes all the fun out of test-writing.

Testing the behavior of my bot during the simulation proved to be trickier. For example, early in my design I decided that it'd be easy to test that my proportional-bullet-power feature was working correctly. (Testing this for bugs was a bit like only looking for lost keys under a streetlight--not because I expect to find them there but simply because searching in the light is much easier!) I simply paired BusyWeek with a bot that always fires shots of power 3. Then I used the RobotTestBed to snapshot all the bullets in the air each turn. I figured if some of the bullets decreased in power as the battle progressed, then it meant my algorithm was working. However, while the test passed, when I set my bot to only fire shots of power 3 too, I couldn't make my test fail! After some digging, it turns out that a bot--even a bot like SittingDuck that never fires--effectively fires a single shot of power 1 when it explodes. So I figured I'd just exclude all bullets of power 1 from my test. But then I realized that sometimes the other bot would fire lower-powered shots near the end of the match when they didn't have enough energy left to make a full-powered shot. Sorting through all these details took over an hour--effectively debugging the flickering of the streetlight rather than looking for my keys. Finally, I gave up: this was all to test a single line of code that I could just eyeball!

So, in conclusion, I learned the following:

  • Start early and get the design and necessary research out of the way. This will let you make a more realistic estimate of how much time the rest will take you.
  • Write tests before you write code. (Or, at very least, write them together.) Writing tests after the fact is drudgery.
  • Write code so as much as possible can be unit-tested. Trying to test emergent or complex behavior through a simulation or an user interface is much harder.
  • Make sure your test can fail before you rely on it when it passes.
  • When you're at a conference, you're not going to have either the time or the energy to work on anything else.

Admittedly, these lesson are all very standard software engineering tips. And I knew most of them, at least consciously. But it's nice to learn viscerally that they really are valid!

Wednesday, September 28, 2011

Ant Katas

So I'm continuing to pick up programming skills. This week, I played around with Ant.

Ant is a build system for Java projects, like make is for C projects. Like all build systems, its primary function is to let you write scripts that intelligently compile the source code files in your project. But it lets you do a lot more than that. Directories referenced in a build script can be relative to the project's directory, meaning its easy to transport the whole project to a different system without having it break. Ant handles any differences across OS environments, such as whether the local system uses \ or / as the path separator. You can write scripts to automate other tasks, such as running test cases, executing other tools like as javadoc, or cleaning up stray files. The scripts can also test conditions in the local environment and adapt their behavior accordingly. When combined with Ivy, Ant can also do dependency management for you, downloading any 3rd party libraries required by your project. In short, Ant lets you automate a lot of routine programming tasks for a specific project. And it does this in a transportable way, across OS environments, so that anyone else who downloads your project can perform those task out-of-the-box too.

To get some practice writing Ant scripts (which are specified in XML), I went through a few katas for a simple Hello, World program. The Ant tasks involved compiling, cleaning, generating javadocs, and packaging the system for distribution.

What I learned from this is that the Ant Manual takes a bit of getting used to. It provides a decent tutorial, but it can be hard to use as a reference. For example, the manual includes a extensive list of all the possible task tags that Ant supports, including documentation of all the attributes they support. But for a while, the only place I could find a link to this important list was an in-text link buried in text of the "Using Ant -> Tasks" manual section. (In writing this post, I found there is a more direct link under Ant Tasks in the Table of Contents, but this is easy to overlook because its location between "Running Apache Ant" and "Concepts and Types" makes it look like a tutorial chapter title.)

I had a bit of trouble using Eclipse write these scripts. Eclipse has a special Ant editor which provides some helpful syntax highlighting and content assistance. However, I couldn't find a way to create an Ant file directly. Instead, I had to create a normal XML file, then right-click on it and open it in the Ant Editor (which required some browsing to find among the various editor options). Once opened there, it was possible to just hit the run button to run the Ant script, which was handy. And I only had to do this once for each file; after that, Eclipse remembered that it was an Ant file.

So I've covered the basics of Ant at this point. I'd like to mess around with filesets and paths a bit more. When I get a minute, I'd also like to figure out how to exclude Eclipse contents (such as the /bin directory and .project file) from distribution zips I build with Ant. Then, since I'm still learning Eclipse too, I want to see how Eclipse handles importing projects that don't have any Eclipse metadata included.

Tuesday, September 20, 2011

Robocode Katas

So for the past week or so, I've been coding robots. Well, virtual robots.

Robocode is a software architecture for constructing little tank-like robots. Each tank is equipped with treads, a rotating gun, and a scanning radar. You can then run battles to the death between the different tanks. Robocode handles all the game physics and animation so that you can just focus on the tank logic.

I've dabbled with virtual robots before. I teach an Introduction to Computer Science lab, and, for the past 2 or 3 years, I've had my students implement a virtual roomba as the last assignment. Like Robocode, I provide the architecture, rule system, and GUI interface, and students then provide the "vroomba" logic. After mastering such a simple system, I was excited to try something a little more complicated.

Luckily, this Robocode work has been part of my Software Engineering class. (Sadly, I probably wouldn't be able to find the time to play around with it otherwise!) We were given 13 "code katas" to bring us up to speed with Robocode. A code kata is basically what it sounds like: a challenging programming exercise intended to build skills. Since the purpose of a code kata is mindful practice, failure can be a learning experience rather than a costly embarrassment, as it would be if the same lessons were being learned as part of a large production project.

These katas were a great way to pick up Robocode, and I recommend them to anyone just starting out with it. Each one tackles another aspect of tank management.

Here are the katas, and what I personally learned from each one. (Most of these lessons were fairly technical, though, and so they may not be of much use to you until you try Robocode yourself!)

Position01: The minimal robot. Does absolutely nothing at all.

Learned: How to create and run a Robot. This is basically the "Hello, World" of RoboCode.

Position02: Move forward a total of 100 pixels per turn. When you hit a wall, reverse direction.

Learned: You can call the ahead(int) method with a negative value to move backwards. Same with the turning methods. A move ends early if you hit something, whether a wall or another tank.

Position03: Each turn, move forward a total of N pixels per turn, then turn right. N is initialized to 15, and increases by 15 per turn.

Position04: Move to the center of the playing field, spin around in a circle, and stop.

Learned: After 15 years of neglect, my trigonometry is very rusty!
It took me over 2 hours to get a working method to compute the absolute heading from Point 1 to Point 2. It seems that Robocode lacks a method for this sort of calculation, though it does have a method for plotting a heading towards another bot. I also learned how to use Eclipse to
refactor methods into a new superclass and proved that the Scala interpreter does indeed make for a very handy interactive Java console.

Position05: Move to the upper right corner. Then move to the lower left corner. Then move to the upper left corner. Then move to the lower right corner.

Learned: I needed to account for the tank's dimensions in order to make it into the corners without hitting the walls on the way in. It seems the (getX(), getY()) point is the tank's center. getWidth() and getHeight() give the bot's dimensions, though, so dividing these by 2 gave me an effective tank "radius". (I added 1 pixel to this value, just to be on the safe side.)

Position06: Move to the center, then move in a circle with a radius of approximately 100 pixels, ending up where you started.

Learned: Circles are too hard/tedious with a Robot (which, unlike an AdvancedRobot, can't turn while moving). So I just traced a diamond here. I also took a break to learn how to color my tank. Important lesson there: setting colors needs to be done in run() or later. Trying to do it in a constructor will cause the bot to explode at the start of each round.

Follow01: Pick one enemy and follow them.

Learned: This got me comfortable with the onScannedRobot(ScannedRobotEvent) method in particular and Robocode's event-driven approach in general. It's initially weird how frequently an infinite while loop in run() will be suspended by other events.

Follow02: Pick one enemy and follow them, but stop if your robot gets within 50 pixels of them.

Learned: This was a surprisingly simple modification of Follow01. Since a negative distance to
ahead(int) moves the bot backwards, I got some cute retreating behavior for free here.

Follow03: Each turn, find the closest enemy, and move in the opposite direction by 100 pixels, then stop.

Learned: Each robot has a unique name accessible with getName(). This is true even if there are more than one instance of the same bot class on the board at a time. From a classmate (Ted), I learned that you can turn the radar (or anything else) to Double.POSITIVE_INFINITY to turn forever without needing an infinite while loop.

Boom01: Sit still. Rotate gun. When it is pointing at an enemy, fire.

Learned: The gun takes a while to cool down. So, even though I continued to rotate my gun, scanning multiple bots per rotation, I could only fire a bullet about once per rotation.

Boom02: Sit still. Pick one enemy. Only fire your gun when it is pointing at the chosen enemy.

Learned: ScannedRobotEvent.getHeading() is the heading in which the scanned robot is pointing, not the heading from you to that bot.

Boom03: Sit still. Rotate gun. When it is pointing at an enemy, use bullet power proportional to the distance of the enemy from you. The farther away the enemy, the less power your bullet should use.

Boom04: Sit still. Pick one enemy and attempt to track it with your gun. In other words, try to have your gun always pointing at that enemy. Don't fire.

Learned: I set my radar to keep rotating in my run() method. Yet I found that the radar would stopped rotating whenever I tracked a moving tank with my gun. This produced some cute behavior, like a little dog that stops panting and puts his ears up while his
target is moving, but relaxes again once the target stops. But it's still a little weird. Presumably, ScannedRobotEvents fire so frequently that, if I update my gun position for each one, Robocode doesn't have time to get back to my run() method before another ScannedRobotEvent fires.



So not every bot here taught me something new, but all were good practice.
Trig review aside, I found the hardest one here was Boom02. I think I may have misinterpetted the requirements for this one, though. I actively tracked the bot with the gun rather than waiting for the bot to pass in front of me. This made Boom04 a bit redundant.

Now that I'm familiar with Robocode, I'm looking forward to designing a combat-ready bot! More on that soon...

Wednesday, September 7, 2011

A short-yet-satisfying open-source license

I've been looking for a good, short, open-source license. This is intended for small projects, such as scripts, that might comprise of one or a handful of files. So I'm looking for a license that is only 2 or 3 paragraphs long that can easily be pasted into the header of the source files. (For larger projects, I would consider something like GPL or Apache License 2.0.)

I've been browsing the Open Source Initiative's list. I'm aware of license proliferation, so I'm trying to pick one that already exists. However, each license seems to have something different to recommend it.

MIT License - I like how short and yet explicit this one is. For example, it explicitly includes the ability to sublicense, so it is obviously compatible with other additional licenses. The only requirement to using the code is to keep the license text (including waiver of liability) and copyright notice (which also acts as an attribution) in place.

BSD 2-Clause License - The nice thing about the BSD license over MIT is that it's a little clearer that the license to freely redistribute also applies to binary (compiled) forms of the work. The 3-Clause version includes an extra clause that prevents any modifying authors from using the names of the original author or contributors in marketing the derived version.

This is a good reminder that, while I want to release my code and have it forever known that I am the originator, I also don't want that reputation tarnished by poor-quality modifications done later by others. In short, I'd like to require that any modifications to the software be clearly marked in some way, which neither MIT or BSD 2-Clause really deal with.

zlib - I think zlib does a good job of dealing with this "don't misrepresent me or my work" issue. However, I don't find the rest of the license as clean and precise as MIT on the main points of what you're allowed to do with the code and that the author(s) are not providing any sort of warranty.

Another issue that none of these licenses really handle is specifying how to clearly mark that the software has been modified. This is something I think Wizard of the Coast's d20 Open Gaming License handled very well: It required later contributors to add their own copyright statement the end of the license, beneath the intact notices from previous contributors. What I like about this is that 1) it's a simple mechanism--just add a line of text, 2) contributors generally want to add their name to a work anyway in order to receive recognition too, and 3) it's clear who the last contributor was, which protects the reputation of earlier contributors from "poor" later modifications.

So, while I was trying to avoid license proliferation, I couldn't find a single short license that was satisfying. I like the MIT License the best, except for a missing "clearly mark that this work has been changed so that you don't misrepresent my work" clause. So I borrowed that from zlib. I also added an explicit requirement that a later contributing author append a copyright notice. Here is my finished "MIT License with Modification Clause":

Copyright (c) <year> <copyright holders>

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

1) The above copyright notice(s) and this permission notice shall be
included in all copies or substantial portions of the Software.

2) Modified versions of the Software must be plainly marked as such, and
must not be misrepresented as being the original software.  If you modify
this Software, append your own copyright notice to the list above.

3) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, express
or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose and noninfringement. In
no event shall the authors or copyright holders be liable for any claim,
damages or other liability, whether in an action of contract, tort or
otherwise, arising from, out of or in connection with the Software or the
use or other dealings in the software.

This is still not a perfect license, though. Unlike BSD, it's a little vague as to whether the license should also be included with binary forms of the software. (I think "all copies or substantial portions" should be read to cover binary versions, though this is not explicit. For the most part, I plan to use this license with libraries or scripts, where the distributed form is the source code itself, so this won't be much of an issue for me.)

Also, dealing with contributions (such as from a large project submitting changes through CVS or the like) is probably a little onerous. I considered requiring only those that publish or distribute a modified version to add a copyright notice. (Thus, modifying a private copy without notice would be fine.) However, it would be easy for someone to then unknowingly publish a version that had been previously modified by someone else. So each modifier should update the copyright notice list. It would also be fine if contributors are waiving or granting their copyright to a project (through some separate agreement between project and contributors) and then only the project or licensor is listed as the modifications copyright holder in the license. For example, consider Section 5 of the Apache License 2.0.

Note that, like all the source licenses, this license is not copyleft: modifiers are not required to release their source code.

As stated above, I got the text of these original licenses from OSI. In practice, everyone freely copies the text of these licenses without attribution. However, I'm not entirely certain I have the right to modify this text--except through the Creative Commons Attribution license at the bottom of all of OSI's pages. So, while I waive copyright to my own contributions to the above license text (meaning you may freely copy or modify it without my permission), you may still need to be attributing the text of the original license to OSI.

Copyright issues aside, there's another problem with modifying the MIT license: The license above is no longer an approved OSI license. That means it cannot legally be called "Open Source" because OSI holds a trademark to that term. The term can only legally be applied to software licensed under approved licenses.

So I'm still thinking about whether to actually use this new license or not. An uglier approach (but one that makes OSI happy) is to license software under both MIT and zlib together. Or possibly to only "politely request" that clause 2 be followed without making it an official part of my license.

While I think about it, any comments or suggestions are welcome...

Wednesday, August 31, 2011

Controlling Blogger's BR tags

I usually write in HTML mode when I use Blogger. This means I tend to put in my own <p> and other formatting tags. But when Blogger replaces every line-break with a <br>, this causes some ugly formatting. <pre> blocks, in particular, come out double-spaced.

Here's a solution I came up with. I added the following CSS to my blog's template:

.entry-content BR {display: none;}
BR.real {display: inline;}

This means any <br>s added to my post's content are ignored. (I added the .entry-content context to avoid conflict with any <br>s used by Blogger elsewhere on the page, although I don't think there usually are any.)

The second line lets me add <br>s of my own provided I do it like this:

<br class="real">

Feel free to replace "real" with your own preferred class name (provided you do it in both the CSS definition and and in the HTML when you use a <br>).



APPENDIX: There is a potentially easier solution. Just switch to the new editor under Blogger's Settings -> Basic. However, in my limited fiddling. it still behaves a little oddly as Blogger tries to guess where you want line breaks and where you don't.

Tuesday, August 30, 2011

Settling into Eclipse


As part of my current software engineering course, I've installed the latest version of Eclipse (Indigo). I've used Eclipse before, but not for some time, so I'm going through a period of reacquaintance and reconfiguring.


As a settling-in exercise, I implemented the following FizzBuzz program:


/**
* The sort of problem given to intro programmers in a job interview:
* print the numbers 1 to 100, one per line, with "Fizz" replacing those
* divisible by 3, "Buzz" replacing those divisible by 5, and "FizzBuzz"
* for those divisible by both 3 and 5.
*
* @author Zach Tomaszewski
* @version 29 Aug 2011
*/
public class FizzBuzz {

/**
* Returns a String version of num or else "Fizz", "Buzz", etc. according
* to the rules of FizzBuzz. Applies the rules even if num is <= 0.
*/
public static String fizzBuzz(int num) {
if (num % 3 == 0 && num % 5 == 0) {
return "FizzBuzz";
}else if (num % 3 == 0){
return "Fizz";
}else if (num % 5 == 0) {
return "Buzz";
}else {
return Integer.toString(num);
}
}

/**
* Prints results of {@link #fizzBuzz()} for 1 to 100 to the screen.
*/
public static void main(String[] args) {
for (int i = 1; i <= 100; i++) {
System.out.println(fizzBuzz(i));
}
}
}


It took me 22 minutes to create a project, write the code, and test it. Since I wrote this program on paper in 3.5 minutes in class the other day, most of this time spent went into playing around with Eclipse.


Part of this time was adjusting to initially-annoying things that Eclipse does. For example, at one point it suddenly highlighted in grey all the return statements in the current method. I wasn't at first able to see why this happened or how to make it go away. I now understand that Eclipse does this highlighting for any variable or return statement you are currently lingering on with the cursor, and it won't change the highlighting until you move your cursor elsewhere.


I also tried to decide how detailed to get for something that is obviously a one-off practice program. For example, should I bother with a package? Should I bother documenting the simple methods using @param and @return tags? Should I prevent fizzBuzz(int) from accepting negative numbers? In the end, I decide no for all these things.


I did move the fizzBuzz logic into its own method, though, in order to make it easier to test. This is something I saw done in class. I wrote a JUnit test too:


import static org.junit.Assert.*;

import org.junit.Test;

public class FizzBuzzTest {

@Test
public void test() {
assertEquals("fizzBuzz(4)", FizzBuzz.fizzBuzz(4), "4");
assertEquals("fizzBuzz(9)", FizzBuzz.fizzBuzz(9), "Fizz");
assertEquals("fizzBuzz(20)", FizzBuzz.fizzBuzz(20), "Buzz");
assertEquals("fizzBuzz(45)", FizzBuzz.fizzBuzz(45), "FizzBuzz");
}
}


This is the first time I've used JUnit, but, for something this simple, it was pretty self-explanatory (once you know about the assertEquals method). I'm looking forward to learning more about JUnit--particularly best practices. Right now, I find writing the test descriptions to be unnecessary duplication. It's easy for the description of the test to get out of sync with the test itself. I'm guessing that perhaps test descriptions should be stated at a higher level: what the test is testing, rather than the details of how it is testing it (as I did here). For example, perhaps the second test here would be better described as "Divisible by 3".


I also set up SyntaxHighlighter to use on this blog for code samples. Various instructions for Blogger installation are here. I went through the extra work of hosting SyntaxHighligher on my own server and then linking to the necessary scripts from here. This took much longer than the above code.


Saturday, August 27, 2011

Timmon and the Three Prime Directives


Philip Johnson proposes Three Prime Directives to guide open-source software development. While provided as goals for developers to aim for, Johnson claims that the developer of a system cannot verify whether their own system actually achieves these goals. Only an external user or developer can do this.


As an example of using the Three Prime Directives as a framework for software review, here is my own review of Timmon Time Monitor. I've actually been a Timmon user for about four years now. Unable to explain how many of my days seemed to get away from me without much to show for it, I searched for a good time tracker program. Of the three or four programs I eventually downloaded and tried, Timmon was my favorite.

PD1: The system successfully accomplishes a useful task.



I think the reason I finally settled on using Timmon is that it is fairly simple. It does exactly what I wanted in a time tracker. I can define my own hierarchy of activities or projects. Then, with a click of a button, I can start tracking time for any of those activities. Another click of the same button stops the timer. It is also easy to edit the time spent on a task, to add a complete task that covers a given time period, or to add a comment describing the work done on a task.


It is then possible to view the accumulated time for all activities at different levels of the hierarchy. For example, I have a Personal category that includes Emailing and Surfing. For the current day, week, month, or year, I can easily see the total time spent either Emailing or Surfing. I can also see the total for both activities combined, plus any other activities I labelled as simply Personal.


Here's a screenshot of Timmon in action:




Timmon has a couple extra features I don't use, such as reports or setting the max time for a given activity.


PD2: An external user can successfully install and use the system.



It's been a while since I installed Timmon, so I had to revist the process. The project homepage lets you try Timmon as JNLP program, so, assuming you don't mind visiting the website every time you wanted to use Timmon and you don't mind giving the self-signed web application full access to your computer, it's actually possible to run Timmon without installing it all. This is a pretty nice feature. When run this way, Timmon still saves its time record in a number of XML files in a .timmon directory in your home folder, so this is a viable option for a novice user.


Installing is a little tricky because the Timmon project provides both a .zip and a .tar.gz. However, the .zip is damaged and unopenable, so you have to be able to unzip a .tar.gz on your system. Also, running the program requires executing a jar file. I think this is foreign to many users, though the main project page provides fairly clear instructions on how to do so.


The Documentation link from the project home page is broken. The project README file included in the download mentions a .pdf manual that doesn't exist. However, the download does include an HTML manual. The manual basically covers the program menus and buttons, so it doesn't add much beyond what could be learned by simply exploring the program. I think Timmon's interface is fairly self-explanatory, though; I don't think I ever used the manual when I initially installed it.


PD3: An external developer can successfully understand and enhance the system.



In the four years I've used it, I have discovered a few little bugs. For example, if I close Timmon while tracking time for a particular activity, Timmon will resume tracking for that task every time I open it. I have to delete the task and restart Timmon to break it of this. So I was interested to see how easy it would be to modify the program.


The project has been dead for about four years. The project README that it is included in the download explains how to build the project manually or by using Ant. The source code for the current version of the project is supposed to be in the damaged .zip file and so is not currently available for easy download. It is still available for checkout from the sourceforge CVS, though.


Browsing the CVS reveals that the code is well-formatted and well-documented with javadoc comments. So it would be easy to extract a javadoc overview of this project's files. However, I did not find an overview document intended for a new developer. There are a number of packages and classes here. Based on the README, the project also builds on 5 separate open-source libraries. With no high-level overview, it would certainly take a fair amount of work for a new developer to build a mental picture of how this program is structured.




In conclusion, I think Timmon clearly satisfies PD1 (does something useful). It does fairly well on PD2 (easy to install and use)--the .zip file and documentation link just need to be fixed. Though well-documented, Timmon needs to provide a short introduction or overview document for a new developer to really satisfy PD3 (easy to enhance).

Wednesday, August 24, 2011

Beginnings

I am starting this blog as part of a Software Engineering class, though I expect it to survive beyond the end of the course.