Pages

Tuesday, November 29, 2011

Putting It All Together: Software Tools and Issue-Driven Project Management

In this semester, I have been completing various skill-learning exercises called "katas" to quickly learn the basics of some software tools designed to make Java development easier, faster, and more error-free. Likewise, through my own struggles, successes, and failures, I have learned that these tools are only as good as the people who use them. Ultimately, any organization is only as good as its people, and no class on software engineering would be complete without at least one project that needed to be organized through a project team.

Over the last two weeks, I have been working with Christopher Foo and Eldon Visitacion to design a console application using the WattDepot API. Throughout the duration of the project, in addition to the Subversion, Ant, Eclipse, and JUnit tools discussed in past entries, we began using a class Jenkins server as a continuous integration tool. Configuring Jenkins to automatically build and test the project after each repository change helped us to pinpoint which Subversion commits introduced faulty code into the system and which developers were responsible, making us more careful about verifying our code in the first place. We used Google Project Hosting to manage our repository and a small wiki.

Our work and school schedules prevented us from meeting in person very often, so we designated issues and set intermediate deadlines mostly through email. Each group member was responsible for posting and closing their own issues and notifying the member assigned to a certain part of the project of any bugs they found while examining or testing each others' code, and all group members were responsible for testing the final .jar executable.

The Hale Aloha CLI

The console application's purpose was to retrieve data from the WattDepot server for the University of Hawaii at Manoa's Hale Aloha freshman residential complex, consisting of four towers where energy consumption and power data were being logged for the Kukui Cup. It needed to display the current levels of power being used, the amount of energy consumed on a given day, the energy consumed since a given day, and the rankings in energy consumption of the four towers from a given start date to a given end date.

The Command Line Interface

Command-line interface.
The command-line interface was designed by Christopher Foo. The Control class passes commands to the CommandProcessor class, which stores a TreeMap of classes which implement the Command interface. The Command interface specifies two methods. The first is an execute method, which constructs the object that carries out the command, initializes its fields, and prints the data retrieved from the server as a string. The second is a getHelpString method, which prints the correct syntax to use the command if the command "help" is issued. Typing "quit" exits the program. The available commands can be extended by adding the new Command-implementing class to the TreeMap.

Our team's implementation does not use Java's Reflect class.

Current Power

>current-power [tower | lounge]
current-power
The Current Power command was designed by Eldon Visitacion. It retrieves the newest available power consumption data recorded by a tower or lounge source's sensor on the current day. Data is in kilowatts.

Daily Energy

>daily-energy [tower | lounge] [date]
energy-since
The Daily Energy command was designed by Eldon Visitacion. It retrieves the newest available total energy consumption recorded by a tower or lounge source's sensor on a given date, in yyyy-mm-dd format. Data is in kilowatt-hours.

Energy Since

>energy-since [tower | lounge] [date]
energy-since
The Energy Since command was designed by me. It retrieves the total energy consumed by a tower or lounge source on a given date, in yyyy-mm-dd format. Data is in kilowatt-hours.

Rank Towers

>rank-towers [start] [end]
rank-towers
The Rank Towers command was designed by me. It retrieves the total energy consumed by each of the four Hale Aloha towers from a given start date to a given end date, both in yyyy-mm-dd format. Data is in kilowatt-hours.

JUnit Tests

JUnit tests were incorporated into our system fairly early on. The first draft tests for EnergySince were running by November 12 (continuous integration build 15). The first tests for Control, CommandProcessor, and Quit were posted by November 15 (build 25). CurrentPower and DailyEnergy tests first appear on November 16 (build 27), a test for Help was posted on November 17 (build 38), and the first version of TestRankTowers was also committed on November 17 (build 39). JUnit tests had mostly reached their final states by Build 54 on November 24, and the system stayed at a consistent level of at least eighty percent instruction coverage afterwards.

Since parts of our command implementations were dependent on the time reported by the server, there were some pieces of my code - mostly those that converted time information to strings or to some other format based on the current date - which could not be tested. Despite this, I was able to test most of the methods used in my classes, especially the utility methods responsible for creating the XMLGregorianTimestamp objects used to retrieve energy consumption data. As I had chosen to make all of my setter and utility methods private, I had to learn to use Java's Reflect class to invoke them in tests. Oracle's tutorial and Lasse Koskela's exception testing examples were very useful here.

Another odd problem I had was using Reflection to invoke a method with a parameter that was a non-primitive-type array. If the parameter is just declared as foo[].class, Java interprets it as a varargs parameter instead of an array. In this case, I was trying to test the setter method for an array of XMLGregorianCalendar timestamps.

The correct way to do this, described at Your Mitra here, is to declare the array as a new class which contains the array:

Method testSetEnd = testClass.getDeclaredMethod("setEndTimestamps", new
Class[]{XMLGregorianCalendar[].class});

Then, in the method invocation, the array being passed as a parameter needs to be cast to a new class as well:

