CS 242 Fall 2010 : Assignment 1.1

This page last changed on Sep 01, 2010 by cemeyer2.

Commenting and Testing a Bit Manipulation Library

Suggested Reading
Chapter 22: Developer Testing from Code Complete 2. It has some discussion and suggestions on what to test for.

For this first assignment, you will take the given code and:

  • Comment all public functions with javadoc style headers
  • Implement unit tests for these functions
Due Date
This assignment must be submitted to Subversion by 8am on Thursday, September 2nd in a folder called Assignment1.1

Objectives

Overview

The given code is a rough outline for a bit manipulation library. The library must be able to read in a stream of bits, one bit at a time, from some data source. Similarly, it must be able to write a stream of bits, one bit at a time. We give you a rough outline for the minimum unit tests that you must implement. Implementing only these tests will receive a 1/2 on the rubric. You must expand on the given tests to receive all 2 points. Additionally, it should be able to read and write a specific number of multiple bits at a time.

The specific functions are:

Reading
  • readBit
  • readByte
  • readShort
  • readInt
  • readLong
  • readNBits
Writing
  • writeBit
  • writeByte
  • writeShort
  • writeInt
  • writeLong
  • writeNBits (you might need to change this in your code)
  • flush
Flushing the write buffer
The flush function forces any buffered data that is waiting to be written immediately to disk. It is up to you to decide whether the write functions write into an internal buffer or immediately to disk.
In the given code, the return values on some of the functions are there only so that the code will compile. The intention is that the return values from the read functions will return the read value, not some status indicator.

Library Properties

The library should exhibit two following crucial properties.

1) Reversibility

Any write operations that save a sequence data to disk, must reproduce the exact same sequence of data when it is read from disk.

2) Compactness

The library must write data as dense as possible. There must be no wasted space (bits) between data (bits). For example, if you perform eight writeBit operations, the output should take up exactly one byte on the disk. Two writeBit operations followed by a writeByte operation should take up no more than two bytes on disk. (For this assignment we can assume that data on disk is stored at byte-level granularity). Additionally, the library should never lose any information when storing to disk – the last bits of data are never clipped off because they don’t exactly fill up an entire byte.

Internally, it doesn’t matter what data structures are used to represent the stream of bits. However, once they are filed out to the disk, they must be real bits! To see what’s actually written to disk:

Under NO circumstances should the characters ‘0’ and ‘1’ written out to a file instead of the actual bits 0 and 1. This means you should not be able to open a file in a text editor and read “01010101101”.

If you happen to be outputting the characters ‘A’, ‘0’ and ‘1’, the output bits should be:

Value Binary Hex Decimal
‘A’ ‘0’ ‘1’ 01000001 00110000 00110001 41 30 31 65 48 49

Getting Started

Download the given code here. Once you have it on your hard drive, from Eclipse do the following:

  1. select file -> import
  2. under general, select existing project into workspace
  3. select archive file and browse to the file you downloaded
  4. click finish

You should now have an “Assignment 1” project in your workspace. There are two folders in the project. The src folder will contain the implementation of your bit library. The test folder will contain your unit tests.

Your task

Again, for this week you are only:

  • Comment all public functions with javadoc style headers
  • Implement unit tests for these functions
You do not have to actually implement the BitLibrary class this week. When we do implement the library, you will not be allowed to use any existing library helper classes beyond file input and output streams (i.e. no ByteArrayInputStream or BitInputStream, although you can use these in your tests)

Unit Testing

Unit testing is a great way to ensure that a function (method) properly implements is specification. This is enforced by creating small (unit) tests for each function. Each test should call the function it is testing, and assert whether or not the function executes the required behavior.

The funny thing is, tests can, and (more often than not) should be written before the functions there are testing are implemented. This may seem very strange at first, but it is really quite simple: If you know the input to a function, and you know how the function should work, then you should know exactly what its expected output. Since you know what the output should be before even calling the function, you can compare the output you get from making the function call to the expected output. If they match up, then the test has succeeded. Otherwise, it fails.

In general, one test is not sufficient to be confident in the correctness of a function. While it is better than nothing, a complete test suite would try all corner-cases, as well as the common case. For instance, if a function operates on positive integers, and it is called with a negative parameter, does it do the right thing? The success and failure of tests are completely defined by the person designing the application. Therefore, it is necessary to clearly understand what a function should do in order to test it.

In the given code, we have spec’d out several tests that you must implement, although for full credit we will expect more than the minimum (think corner-cases).

An example of a valid jUnit test would look like (for an imaginary Phone object):

public class PhoneTests {
        //note these are jUnit 4 style tests, the naming convention is different for jUnit 3 style tests

        /**
	* The constructor is supplied with valid values so no exceptions should be thrown.
	*/
	@Test
	public void ValidConstructor1() throws Exception {
                String number = "1-555-555-5555"
                String name = "Charlie Meyer"
		Phone phone = new Phone(name,number);
		assertEquals(number, phone.getNumber());
                assertEquals(name, phone.getName());

	}

	/**
	* This test checks the case where the constructor is supplied a phone number with too many dashes.
	*/
        @Test(expected=InvalidPhoneNumberException.class)
	public void TooManyDashes() throws Exception {
                String number = "9-9-999-999-9999";
                String name = "Charlie Meyer";
		Phone phone = new Phone(name,number);
	}

        //more tests below of course, you can never have too many!
}

Good testing requires you to test for the unexpected. Every function should have at least one test. However to get full credit, you should write tests which cover all corner cases and other possibilities that your library may eventually encounter. You do not need to write tests for extremely trivial, small functions (5 lines or less).

Finally, you should include tests that:

  • Ensure the input and output values of each function are valid.
  • Ensure you functions properly handle improper inputs.

Remember, test-driven development means write your tests before you write their implementation!

Although we give you the definitions for the functions to test this time, in the future when writing your functions always keep in mind writing them so that they are easy to test. The more focused each individual function is, the easier it will be to test, and the more correct your code will be. Modularity is key to unit-testing.

There are some resources which aid in the unit testing process, such as JUnit for Eclipse. You are free to use these if you would like. Eclipse and JUnit are available on the CSIL Linux computers, and an introduction to using JUnit with Eclipse is provided in this assignment description.

Introduction to Eclipse and JUnit

An example of jUnit 4 tests was shown above. Generally, developers put all unit tests for one type in one file, with multiple files of tests composing a test suite for the library or application. It will be much easier for you to keep things organized if you follow a similar pattern. Although Eclipse will let you run a single file containing jUnit tests directly, it takes a bit more effort to have Eclipse run all tests in all files at once (your test suite). For example, say I had tests for a Phone object in a file called PhoneTests and tests for an Address object in a file called AddressTests, I could have the Eclipse jUnit runner run them both by creating a file called something like ContactInfoTests and have it contain:

package contactInfo.tests;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

/**
 * Runs all tests for classes in the <code>contactInfo</code> package
 * @author Jerome Bell
 *
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({AddressTests.class, PhoneTests.class})
public class ContactInfoTests {
}

If I was to then instruct Eclipse to run ContactInfoTests as a jUnit test, it would run all the tests in both the PhoneTests class and the AddressTests class. You might not need to, but you can chain it up even further if you divide your tests into subcategories and have a master suite file like:

package allTests;

import junit.framework.TestSuite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import contactInfo.tests.ContactInfoTests;
import persistence.tests.DatabaseInterfaceTests;

@RunWith(Suite.class)
@Suite.SuiteClasses({ContactInfoTests.class, DatabaseInterfaceTests.class})
public class RunAllTests extends TestSuite{
}

Resources

  • JUnit API JavaDoc: Note that this JavaDoc is for JUnit 4.3 which is the version on the CSIL Linux terminal servers. The latest version is 4.5. You will need to find the JavaDoc for the version you use. You will certainly be interested in the Assert class. Its documentation is available here. You can use the statement “import static org.junit.Assert.” at the top of each file you have that contains tests then use the assert functions in your tests as if they were defined in that file.
  • JUnit (Wikipedia)
  • JUnit Homepage
  • Test-Driven development (Wikipedia)
  • Eclipse Homepage

Getting Eclipse

You are encouraged to use the Eclipse which is installed on the CSIL machines. If you would like, you may download and install Eclipse on your own machine. Once you have downloaded eclipse, there is a great plug-in called Subversive that integrates subversion into the IDE for you, so you do not have to worry about using the command line client or tortoisesvn. In addition, it is smart in that it only will commit the files that we require and nothing more, so you do not have to worry about accidentally committing binaries to the repository. See the home page for instructions on how to use subversion from Eclipse.

Building JUnit test cases

There is a good tutorial at http://www.vogella.de/articles/JUnit/article.html. You may ignore section 5 on Easy Mock, as it is not a requirement for this assignment. Also note that the JUnit4.x.jar file is included with Eclipse, so you should not have to download the file from the JUnit website to add it to your classpath.

Running JUnit tests

After writing some JUnit tests, you can run them by right-clicking the file containing the tests in the Package Explorer, and select Run As > JUnit Test. This will bring up the JUnit view which shows information on which tests were run, and how many passed.

For this week, all of your tests should fail. This is because we are testing an empty implementation. When we go back to implement the bit library class, you will be able to use your already written unit tests to verify that everything works properly. This is test driven development.

Deliverables

You must submit your code to SVN by 8am on Thursday, September 2nd. Please submit your code to a folder called Assignment1.1. There are instructions on how to do this from Eclipse at Using Subversion From Eclipse.

Grading

We will use the standard Grading rubric for this assignment.

Criteria Weight Comment
Code submission 1 Same as before.
Basic preparation 1 Same as before.
Requirements satisfaction 0 For this assignment, the requirements are the tests and the documentation.
Presentation 2 Same as before. Almost without exception, all the presentations have room for improvement.
Participation 2 Same as in before. This course demands that you show initiative, so please speak up. If you are having any trouble participating in section, please come to office hours and we will help you.
Testing 4 This is the primary point of this assignment.
Documentation 3 This is the secondary point of this assignment.
Naming 2 When in doubt, always use consistent and descriptive method and variable names.
Decomposition 2 You testing methods short and understandable.
Personal issue 0 Not graded yet. We will pursue this in earnest in our reaction to Assignment 1.1

Assignment1.1.zip (application/zip)
Document generated by Confluence on Feb 22, 2012 18:13

  1. No comments yet.

  1. No trackbacks yet.