← Back to course
Python 11-12 / Week 12 / Friday
5/6
Week 12 · Reading Java II and Ship

Friday

Trace Friday: Document the design
// ArrayList, sorting, searching and recursion in Java; then a code review and the FieldSim release
⏱ about 30 min

Friday: Trace Friday: Document the Design

Comet flips through her sketch notebook, full of FieldSim drawings. "All our design ideas are in here," she says. "But nobody else can read my notebook."

Wren taps his chin. "What does the evidence say? A new programmer would only see the code."

Nova projects fieldsim.py with a blank space under its header. "How can I help?" she asks. "Comments help you and other programmers. A design note at the top can explain the classes and the choices."

Comet picks up her pencil. "And we should add tests for the algorithms, too."

Wren smiles. "Lead programmer, document it and test it."

A design note in comments

Comments are written for you and for other programmers, so they can understand the code and what it does.

Before a class is written, it helps to design its attributes and behaviors in words or a diagram. A design note keeps that design with the code.

Add these comment lines to fieldsim.py, just below its two header lines:

#
# Design note
# Classes: a Sample is one sample token. A Rover drives, turns and
# carries samples. A FieldGrid is the test field as a 2D list.
# Why _battery is hidden: only move() changes it, one step at a time,
# so outside code cannot set it to a value Pip never earned.
# Why tests exist: test_fieldsim.py checks the grid, turns, moves,
# pick_up and the algorithms after every change.
# fieldsim.py
# FieldSim: drive a rover across the test field and report what it found.
#
# Design note
# Classes: a Sample is one sample token. A Rover drives, turns and
# carries samples. A FieldGrid is the test field as a 2D list.
# Why _battery is hidden: only move() changes it, one step at a time,
# so outside code cannot set it to a value Pip never earned.
# Why tests exist: test_fieldsim.py checks the grid, turns, moves,
# pick_up and the algorithms after every change.
from field_data import FIELD, FIELD_SAMPLES
from grid import FieldGrid
from rover import Rover
from sample import Sample
from sorts import sort_by_grams


def collect_here(rover, grid, samples_by_spot):
    """Pick up the sample under the rover, if there is one."""
    spot = (rover.get_row(), rover.get_col())
    if grid.cell(spot[0], spot[1]) == "*" and spot in samples_by_spot:
        if rover.pick_up(samples_by_spot[spot]):
            grid.set_cell(spot[0], spot[1], ".")


def main():
    grid = FieldGrid(FIELD)
    samples_by_spot = {}
    for label, kind, grams, row, col in FIELD_SAMPLES:
        samples_by_spot[(row, col)] = Sample(label, kind, grams, row, col)
    pip = Rover("Pip")
    for c in "FFRFFFLFFLFRRFFLF":
        pip.run(c, grid)
        collect_here(pip, grid, samples_by_spot)
    grid.show(pip)
    print(pip.status())
    found = pip.get_samples()
    sort_by_grams(found)
    for s in found:
        print(s.describe())
    print(f"Total: {pip.total_grams()} g")


main()
COMMENTS AND THE RUN
  • Read the question.
  • Tap your answer.
After you add the design note, what does fieldsim.py print?
Why does the note say _battery is hidden?

Extend the tests

Programmers test with many conditions to make a program reliable. Add these two tests to test_fieldsim.py, above the lines that call the tests.

Add the three new imports at the top, below the import of Sample. Then call test_algorithms() and test_reachable() before the final print.

from search import binary_search
from sorts import selection_sort
from recursion import total, reachable


def test_algorithms():
    nums = [42, 18, 65, 7, 30]
    selection_sort(nums)
    assert nums == [7, 18, 30, 42, 65]
    grams = [7, 12, 18, 25, 30, 39, 42, 51, 65, 88]
    assert binary_search(grams, 39) == 5
    assert binary_search(grams, 40) == -1
    assert total(grams) == 377


