Preparing and Submitting Programming Assignments

Steven J. Zeil

Last modified: Dec 23, 2023
Contents:

This document explains the procedures for preparing and turning in programming assignments in this course. For information on non-programming assignments, see here.

1 Lots to Read, Little to Write

Most of the assignments in this course will involve making small changes to a set of code for a nearly complete program, or altering a completely working program to take advantage of some new data structure or algorithm we have studied. This feels “wrong” to some students, but, when I give an assignment, it’s designed to exercise a specific lesson you have just read. I want you spending your time and effort on that lesson’s material, not on writing yet another main() function, lots of basic C++ I/O that you should have learned in earlier courses, or any of the other myriad details that suck up your time when writing a program from scratch.

As it happens, this modification of existing code is probably more representative of what real programmers do. Most real-world programming projects will not involve creating an entire program from scratch. Far more often, you will be asked to modify, fix, or enhance existing code. Even when the program itself is new, you are likely to be building upon substantial libraries of existing code.

So you may not be writing that much code in any given assignment, But part of the challenge in working with programs at this scale is that you have to develop a knack for reading existing code and seeing where you need to make changes and what you need to leave alone. Again, this is realistic. By some estimates, practicing programmers spend four times as much time reading code than they do writing it.

2 Walking Through A Typical Assignment

  1. Read the assignment page.

  2. Get the starting code and set up the project in your IDE.

  3. Write your solution, compiling, testing, & debugging as you go.

  4. Commit & push your changes.

  5. After the due date, watch for your final grade report.

  6. Review the solution and the instructor’s tests.

2.1 Read the Assignment Page

Assignment pages describe the purpose of the assignment, describe a problem and related program, and provide a limited number of examples program inputs and outputs.

2.2 Get the starting code and set up the project in your IDE.

As noted above, in a typical assignment you will be altering a few components of a larger program.

2.3 Write your solution, compiling, testing, & debugging as you go.

2.4 Commit & push your changes.

2.4.1 Submitting

2.4.2 Preliminary grade reports.

Preliminary grade reports should be generated automatically any time you push your code. These can take anywhere from a few minutes to an hour to appear, depending on how many tests are being run on your code and on how many other students may be queued up in front of you.

Preliminary grade reports are preliminary.

  • The instructor may change test data if it becomes clear that the original set is incorrect or inadequate.
  • The instructor may use additional tests for the final evaluation besides those that have been provided to you.

In the final judgment, it is your job to make sure that your code is properly tested and debugged before the due date.

2.5 After the due date, watch for your final grade report.

After the due date, the instructor will clone your repository and grade the code that you last pushed there.

You can view this final report from the same link where you obtain the preliminary grade reports.

You will know the final report is available when you receive notice that the assignment grades have been posted to Canvas.

2.6 Review the solution and the instructor’s tests.

After reading your final grade report, you can return to the “Assignment Manager” section of the assignment page and request access to the instructor’s solution.

This will include the instructor’s source code and, in a .zip file, the instructor’s tests.

The format of these tests is explained in the section on Testing Your Code, below.

2.6.1 Appeals

Instructors are fallible too, and sometimes we will have a bad test or other problem. If you discover a bad test when you review the assignment solution, you can ask to have it re-scored.

3 Policies

3.1 The Grading Criteria for Programming Assignments

For any programming assignment, you must

  1. Satisfy the stipulations and limitations of the assignment.

    Each assignment will have certain stipulations. You may be limited in what files you can change, what data structures you can use, etc. The point of any assignment is to practice and demonstrate your ability with certain skills and techniques that we have covered.

    You might be able to get that program running in some completely different manner, maybe rewriting the whole thing from scratch. It might even be easier for you to do it that way. That doesn’t matter. If you have not employed the stipulated techniques, your submission will be disqualified.

  2. Produce the correct output.

    The primary scoring criteria for programming projects is the percentage of the test cases on which your code produces correct output.

    While it’s common in beginning programming classes to write interactive code that prompts for inputs and accepts them from the keyboard, few programs these days work that way. Most truly interactive programs will work through a graphic user interface.

    But there are still many programs that work through simple text input/output interfaces. These programs typically read and write to files or possibly directly to other programs. Consequently, the I/O formats of these programs are crucial and must be followed very precisely.

    The good news is that, in most assignments for this course, I will provide the I/O code so you should not run into issues with formatting.

3.2 The Anti-Grading Criteria for Programming Assignments

These are things that you will not get points for in the grading of programming assignments:

  1. The code is “almost correct” or “looks similar to” a correct solution.

    Sorry, but almost everyone who submits buggy code thinks that their code is “almost correct” or that it “looks like” code that “should work”. It’s a well-known truism in our field that the last 10% of most projects takes 90% of the time. This is sometimes known as the 90/10 rule or the 90% syndrome.

    It’s very easy to get code to a point where it looks like something that should be correct and yet fails every test case submitted to it. The real effort in programming, and what you are rewarded for in the grading, is for getting past that “almost correct” point to something that actually passes one or more tests.

  2. Time spent on the assignment

    “I spent so many hours on this assignment.” That’s gratifying, but do you think that your classmates did not? Or, if someone managed to produce correct output while spending only half the time that you did, do you think that I should take points away from them for having worked more efficiently than you?

    Telling me how much time you spent on an assignment may earn you sympathy. And in some cases I may want to talk with you about whether you are using your time in a smart way. I may be able to suggest more efficient ways for you to approach your coding, testing, and debugging. But it does not demonstrate your mastery of whatever techniques the assignment is trying to showcase.