XMLGregorianCalendar sampleTimestamp =
Tstamp.makeTimestamp("2011-11-11T11:11:11.111-11:00");
XMLGregorianCalendar[] holdTimestamps = new XMLGregorianCalendar[]{sampleTimestamp,
sampleTimestamp};
// Cast the array to an object, otherwise it will be treated as varargs.
testSetEnd.invoke(testObject, new Object[]{holdTimestamps});

The large number of date-processing utility methods in my classes made it difficult to test all possible branches of the code, especially those which executed based on times retrieved from the WattDepot server. We also did not implement tests for the main method in the Control package, which we tested manually. JUnit’s policy of factoring in what percentage of test case code was executed greatly decreased the branch coverage score: the test which branched based on the time returned from the server executed only a few of their branches in the average unit testing. In the end, we achieved roughly 85% instruction coverage and 80% branch coverage.

Our software system is thoroughly documented and contains input-checking code that ensures that the right number of command-line arguments are given, dates do not overlap with each other or exceed the current date, and that source names match with an existing source's name, and most or all of these input checks are intentionally triggered in each class's Junit tests by passing bad input to the each Command's execute method. In addition, all of the Commands which take date parameters are capable of collecting data for the current day up to the most recent data reading from the requested source. Though it has its flaws, I consider it to be fairly high-quality in its error checking, operation, and test case coverage.


Server Issues

Though it had been difficult and at times annoying to get used to continuous integration practices, it was harder to coordinate system changes when the Jenkins server briefly crashed, or when it began automatically failing builds when the WattDepot server crashed and our Junit tests failed automatically after being unable to establish server connections. As the cliche goes, "You don't know what you got till it's gone." For all the annoyance of having a Jenkins build fail over some minor formatting error, it was still preferable to having that error remain in the code because the committer forgot to verify it beforehand.

In the end, Jenkins forced me to adopt better development practices by encouraging me to obsessively run Checkstyle, PMD, FindBugs, and JUnit tests - both in order to commit workable code, and to avoid having my usually careless errors broadcast to my team members. This makes continuous integration an effective tool for pinpointing which committers are responsible for errors and encouraging better practices (though maybe not quite as effective as a foam rocket to the head).


Debugging A Time Problem

The other server-related issue I experienced was in retrieving energy consumption data from the server at midnight. The WattDepotClient.getEnergyConsumed method retrieves data given a source name, two XMLGregorianCalendar objects representing the start and end times for data collection, and an integer representing the sampling interval in minutes. The original versions of my code had the interval set to 15 minutes (or 96 data points per 24-hour period). I noticed that JUnit tests would always fail mysteriously for current-day data requests made within about twenty minutes of midnight, throwing BadXMLExceptions which stated that my time range requested data beyond the available range of data on the server. Since my ending time was always based on the timestamp of each source's most recent sensor data on the server, my start time was hard-coded to midnight of the starting day, and I was in the same time zone as the server, I knew I was not experiencing synchronization issues between local time and the server. Further testing on November 24 revealed that the problem disappeared at about 12:17 AM, which was suspiciously close to my sampling interval. When I reported my problem to the class mailing list, Professor Johnson confirmed that the bug was the result of my sampling interval exceeding the range of available data. The solution was to set the sampling interval to 0 minutes, which allowed it to make data requests even when the starting time and the ending time were exactly the same.


Conclusion

Learning about the proper use of the tools of automated quality assurance and continuous integration has helped me reduce the number of syntactical errors I make in the first place, while making sure that most of my worst development mistakes are eliminated by automated quality assurance before entering the repository. Having team members with whom communication was easy made it faster and less confrontational to diagnose issues and suggest solutions, and enabled our group to proceed at a steady rate. Incorporating unit testing into our builds early on helped us get test coverage up to a reasonably high level by the end of the project, even if it was not possible to test some parts of the code. Ultimately, the combination of good time management and industry-standard quality-assurance tools helped us work around server crashes, data-request bugs, and test coverage problems and see the project to its completion. A good team can compensate for occasionally dysfunctional technology, but even good technology usually cannot save a project from a truly dysfunctional team.

Tuesday, November 8, 2011

The WattDepot Katas Project: Thoughts on an Open Source API

This week, I worked on a series of code katas for the WattDepot API, an open-source interface used to retrieve data on daily power and energy generation and use from remote sensors. The WattDepot API is intended to give users the tools to process technical, obtuse, automatically generated energy data and put it into a form that means something to the people who will use it.

I was able to complete the first Kata, SourceListing, in one or two hours; it asked for an alphabetical listing of all the Sources on the WattDepot server and their descriptions. Most of the code for this can be attributed to the SimpleApplication.java example program included in the distribution, as a Source object includes a name and a description.

The second Kata, SourceLatency, which required a program that calculated latency for each Source and printed the sources in ascending order based on latency, took two days. I had somehow managed to finish ICS 211 without learning how to use Hashtable, or else I had just forgotten that it existed. As a result, I spend the next two days trying to figure out how to sort one array according to the ordering of another array, without any success whatsoever. The solution I eventually found, and the way in which I found it, demonstrate why it is better to work on complex projects in groups. The reason I finally gave up on my insane attempt to sort two arrays simultaneously was because one of my classmates suggested during peer-programming exercises that I use a Hashtable. It turned out that a Hashtable with array-list values to store Sources with the same latency values worked just fine using the latencies as keys. As in some of my other projects, I have found that solving programming problems is a strange mix of happiness that something works, and frustration that it took me several sleep-deprived hours to figure out a simple mistake.

The third Kata, SourceHierarchy, required a program which printed Sources and their SubSources with increasing indentation for each level of sub-source. This kata also took two days, mostly because I sought clarification on how to parse the SubSources. The major issue I had at first, which turned out to be a non-issue when I figured out the actual format of the assignment, was that the SubSources returned by Source.getSubSources are not a recursive data structure (that is, a SubSource does not contain the Source data for its SubSources, only their URIs), and it was not possible to construct the hierarchy recursively. The trick, as Professor Johnson said, was to make a single pass through the list to figure out which sources were SubSources, and then use that to create the hierarchy. The solution I eventually implemented parsed the URIs to retrieve the SubSource names and did not print any Source which was listed as another source's SubSource.

The fourth Kata, EnergyYesterday, required a program which totaled up the energy used by each source on the previous day and printed each Source and its energy use in ascending order by energy use. This was similar in design to SourceLatency; in both cases, I used Hashtables to store Lists of Sources with identical energy or latency values, using the energy or latency values as the keys to retrieve them, then iterating through the lists and printing each Source with its associated key. Once I had fixed my SourceLatency issues, it did not take much editing to create EnergyYesterday; the main additional problem was the simple task of creating a timestamp which was rolled back by one day.

The fifth Kata, HighestRecordedPowerYesterday, required a program to print the highest recorded power for each source for the previous day, sorted in ascending order. It took up almost all of Monday, and several hours of Tuesday morning; this blog entry is being written at 6:00 AM on Tuesday morning, November 8, and the last kata still isn't finished. I had to write several utility methods for this class, mostly to generate the timestamps for power data requests and to parse the timestamps back into AM/PM format. The kata appears to be working correctly, except for one thing: I was never able to figure out whether it was a bug, or just a reflection of college study habits, but the peak power measured for every single sensor was at 12:00 AM midnight:

// My output for Kata 5 is reproduced below:
Connected successfully to: http://server.wattdepot.org:8190/wattdepot/
Server: http://server.wattdepot.org:8190/wattdepot/

Source              Highest recorded power in watts ( 07-Nov-2011 )
Lokelani-12-telco   1,203  12:00am
Lehua-10-telco      1,263  12:00am
Mokihana-08-telco   1,390  12:00am
Mokihana-04-telco   1,612  12:00am
Ilima-12-telco      1,818  12:00am
Lehua-04-telco      1,868  12:00am
Ilima-10-telco      2,227  12:00am
Lokelani-04-telco   2,432  12:00am
Mokihana-10-telco   2,662  12:00am
Lokelani-06-telco   2,890  12:00am
Ilima-08-telco      3,276  12:00am
Ilima-08-lounge     4,036  12:00am
Lokelani-10-lounge  4,204  12:00am
Ilima-10-lounge     4,285  12:00am
Lokelani-06-lounge  4,667  12:00am
Ilima-06-lounge     5,208  12:00am
Ilima-A             5,237  12:00am
Lokelani-04-lounge  5,458  12:00am
Mokihana-12-lounge  5,665  12:00am
Lokelani-D          5,845  12:00am
Mokihana-A          5,862  12:00am
Lehua-08-lounge     6,233  12:00am
Ilima-D             6,512  12:00am
Mokihana-10-lounge  6,612  12:00am
Mokihana-C          6,877  12:00am
Lehua-B             7,258  12:00am
Mokihana-E          7,484  12:00am
Lehua-C             7,760  12:00am
Ilima-B             8,022  12:00am
Lokelani-08-lounge  9,564  12:00am
Ilima               33,961  12:00am
Mokihana            35,322  12:00am

The sixth kata, MondayAverageEnergy, required me to write out the average energy consumed by a Source over the previous two Mondays. It is probable that I did not finish this kata in the hours remaining before the deadline. The main problem is in figuring out if today is Monday, then subtracting the number of days since the last Monday (to generate the first data set), then rolling back the date seven days to the previous Monday (to generate the second data set), and if I had had more time I would most likely have been able to reuse some of the code from EnergyYesterday to do this. I would have to put most of the blame for my poor scheduling on the two days of time I used to try to sort two arrays simultaneously for the second and fourth katas.

Though I may have frequently been frustrated with my own lack of knowledge or the difficulties of certain parts of the assignment, the WattDepot katas generally served as another example of how code katas can force developers to learn about a language or API's capabilities very quickly. Parsing WattDepot energy data into human-readable form is mostly a matter of timestamp generation, sorting among multiple sets of data elements, and parsing strings, and I was forced to learn new capabilities of the Java language and the WattDepot API for all of these tasks. The difficulties of making energy conservation relevant to consumers and enterprise users begin with putting information about their energy use into a form they can understand, and the WattDepot API provides a flexible platform for this purpose.