def test_reachable():
    grid = FieldGrid(FIELD)
    assert reachable(grid, 0, 0, set()) == 26
    grid.set_cell(0, 1, "#")
    grid.set_cell(1, 0, "#")
    assert reachable(grid, 0, 0, set()) == 1
# test_fieldsim.py
# Tests for FieldSim. Run this file: it prints one line when all pass.
from field_data import FIELD
from grid import FieldGrid
from rover import Rover
from sample import Sample
from search import binary_search
from sorts import selection_sort
from recursion import total, reachable


def test_grid():
    grid = FieldGrid(FIELD)
    assert grid.height() == 5
    assert grid.width() == 6
    assert grid.is_open(0, 0)
    assert not grid.is_open(1, 1)
    assert not grid.is_open(-1, 0)
    assert not grid.is_open(0, 6)


def test_turns():
    rover = Rover("Test")
    rover.turn_right()
    assert rover.get_heading() == "S"
    rover.turn_left()
    rover.turn_left()
    assert rover.get_heading() == "N"


def test_move():
    grid = FieldGrid(FIELD)
    rover = Rover("Test")
    assert rover.move(grid)
    assert (rover.get_row(), rover.get_col()) == (0, 1)
    assert rover.get_battery() == 95
    rover.turn_left()
    assert not rover.move(grid)
    assert rover.get_row() == 0


def test_pick_up():
    rover = Rover("Test")
    for n in range(3):
        assert rover.pick_up(Sample(f"T{n}", "clay", 10))
    assert not rover.pick_up(Sample("T3", "clay", 10))
    assert rover.total_grams() == 30


def test_algorithms():
    nums = [42, 18, 65, 7, 30]
    selection_sort(nums)
    assert nums == [7, 18, 30, 42, 65]
    grams = [7, 12, 18, 25, 30, 39, 42, 51, 65, 88]
    assert binary_search(grams, 39) == 5
    assert binary_search(grams, 40) == -1
    assert total(grams) == 377


def test_reachable():
    grid = FieldGrid(FIELD)
    assert reachable(grid, 0, 0, set()) == 26
    grid.set_cell(0, 1, "#")
    grid.set_cell(1, 0, "#")
    assert reachable(grid, 0, 0, set()) == 1


test_grid()
test_turns()
test_move()
test_pick_up()
test_algorithms()
test_reachable()
print("All FieldSim tests passed")

The file still prints one line when every test passes:

All FieldSim tests passed
TRACE THE TESTS
  • Read the question.
  • Tap your answer.
In test_algorithms, what is nums after selection_sort(nums)?
Which test checks an edge case, a field where Pip is boxed in?
At your computer
1. Add the design note to fieldsim.py. Run it and check the output did not change.
2. Add the imports and the two tests to test_fieldsim.py. Run it.
3. Change 26 to 27 in test_reachable on purpose. Run it and read the AssertionError calmly.
4. Change it back to 26 and run the tests once more.

Impact and reliability

System reliability means a program does its job as expected without failing. Programs can have helpful and harmful effects, including effects nobody intended.

Imagine the crew had trusted a FieldSim that was never tested. Suppose is_open forgot the rocks. The simulation would say a route is clear when it is not.

StatementTrue or false?
A comment changes what a program prints.?
Testing with many conditions helps make a program reliable.?
A simulation that was never tested could give a team a wrong route.?
A program built to help can never have a harmful effect.?
WHY THIS EXERCISEComments never change the run. Testing improves reliability, and even helpful programs can have unintended effects.
  1. ArrayList is Java's resizable list: add, get and size. Java needs an import for it.
  2. Selection sort, binary search and recursion read step for step the same in Java and Python.
  3. A release is saved once the program runs and its tests pass. Experiments go in a copy.
  4. A code review checks qualities such as correctness, readability, efficiency, usability, portability and scalability.
  5. A design note in comments explains the classes and the choices to the next programmer.

FieldSim is shipped, reviewed, documented and tested, lead programmer. Tomorrow you get to show it off.

← Thursday