6.170 Laboratory in Software Engineering
Spring 2004
Problem Set 2: Representation Invariants, Specifications, and Immutable Data Types
Due: Thursday, February 19, at 9pm

Handout P2

Quick links:

Contents:

We recommend that you read the entire problem set before you begin work. Also, note that Problem 5 DOES NOT DEPEND on earlier parts. If you get stuck on earlier parts, you should attempt Problem 5.


Introduction

In this problem set, you will implement a pair of classes that will complete the implementation of a graphing polynomial calculator, and answer questions about both the code you are given and the code you have written.

To complete this problem set, you will need to know:

  1. Basic algebra (rational and polynomial arithmetic)
  2. How to read procedural specifications (requires, modifies, effects)
  3. How to read and write basic Java code
  4. How to create a test driver that thoroughly tests an implementation of a given specification using the JUnit testing framework.

Getting started

Make sure you have followed the instructions on the tools handout relevant to directory setup before you begin development. Furthermore, be sure you have read the problem set procedure handout before you begin.

You can get the source files for problem set 2 by using CVS. Follow the problem set checkout instructions and checkout the ps2 module from your repository.

Your code MUST work on Athena, so if you choose to work on a non-Athena machine, please take a few minutes before submitting your solution to ensure that it runs correctly on Athena. Once everything is in order, read the Problem Set Submission instructions for how to run a final check of your code and turn it in.




Problems

Problem 1: RatNum (24 points)

Read the specifications for RatNum, a class representing rational numbers. Then read over the staff-provided implementation, RatNum.java.
You may find it helpful to peruse the code in RatNumTest.java to see example usages of the RatNum class (albeit in the context of a test driver, rather than application code).

Answer the following questions, writing your answers in the file problem1.txt

  1. What is the point of the one-line comments at the start of the add, sub, mul, and div methods?
  2. RatNum.div(RatNum) checks whether its argument is NaN (not-a-number). RatNum.add(RatNum) and RatNum.mul(RatNum) do not do that. Explain.
  3. Why is RatNum.parse(String) a static method?  What alternative to static methods would allow one to accomplish the same goal of generating a RatNum from an input String?
  4. The checkRep method is called at the beginning and end of most methods. Why is there no checkRep call at the beginning of the constructor?
  5. Imagine that the representation invariant were weakened so that we did not require that the numer and denom fields be stored in reduced form.  This means that the method implementations could no longer assume this invariant held on entry to the method, but they also no longer were required to enforce the invariant on exit.  The new rep invariant would then be:
  6.     // Rep Invariant for every RatNum r: ( r.denom >= 0 )

    Which method or constructor implementations would have to change?  For each changed piece of code, describe the changes informally, and indicate how much more or less complex the result would be (both in terms of code clarity, and also in terms of execution efficiency). Note that the new implementations must still adhere to the given spec; in particular, RatNum.unparse() needs to output fractions in reduced form.

  7. add, sub, mul, and div all end with a statement of the form return new RatNum ( numerExpr , denomExpr);. Imagine an implementation of the same function except the last statement is

    this.numer = numerExpr;
    this.denom = denomExpr;
    return this;

    Give two reasons why the above changes would not meet the specifications of the function.

Problem 2: RatPoly (30 points)

Read over the specifications for the RatTerm, RatTermVec, and RatPoly classes.  Make sure that you understand the overview for RatPoly and the specifications for the given methods.

Read through the provided skeletal implementation of RatPoly.java. The most significant parts of the provided file are the comments describing how you are to use the provided fields to implement this class.  The Representation Invariant is an especially important comment to understand, because what invariants you define can have a drastic effect on which implementations will be legal for the methods of RatPoly.

You'll notice that all of the methods that you need to implement currently have this method body: throw new RuntimeException("Method is not yet implemented!"); For more about what this means, see Problem Set 1.

Fill in an implementation for the methods in the specification of RatPoly.  You may define new private helper methods as you like; we have suggested a few ourselves, complete with specifications, but you are not obligated to use them. (The staff believes that doing so will drastically simplify your implementation, however.)

We have provided a checkRep method in RatPoly that tests whether or not a RatPoly instance violates the representation invariant. We highly recommend you use checkRep in the code you write when implementing the unfinished methods.

We have provided a fairly rigorous test suite in RatPolyTest.java. You can run the given test suite with JUnit as you program and evaluate your progress and the correctness of your code.

The test suite depends heavily on the implementation of the RatPoly.unparse() method; if the test suite claims that all or many of the tests are failing, the source of the problem could be a bug in your implementation of unparse().

Problem 3: RatPolyStack (15 points)

Follow the same procedure given in Problem 2, but this time fill in the blanks for RatPolyStack.java. The same rules apply here (you may add private helper methods as you like). Since this problem depends on problem 2, you should not begin it until you have completed problem 2 (and the ps2.RatPolyTest test suite runs without any errors).

We have provided a test suite in RatPolyStackTest.java. You can run the given test suite with JUnit as you program and evaluate your progress and the correctness of your code. Again, the test suite relies heavily on RatPoly.unparse().

Problem 4: PolyCalc (1 point)

Now that you have implemented the two remaining classes in the system, you can run the PolyCalc application. This allows you to input polynomials and perform arithmetic operations on them, through a point-and-click user interface. The calculator graphs the resulting polynomials as well.

To run PolyCalc, type the following command:

athena% cd ~/6.170/workspace/ps2/src
athena% java -cp .:$CLASSPATH ps2.PolyCalcFrame

A window will pop up with a stack on the left, a graph display on the right, a text area to input polynomials into, and a set of buttons along the bottom. Click the buttons to input polynomials and to perform manipulations of the polynomials on the stack. The graph display will update on the fly, graphing the top four elements of the stack.

Submit your four favorite polynomial equations, in the RatPoly.unparse format, in the file problem4.txt inside the doc/ directory. Make sure to add the file to CVS and commit it. Follow the Problem Set Submission instructions.

Problem 5: Writing a Black Box Test Driver (30 points)

The final problem for this assignment is unrelated to the "RatPoly" calculator. It involves Black Box testing BasicList. The implementation that is provided to you contains several implementations errors, where the programmer did not adhere to the specification. Your task is to create a test driver that isolates the implementation errors in this class. You are only given the specification for this class; you are not given the source of the implentation. You are only given the binary class file so you can use the BasicList class in your JUnit test driver. Using the Black Box testing techniques discussed in lecture, you should create test cases that test that the given implementation satisfies the specification.

The specification for the BasicList class can be found here. Note that this specification is very similar to the ArrayList class that is part of the Java API.

The purpose of this assignment is to create a test driver that tests a given implementation against a given specification. You will need to carefully read the specifications of all methods and constructors for BasicList and construct test cases. Also, you might want to look over the provided JUnit test files from both this problem set and the previous assignment (e.g. RatPolyTest, CardTest).

You will fill in BasicListTest.java, which is a skeleton for the test driver. Remember to thoroughly test that ALL public methods and public constructors satisfy the specification. Finally, in problem5.txt give a description of all errors you uncovered in your test driver. Place the file in the doc/ directory and add and commit it to CVS.

Provided classes

The following classes are all provided for you, in compiled form, by the staff:
(If you have run the 6.170 setup scripts, the compiled code should already be in your classpath; you can just use the provided classes according to the HTML Documentation specification.)

With source code also provided:
ps2.RatNum
ps2.RatNumTest
ps2.RatPolyTest
ps2.RatPolyStackTest
ps2.BasicListTest
ps2.Cons (as part of the RatPolyStack.java starter code)

With source code not provided:
ps2.PublicTest
ps2.PolyGraph
ps2.PolyCalcFrame
ps2.RatTerm
ps2.RatTermVec
ps2.BasicList

JUnit Revisited

Think before you code! The polynomial arithmetic functions are not difficult, but if you begin implementation without a specific plan, it is easy to get yourself into a terrible mess.

Running the Tests using Eclipse

JUnit is integrated with Eclipse, so you can run the test suite from within the IDE.

Running the Tests from the Command Line

If you are not using Eclipse, you can invoke JUnit from the command line. To run the RatPolyTest test suite, type the following command:

athena% java junit.swingui.TestRunner ps2.RatPolyTest

For a non-graphical alternative method of running JUnit, use junit.textui.TestRunner instead of junit.swingui.TestRunner.

Remember that in order for this to work, ps2-lib.jar, junit.jar and the directory containing your class files must all be in the Java VM's classpath (i.e. part of the CLASSPATH environment variable or you may need to explicity use the -classpath parameter for the Java VM).



Errata


Q & A

This section will list clarifications and answers to common questions about problem sets. We'll try to keep it as up-to-date as possible, so this should be the first place to look (after carefully rereading the problem set handout and the specifications) when you have a problem.

Q:Do we need to test that remove() is implemented in the iterator() returned by BasicList?
A: No, you only need to test that the elements returned by the iterator() are in the proper order.
Q:Should we only include tests in our BasicListTest that generate failures?
A: We have provided a reference broken implementation, but your test code should be designed to reveal errors in ANY implementation. We have a (hidden) staff jar that is broken in different places from the jar we handed out. Your BasicListTest should correctly identify the errors in the staff jar too! Therefore, do not comment out test cases just because they pass and fail to generate errors.
Q:The class overview and the no-arg constructor in the specification for BasicList discuss a "capacity" value. How are we supposed to test that this capacity is correctly initialized.
A: It is not possible to test the capacity, so don't worry about it. In fact, the mention of a "capacity" value is an implementation detail that doesn't really belong in the specification. It appears only to alert you to similar occurrences in the Java API where you will see implementation details inappropriately appearing in a public specification.
Q:We are told in Problem 5 to write a test driver that tests all public methods AND public constructors. How can we be sure whether a bug has arisen from the constructor or from the method?
A: Testing is not an exact science. We only want you to write up your hypothesis for where the errors that you have uncovered may occur.
Q: How do I represent the "0" polynomial?
A: Look at the no-arg public constructor.
Q: What is a representation invariant?
A: A representation invariant is a statement that must be true at the exit of every public method and constructor. You can check that you meet this requirement by using checkRep(). Given that you meet this requirement, you can then assume that the representation invariant holds at the entrance of every public method. This might make writing methods in RatPoly easier. For instance, you can assume without checking that there are no terms with zero coefficients.

Back to the Problem Sets.
For problems or questions regarding this page, contact: 6.170-webmaster@mit.edu.
$Id: ps2.html,v 1.19 2004/02/19 22:54:49 noto Exp $