Tuesday, November 1, 2011

Hawaii's Alternative Energy Future

The marketing image of Hawaii as a carefree, distant paradise masks the higher cost of living and vulnerability to supply disruptions that such distance brings. My home state in the middle of the ocean has no locally available fossil fuels and little alternative energy generation. As a result, it generates 89.8 percent of its power by importing oil - 75% of it from foreign countries - at a cost of $7 billion per year. As a result, Hawaii pays twice the U.S. average for electricity. The costs don't stop there: ever-rising gas prices are passed on to consumers directly at the pump and indirectly at the store, a result of having to import most food and other goods by plane or container ship.

Recognizing the difficulties ahead, Hawaii's state government passed the Hawaii Clean Energy Initiative, a set of clean energy goals for providing 70% of Hawaii's energy from renewable sources by 2030: 30% by increasing energy efficiency, and 40% from increased use of renewable energy. The plan asserts that Hawaii's temperate climate, wind conditions, large coastline, and geological activity allow its islands to take greater advantage of geothermal, wind, solar, and other alternative energy sources than other parts of the United States. It also acknowledges that the plan depends on public support for its policy goals and public participation in efforts to reduce personal energy use.

One of the challenges of the HCEI is the integration of new types of power generation, such as wind and solar, which do not generate power as consistently as fossil fuel power plants. Since wind speed and sunlight should vary based on weather patterns, it is necessary to collect enough data to determine what those patterns are, limit electricity usage when less power is available, and alert customers when excess power is available - in effect, to create a "smart grid" of sensors to collect information about power generation and use. As explored in a Maui residential testbed by the Hawaii Natural Energy Institute, the smart grid would rely on a network of gauges which communicate wirelessly with a central "smart meter," which sends information to the utility. Future software could allow the utility to control certain devices through this network and remotely deactivate them to avoid blackouts. This type of energy control, called "demand response technology", is intended to reduce energy use by high-energy-use devices such as air conditioners during peak hours. Demand response technology, according to the HNEI's Jay Griffin, should be transparent - customers should know why their devices are being shut off, and when supply is in excess of demand, customers should be told and billed at a reduced rate for using electricity during hours which are less stressful for the grid.

The other challenge of the HCEI is convincing people that energy efficiency is something they control and that there is a financial benefit to their doing so. Regardless of utility customers' personal views on environmental issues, anyone is likely to try to reduce energy use if they can be convinced that it will save them money and that the causes of high energy use can be isolated. The Sea Grant institute at the University of Hawaii is currently conducting energy audits (logging of individual devices and circuits' energy use) of military housing. The ultimate goal of an energy audit is to help the audited person or organization to recognize points of high energy use, change their behavior, and ultimately save money. Sea Grant's current auditing procedure requires that its researchers manually graph and check the data gathered by the power-use sensors. Sea Grant uses the comprehensive data, which includes times, device model numbers, and temperature readings, to draw conclusions about residential energy use and provide efficiency advice to the audited family. Sea Grant's eventual goal is to automate data collection and organization in order to get the data into a human readable state in less time.

Hawaii has a long way to go to achieve its 2030 goals for renewable energy production. However, the research efforts now under way promise to make it possible to easily add renewable energy generators onto the power grid and to account for their unpredictability. They also promise to empower the public with new tools and services to track and take control of their energy use, a key part of achieving the increased efficiency goals. In all of these developments, software developers have a key role to play in solving the problems of implementing and analyzing the "smart grid" that is ultimately the backbone of any future system of energy generation.

Thursday, October 20, 2011

A Week of Configuration Management

Configuration Management

Configuration management software attempts to identify and track the elements of a system's configuration so that errors can be found and fixed. Version control is a special sub-case of this which focuses on maintaining multiple branches (development versions used for experimentation or developing new features) and the central trunk (the main version which all branches must eventually be merged with). Defect tracking systems track error reports from identification to the point at which the errors are fixed, and are part of configuration management because fixing the errors usually requires configuration changes.

To use a configuration management system, developers must first check out (download) the project from the SVN repository (the directory on a remote server where the project files are stored). Once this is done, they can edit their local copy of the project files, then commit (upload) the changed files or directories. If a developer adds new files to the project, they must be manually added to the files under version control before being committed to the repository. If others make changes, a developer must update (download new versions) the local copies of the changed files. William Wake explains it in more detail in this graphic.

As we were instructed in class, best practices for configuration management systems include verifying that the system runs and builds correctly before committing any changes. Otherwise, you run the risk of having to hurriedly fix previously undetected mistakes one after the other, as reflected in this log of commit messages:
Rev 53571: "This is a basic implementation that works."
Rev 53572: "By works, I meant 'doesnt work'. Works now.."
Rev 53573: "Last time I said it works? I was kidding. Try this."
Rev 53574: "Just stop reading these for a while, ok.. "
Rev 53575: "Give me a break, it's 2am. But it works now."
Rev 53576: "Make that it works in 90% of the cases. 3:30."
Rev 53577: "Ok, 5am, it works. For real.
Back when I said basic implementation? Scratch that."
(Source: This Stack Overflow thread)

Subversion

Subversion, the successor to the earlier CVS, is an open-source configuration management system that runs on the Berkeley Database, manages tags and branches similarly to folders, and handles all file types in the same way. It is currently an industry standard due to its centralized nature, which helps with access control issues and allows a single project leader to coordinate development; however, many in the open-source community prefer Git's flexibility and multiple workflows over Subversion' centralization.

Many GUI and command-line clients exist to access SVN repositories; I used TortoiseSVN, a Windows client. The GUI was fairly easy to use for my first assignment of revising and committing a preexisting project. For the second project, in which I had to upload my project to Google Code (about which more is written in the next section), I had a little more trouble with the command line interface, and what was ultimately more helpful than the project documentation in figuring out how to add files and do other operations was this general SVN manual, which did a good job of describing SVN's general logic.

Google Project Hosting

To complete my training in configuration management, after uploading my code to Google's remotely hosted repository via TortoiseSVN, I set about configuring the front page and some instructions for users and developers working with my robocode-tjo-meteor robot. The page I set up can be found here, and the source files can be checked out using the instructions here.

Google Project Hosting is fairly easy to add new pages to and configure settings for, using a system of simple menus similar to other Google interfaces I have used in Blogger or Google Sites.

Google Project Hosting allows users to choose Subversion, Git, or Mercurial as their configuration management system, and handles defect tracking by listing open issues on the "Issues" page. The current SVN trunk is viewable in the Source page, and the revision history is also available.

The only real issue I have with Google Project Hosting mostly deals with with element positioning:
  • The tables are made with sets of ||s, not tags:Google only detects a table cell if both the opening || and closing || are on the same line (i.e., you never press enter at any point between them).
    For example, typing:

    ||Contents||
    ||I want this entire sentence to appear
    in one table cell||

    results in:
    If you break this rule, you may end up with several scattered partial table cells. The workaround, typing the text you want to enclose as one long sentence, means that enclosing long sentences or paragraphs in table cells looks ugly in code and is almost unreadable if you use any other HTML tags or wiki markup within the table. I spent hours figuring out how to force Google to recognize the table cells before I realized that the bug was related to newline characters in this way. Google does allow the use of <table> tags, however, so there is a workaround for this.
  • The anchor links:There are several things to be aware of when writing these. The support page helpfully explains that spaces need to be replaced with underscores in anchor links. Essentially, what I discovered was this:

    // In this case I am trying to make an anchor link to header == Foo Bar ==
    [# Foo Bar ] // This creates a link to the main URL of the page itself, and reloads it instead of going to the anchor. In some cases it does not work at all. The preceding and trailing spaces are not needed even though they are in the header.
    [#Foo Bar] // This will not work either. As mentioned, the space needs to be replaced with an underscore.
    [#Foo_Bar] // This will work.

    If you are mostly using this to make a table of contents as I did, there is a faster piece of built-in wiki code that does the job:
    //Set the depth to specify how many layers deep you want the contents to go.
  • You cannot import images directly:Blogger allows you to import pictures from within the editor and host them on Picasa, in addition to embedding image links to pictures you have hosted elsewhere. This is not possible in Google Code. Though I there are definitely reasons to prefer this approach (Picasa's limitations, privacy concerns, or Blogger's arbitrary display sizes of "large", "medium", and "small"), a similar feature might be nice as an additional option.
Despite these minor issues (some of which were aggravated by my misreading of Google's support page), Google Code seemed to provide a decent level of services for being a free service and was an easy way for me to learn the basics of version control in a controlled setting, without the risk of damaging a real enterprise project - and demonstrated the ease with which I could use Subversion systems to store personal projects as well, though perhaps not quite at the level of Joey Hess.

Some Lessons In Software Engineering

It's been an interesting semester of ICS 314 so far, and since it's about the middle of the semester (and time for our first midterm) I decided to summarize some of what I've learned over the past few weeks.

    Validation and Verification

  1. In software review, validation is the process of checking that code is being made for the correct purpose, and verification is the process of checking that code is made correctly.
    Here I define these four validation and verification methods, and specify whether they are static or dynamic: load testing, defect testing, software inspections, and static analysis of source code.
    • Load testing (Dynamic): Dynamic tests, unlike static tests, test the program by running it. Checks for errors in scalability, performance, and reliability.
    • Defect testing (Dynamic): This is another form of static testing that checks for functionality errors.
    • Software inspections (Static): Manual inspections of code by other developers. This refers to the formal process of "inspection" developed by Michael Fagan (described in more detail by "The Software Experts" here), as well as more informal code reviews and peer reviews. In all of these processes, other trained developers examine a project's code for implementation errors and requirement satisfaction.
    • Static analysis of source code (Static): Examines the source code by looking for control and data flow issues, without running it.

    Testing and Branch Coverage

  2. One of the factors used by tools like Jacoco to determine the coverage of a program's test cases is control flow coverage - whether and how many of your control statements (if, else, etc.) are ever executed. I explored many issues of control flow coverage as I designed my test cases for the Robocode project.
    So, what are the three types of control flow coverage?
    • Branch coverage: Testing evaluates each conditional as true and false.
      • I made an attempt to provide branch coverage in my method unit tests. In what was probably at best a confusing design decision, I created and tested a method called whichWallOrCorner that returned one of nine integer values based on whether the robot was near a wall, near a corner, or near neither. What made this complicated to test was that, in accordance with branch coverage, the JUnit test needed to check all possible return values, as did every other method which called this method. In effect, each assertion in the JUnit test case evaluated one of the conditionals as true and all of the other conditionals as false. The distanceToWallOrCorner method, which depended on whichWallOrCorner, was just as long as the method it relied on; in effect, every piece of code that used the whichWallOrCorner method had to handle all nine possible return values in some way.
    • Loop coverage: Testing executes every loop 0 times, once, and more than once.
      • Apart from the main while(true) loop, the robot did not have many other loops in its code. The only way to check the while(true) loop and its sub-loops was to run acceptance tests which pitted the robot against other robots. Even so, these could only verify victories or proper method execution, not loop execution.
    • Path coverage: Testing executes all possible paths through the program.
      • Once again, as most loops were nested within the the while(true) code, it was not possible to test each nested loop directly, or even indirectly via the acceptance tests. For the method unit tests, it was much easier to force all possible paths to execute by passing appropriate variables to the method.
    My Robocode experience is described in more detail in this previous post.
    For at least the next few months, a more current version of my robot can be found here.

    Unit Testing for Input which is a Value

  3. How should unit tests be written for methods which take values as input?

    Unit tests for methods that take values as input should, at minimum, test the method's behavior for the maximum legal value, the minimum legal value, an empty value, a value somewhere within the range of legal values, and an illegal value.

    Alternately, the minimum, according to equivalence partitioning, is as follows: once the values have been divided into partitions (ranges of input), only one test is needed per partition (e.g., one partition holding illegal values that are too low, one partition holding all the legal values, and a third partition after it which holds illegal values that are too high). In equivalence testing, it is thought that all the test cases for values within a given partition are the same - so only one value in each partition needs to be tested.

  4. Pitfalls of Automated Quality Assurance

    Just as a car company would use a wind tunnel to test aerodynamics but would use a focus group to test visual appeal, in software engineering there are some things manual testing does much better than automated testing.

  5. Why is it a bad idea to use automated quality assurance to verify product requirements?

    Automated quality assurance is not smart enough to know whether something implements design requirements. It can only check if code is syntactically correct (e.g., Checkstyle, PMD) or creates usable bytecode at build-time (e.g., FindBugs).

    Only manual quality assurance can find problems related to the project requirements - a machine can check whether code is error-free, but only humans can reliably judge whether or not code is useful. My Robocode robot is a good example of this - despite its being very ineffective, even against most of the sample robots, the current version passes all of the FindBugs, Checkstyle, and PMD tests with no errors.
  6. Why does automated quality assurance sometimes generate false positives?

    Automated quality assurance often flags code as a "problem" because it thinks it is a stylistic error, even if you intended it to work this way.

    I encountered this issue often when writing my JUnit tests. PMD would repeatedly flag my tests with UseAssertSameInsteadOfAssertTrue errors. In an ill-advised attempt to make this "go away", I replaced all the assertTrue methods with AssertSame. However, because this checked object equality, all of my tests that compared the values of numbers failed. As I found out from Stack Overflow, assertSame fails for any integers larger than 128, and based on personal experience, it does not work with doubles at all. I then converted all the assertions to assertEquals and added statements to print the purpose of each test. This caused all the tests to throw JUnitAssertionsShouldIncludeMessage errors in PMD. The errors finally went away when I converted the assert statements to the three-argument form of assertEquals, which includes a message. The code worked as expected before, but PMD refused to validate it until the "correct" test was used. Of course, one of the benefits of automated quality assurance is that it can detect problems the developer is too inexperienced to notice, so it is more probable that PMD was correct and my code was written inefficiently.

Tuesday, October 11, 2011

Going In Circles And Then Some: Preparing for a Robocode Tournament

Project Overview

The robot I was attempting to design for the Robocode tournament would have avoided hitting walls altogether, and would have been able to maintain a basic lock on its target. Neither of these goals was accomplished to any useful extent.

Design

  • Movement

  • Ideally, the robot was to have moved 100 pixels each turn, turning itself perpendicular to the headings of any enemies encountered and turning back before hitting walls where possible. I attempted to do this via the avoidBounds method, which was intended to turn the robot away from walls and thus avoid collisions with the walls entirely. As far as I can tell, this instead caused freezing problems whenever the robot managed to be positioned perpendicular to a corner, and in some other cases I was unable to define.
  • Targeting

  • Ideally, the robot was to have rotated its radar 180.0 degrees per turn, turned its body to be perpendicular to an enemy robot once it was scanned, logged its opponent's name, then begun scanning in smaller 22.5-degree intervals as long as it continued to detect that same opponent. Once that opponent was destroyed or had not been seen for two scans, it would resume scanning in increments of 180.0 degrees. In reality, the robot lost targeting locks almost constantly, as its other behaviors caused it to turn too frequently to maintain a consistent heading or back-and-forth pattern of strafing.
  • Firing

  • The robot implements a basic bullet-power calculation algorithm based solely on distance, starting at the maximum of 3.0 for anything less than 100 pixels away, dropping from 2.9 to 2.0 for anything 100-300 pixels away, then to 1.9 to 1.0 for anything 300-500 pixels away, then to 0.9 to 0.5 for anything more than 500 pixels away. It fires whenever it detects the robot it is currently targeting, and targets the same robot for the duration of at least three 22.5-degree scans before giving up and looking for a new target.

Results

(Each robot was fought for 10 rounds.)


Meteor's lack of predictive targeting means that Walls has moved out of range by the time Meteor aims its cannon at where Walls used to be; Meteor has no way of accounting for drastic shifts in direction or velocity on the part of an enemy robot.
Robot NameWins AgainstLosses Against
SittingDuck10/100/10
Virtually all robots can beat SittingDuck.
Tracker1/109/10
Meteor's primary weakness against Tracker is that it takes too long to scan for changes in motion (Tracker hardly scans at all once it has a lock), and thus fires much less often than Tracker. In addition, when this robot loses track of a target, it completes a 180.0-degree radar rotation, leaving it vulnerable to attack.
Corners0/100/10
Meteor's avoidBounds method backfired here, frequently causing it to spin in circles without firing or moving while Corners swept its radar across the battlefield in increments.
Fire0/100/10
Once again, the problem was that Meteor took too long to aim and fire,getting only a fifth the bullet damage of Fire. Meteor also froze repeatedly in this match.
Crazy3/107/10
Meteor did not perform predictive movement analysis, and had no way of estimating Crazy's movement pattern, whatever it was. I am not sure why Meteor won against Crazy at all.
SpinBot0/1010/10
Meteor actually dodged several of SpinBot's projectiles, but it failed to score any hits of its own and usually got stuck against a wall after about thirty seconds, thanks to its freezing bug.
RamFire2/108/10
Once again, Meteor's need to make large turns and radar sweeps each time it lost a targeting lock did it in; this made it easy for RamFire to push Meteor into walls or otherwise obstruct its movement.
Walls0/1010/10

Testing

My tests consist of two acceptance tests, TestMeteorVersusCorners and TestMeteorVersusFire, both of which the robot fails (see the Results section); one behavioral test, TestMeteorFiring, that checks whether Meteor.calculateShotPower is functioning correctly; and three unit tests (UnitTestWhichWallOrCorner, UnitTestDistanceToWallOrCorner, UnitTestNewXAndY) for the methods Meteor.whichWallOrCorner (which returns an integer code based on whether the robot is near a wall or corner), Meteor.distanceToWallOrCorner (which calls whichWallOrCorner itself, and determines the distance to a wall or corner) and a combination unit test for the very similar Meteor.newX and Meteor.newY methods, which calculate the point that a particular motion will cause the robot to drive to. UnitTestNewXandNewY does not work, however, due to what are probably errors in how I assumed it was supposed to work.

Lessons

The Robocode design process has revealed a harsh truth about my coding style: specifically, that it tends to produce a lot of code, most of which performs as expected, but most of which goes towards performing some extremely complex task (avoiding walls) which in the long run is not very important. In addition, the sheer length of my code virtually guarantees problems like Meteor's mysterious freezing episodes, which could come from any number of points in the code which control movement and which do not seem to share any common factors. Thus, I have a robot with about 400 lines of code (excluding comments) that cannot beat any of the sample robots with any sort of reliability, is still incapable of detecting when it is near a wall and stopping, and shoots straight but on average takes longer than most of its opponents to do so. The major problem is the freezing bug, which I have repeatedly failed to fix over the course of the last two days. If I had tried to do this over again, I would have spent less time trying to reinvent the wheel and more time modifying example code, as my attempt to implement a wall-avoiding function took so much time there was little left to actually test anything else.

Wednesday, September 28, 2011

Apache Ant Code Katas: Learning A Build System In One Week

Introduction

Build systems are software packages which help software developers by automating project tasks that need to be done repeatedly, such as compiling source code, running compiled code, generating documentation, and packaging a system for distribution. All of these tasks do not take much time to do by hand for small projects, but for large academic or enterprise projects with hundreds or thousands of files, doing anything manually is virtually impossible. Apache Ant, a free build system commonly used in enterprise, carries out all of these tasks and more by using Java libraries to process instructions in user-created XML files. Users can extend Ant's functionality by writing their own libraries or by combining it with dependency-management tools like Ivy, which download whatever third-party libraries are needed to run a given piece of code, then save them locally. For this exercise, I completed several code katas designed to teach me some of the basic functionality of Ant 1.8.2. Each kata required me to create a specific filename.build.xml file, which would contain processing instructions for Ant.

Kata 1: "Hello World"

In the time-honored tradition of everything in computer science, I started here. The objective here was to teach use of the <echo> element to print informative text to the console. This did not take very long.

Kata 2: Properties Are Immutable

This kata was intended to teach procedures for defining properties, and to demonstrate that properties cannot be changed once defined within a build file. I defined a property, then attempted to define another property with the same name. As intended, this had no effect whatsoever, and the <target> which printed the property value printed the first value which was defined.

Kata 3: Dependencies

In Ant, the "depends" attribute of a <target> element specifies which targets must be completed before a target's instructions will be executed. This is used to make sure that any prerequisites for a target to run correctly (e.g., compiling code before trying to run it) can be carried out. This kata required us to print the name of the target as it was being executed, and I spent a few hours fruitlessly trying to print each target's "name" attribute before discovering that it might not be possible without Javascript workarounds. In the end, I defined properties with the same names and values as the targets they were defined within, and settled for <echo>ing those.

Kata 4: Calling the Java Compiler on HelloAnt

This exercise used a simple Java program which printed "Hello Ant" to the console. Here, within a "compile" target, I defined two properties: paths to a source directory (where the .java file was) and a build directory (where the .class files would be output to). I then used the <javac> element, which would tell Ant to run the javac compiler given the specified directories as parameters. This kata was still fairly easy. More importantly, running this script repeatedly was faster than having to specify the directories each time I ran javac from the command line.

Kata 5: Running HelloAnt

This exercise called for importing the .xml file from Kata 4 and using its "compile" target to compile the system before running it. As it turned out, defining the directory paths in Kata 4 meant I could easily reuse them here, as importing a file also imports all of its defined properties. Using the same principles outlined by the Dependencies kata, I made the "run" target dependent on "compile". The hardest part of this was guessing at how to write the <java> element's "classname" attribute for java files in a package, which I couldn't find an example of in the Apache documentation. At first, I tried to treat the class name as a series of nested folders (/edu/hawaii/ics314/filename.extension), which is how it appears in the file system. After several variations on this, I learned that ant requires that the classname be written using its Java format (edu.hawaii.ics314.filename, no file extension).

Kata 6: Making Documentation for HelloAnt

This kata demonstrated the use of the <javadoc> element, within a target that was dependent on the <compile> target, which was imported. This ensured that the Javadoc files would only be generated if the source code actually compiled. The <javadoc> element also allows you to control what information is included in the javadoc by setting attributes such as "author" or "overview" to "true". Generating documentation manually is always tedious, and the attribute options are less tedious to use than typing each option individually (every time you needed to revise the documentation) and running Javadoc from the command line.

Kata 7: Cleaning the Software Package

This exercise required me to modify the .xml file from Kata 4 to add a target which deleted the "build" directory generated by the "compile" target. This was intended to teach the use of the <delete> element, which instructs Ant to delete the directory specified in the "dir" attribute.

Kata 8: Package HelloAnt for Distribution

I had a recurring problem when I first tried to implement this kata: as required, it would delete the build directory, but it would mysteriously include the build directory in the .zip file it created for distribution. Eventually, I realized that because I had to create the build directory again to store the .zip file in build/dist, it was always saving the empty build directory into the .zip file. I fixed this problem by modifying some code from a class example to first save the .zip file to a temporary directory, copy it over to the desired directory, then delete the temporary directory. The main lesson here was to use the "excludes" attribute to exclude the build directory from the distribution. Regardless, manually excluding unnecessary files before creating the distribution directory would have been an annoyance for a small project like HelloAnt, but impossible for any enterprise project of moderate size.

The second requirement was to make sure that unzipping the directory from the command line would preserve the original directory structure. This was simpler and involved creating a directory with the same name as the current directory within the distribution directory. Removing the build files from the distribution not only reduces its file size, but makes sense in light of Prime Directive 2: forcing other developers or users to build the system using the build files you've provided is a good way to verify that others can install and use your system.

Ant / Ivy and the Three Prime Directives

As free software packages which work together, Ant and Ivy are fairly easy to install as long as one follows the site instructions (Prime Directive 2), and automating otherwise tedious tasks easily fulfills a useful task (Prime Directive 1). While I can't say that I have personally tried to extend the system, I was able to complete most of the katas without going outside Apache's documentation, which does go some of the way towards fulfilling the third prime directive as well. It is also worth noting that despite my initial problems on some of the later katas, all of the tasks I was trying to instruct Ant to perform would have taken just as long, if not longer, if they had to be performed by hand and/or directly from the command line over and over again. Build systems may take time to learn, but a little time invested up front here will always save more time in the end.

Final Thoughts

Though I wouldn't claim to understand everything about Ant at this point, the code katas did force me to learn enough to understand its basic capabilities and encourage me to look further into its other features. The extensive use of custom XML markup for interpretation by Ant and Ivy's Java libraries demonstrates to me that XML, as a user-defined markup language, is as useful as a developer wants it to be. Learning new software tools or APIs quickly is always a little like jumping into a pool; code katas won't get you into the deep end right away, but they can teach you enough to slowly move into deeper water and not drown.