4 Testing Your Code

Testing is an important and non-trivial part of any programming project, and I regard it as part of your duties in any programming assignment.

There are two kinds of testing that we practice in this course, system testing and unit testing.

The assignment makefile will be set up to run both kinds of test automatically, including checking the output to see if your code passes or fails the test.

You will need to know, however, how to launch the tests “manually” fom the command line so that, if you are failing a particular test, you can run it in a debugger.

4.1 System Testing

System testing is the testing of the entire program. This is the type of testing that you are most used to from your earlier programming courses. You run the program, feed it appropriate input, and observe the outputs to see if they are correct.

Don’t be satisfied with the one or two examples given on the assignment web page. Make up your own test data. Proper choice of test data was discussed in the prerequisite course CS250, and I expect you to use that knowledge.

4.1.1 Running System Tests Automatically

The command

make tests

will compile the code if necessary and then will run the system and unit tests.

System tests are typically packaged in the Tests/ directory.

Each subdirectory of Tests/ represents a single test case. Inside that subdirectory, you will find one or more of the following file types:

4.1.2 Running System Tests Manually

The assignment page will describe how to run the main program. You can do this with system test data of your own devising or with, the appropriate path, data files from the instructor-provided tests/ directory. For example, if the assignment page said to run your program like this

./yourProgram < yourData.txt

and you have a systems test Tests/test01/, you could run

./yourProgram < Tests/test01/*.in

4.1.3 Writing Your Own System Tests

You are strongly encouraged to do additional system testing beyond what is provided by the instructor.

You can simply run the program, as described in the assignment page, with your own inputs.

4.2 Unit Testing

A unit test is a test of a module (typically a single class, sometimes a single function) in isolation from the rest of the program.

Because the module being tested cannot be executed by itself, unit testing requires programmers to write scaffolding code to wrap the module into something that can be executed, providing a way to feed inputs to the module and collect the outputs from it.

Although this may seem like more work, writing code that you won’t actually be turning in for grading, it can make it easier to test your class and much, much easier to debug it.

Real programmers do a lot of unit testing, and most use a unit test framework to do it. You may already be familiar with frameworks such as JUnit or GoogleTest from CS350 or from your own reading. And if you have not taken CS350 yet, you can look forward to learning about them there.

Unit test frameworks simplify the process of writing tests by providing

It is typical of these frameworks that they are quiet so long as your code is passing the tests, but announce failures immediately, ending the test case as soon as a failure is detected.

For example, a test of a simple Counter class:

class Counter {
    int theCount;
  public:
    Counter (int initialValue = 0);
    void increment();
    int getValue() const;
    bool isZero() const;
};

might look like

UnitTest (testConstructor)
{
   Counter x (23);
   assertThat (x.getValue(), is(23));
   assertThat (x.isZero(), is(false));

   Counter c;
   assertThat (c.isZero(), is(true));
 }

UnitTest (testIncrement)
{
   Counter x (23);
   x.increment();
   assertThat (x.getValue(), is(24));
   assertThat (x.isZero(), is(false));
   x.increment();
   assertThat (x.getValue(), is(25));
 }

If these tests were run on a correctly implemented version of Counter, all that likely would be seen is a final summary:

 Passed 2 of 2 tests.

But if these tests were run on a version of Counter with a buggy implementation of increment, you might see something more like:

Assertion x.getValue() is(24) failed in testIncrement, line 14.
Passed 1 of 2 tests. Failed 1 tests.

In some assignments, if you are assigned to implement a class that is a small portion of the overall program, I may use unit tests like these to exercise your code. Sometimes, I will provide those unit tests as part of the assignment code, written using this test framework. If you see files named unittest.h and unittest.cpp included in an assignment’s code, then you can expect to find unit tests in files named test*.cpp. The makefile in such assignments will probably produce an executable program called unittests or unittests.exe, which you can run to apply those unit tests.

Unit tests are not a replacement for system testing. You are still responsible for doing your own system tests.

4.3 Illegal Inputs

You are not responsible for testing your program on illegal inputs.

An illegal input is one that is specifically prohibited by the problem statement or one for which no behavior has been specified for the program to follow.

It’s actually not possible to test programs on illegal inputs because you cannot pass or fail such a test. A program can do anything it wants on an illegal input (including crash) because, by definition, no behavior has been specified.

5 Debugging Your Code

If your testing reveals a problem, debug your code (also here (250/333), here (252) and here (252).

Once you have fixed the current problem, go back to the previous step and re-test.

6 Files Provided in Assignment Directories

Typically, each assignment directory will include a number of .cpp and .h files. Some of these are for you to modify, some should be left alone. You can tell which should be modified by consulting the list of files to be submitted. Anything that you aren’t going to turn in must be left unchanged.

Other files that you may find in the assignment directories include: