CMSC 433, Fall 2006

Programming Language Technologies and Paradigms

Project 1

Due October 22 2006, 10pm (Late deadline, Oct 23rd, 8am)

Updates

In this project, you will write JUnit test cases for our code. In this case, our code will be an implementation of the Observer pattern.

Background: Observer Pattern

You should begin by reviewing course material on the Observer Pattern. The code you will test is a modification of Java 1.5's observer pattern classes from java.util . The specification for these classes is given in the detailed specification. Here are highlights of the differences:

  1. Observable is an interface, rather than a class.
  2. Observable and Observer have been parameterized, using generics, by the type of data that is passed to the Observer.update method. This obviates the need for dynamic downcasts within Observer.update .
  3. When registering an Observer with addObserver , you must pass a Filter object. Only if the valid method of this object returns true when given the updated state will the associated observer be notified. You can pass a TrueFilter if you don't want any object filtered out.
  4. We have removed the setChanged and hasChanged methods. To notify the observers, simply call one of the notifyObservers methods directly.

An Application of Observers: Newspaper Simulation

As an application of the observer pattern, we have written newspaper publishing and subscription simulation. There are five classes/interfaces: an implementation of interface
NewsSimulatorParser
provides a static method that reads in a sequence of text commands, parses them and invokes the appropriate methods of a NewsSimulator ,
NewsSimulator
provides methods that perform actions such as subscribing to a newspaper and publishing a newspaper,
Observable
the interface that describes things that can be observed;
Person
represents a newspaper subscriber and implements the Observer interface;
Newspaper
represents newspapers published by Observerable and received by the update method of Person .
The API documentation explains how these work. You only need to worry about errors in the implementation of NewsSimulator and Observable. The other classes (such as NewsSimulatorParser, Person and Newspaper) are used unchanged in all of the examples and are not believed to contain any errors.

The Project

Your job is to write (black box) test cases to ensure that implementations of Observable and NewsSimulator meet their specifications (we will provide implementations of Person and Newspaper ). In particular, you must supply us with a class AcceptenceTests that extends junit.framework.TestCase. Inside this class you will write JUnit test cases to test implementations of the specification. Here is a skeleton for the class you will write:
public class AcceptenceTests extends TestCase {
  public void testCase1() { ... } // feel free to use more informative names
  public void testCase2() { ... }
  ...
}

This class will contain one public, no-argument, void -returning method for each test that you write, and each method's name must begin with the word test (all lowercase). Recall that by following this convention, JUnit can automatically create a test suite from your AcceptenceTests class. Each test should be self-contained, with no dependency to other tests. Do not use static fields. You may want to use setUp and/or tearDown methods. You may also define other methods in AcceptenceTests and even helper classes (under the cmsc433.observer package) as necessary. Be careful not to create your own classes that mask the JUnit classes. For example, a class called Test (in a source file Test.java ) could cause problems.

Your tests should not directly construct any implementation of the Observable NewsSimulator classes. Instead, use TestFactory.getObservable() to retrieve an object implementing Observable that is to be tested; and use TestFactory.getNewsSimulator() to retrieve an object implementing NewsSimulator that is to be tested.

The initial project code contains one correct implementation of Observable and NewsSimulator under the cmsc433.observer.correct package, and three buggy implementations under the cmsc433.observer.buggy1 , cmsc433.observer.buggy2 , and cmsc433.observer.buggy3 packages. In these packages, the Observable implementation has one of the following flaws:

  1. The notifyObservers() call does not notify an observer that previously registered.
  2. The deleteObserver(o) call does not remove an observer o , which was previously registered.
  3. The notifyObservers(p) call ignores any registered filters (always updates).
The PublicTests check two things: (1) that all of your test cases (in AcceptenceTests) pass the correct implementation; (2) that at least one of your test cases fails on each of the sample bad implementations. The release tests will do the same thing, but on implementations with flaws you have not seen. Therefore, try not peeking at the bad implementations and see if you can write test cases that fail on the three flaws.

Be systematic in building your tests. You will probably want to create a set of tests for each public method of the classes to be tested. For each of these sets you will probably have one test for each possible outcome, possible erroneous input, etc.

Finally, you can assume that all testing and implementations are single-threaded. I.e., you don't have to worry about an Observable notifying an Observer in a separate thread.

Important note

Do not change any of the implementations. The only code you should write is test cases and additional buggy implementations that you might want to use to test your test cases.

Grading

We will grade your assignment by running your test suite on a series of 24 additional buggy implementations and 2 additional good implementations of Observable and NewsSimulator ; three buggy implementations are provided to you and tested by the public tests.

Since you only have to have at least one test case that finds a particular bug, when you are writing test cases you don't need to recheck for things that other cases handle. For example, once you've written test cases that check the addObserver method of Observable, your other tests can assume that addObserver works correctly: If it does for a particular buggy version, then your assumption is correct, and if not, the other test cases will find the problem.

For each buggy implementation, you will receive credit for finding that bug if (and only if) you have at least one test in your test suite that both passes our correct implementation(s) and fails the buggy implementation. In other words, writing a test case that simply fails all implementations will not get you any points. Put another way: In order to count for anything, your test cases must pass the correct implementation. Your test cases must also pass any other implementation that satisfies the specification. Thus we may also discount test cases that fail a different correct implementation than the one provided.

You should write test cases keeping in mind that the buggy implementations are buggy in ways that could happen in real life. We have not put in malicious things, like making a method fail on exactly the 4,237th call to addObserver , for example.

Extra Credit

Although we believe we've given you a completely correct implementation in cmsc433.observer.correct , there's a chance that our implementation still contains bugs. As an incentive for starting the project early, the first person to report a particular implementation bug to us will receive 5 extra points, for a maximum of 15 bonus points per person. You should report these extra-credit bugs by posting them on the Class forum.

Java 5.0 and JUnit 4

This project employs both annotations and generics, which are features only of Java 5.0. We also use features of JUnit 4.1. Thus you must develop this project with Java 5.0 and JUnit 4.1.

Web Accessibility