Pyret Testing Design and Clarity Guide

Testing

  1. All functions require examples/test cases, including helper functions. Functions that output images require examples; all other functions require test cases.
  2. Examples/test cases should reflect various input scenarios so that you exercise the possible main behaviors of your function. You do not need to test every input or every input combination, but rather enough to be confident that your function works with various inputs.
  3. Test the “edge cases” of functions. For example, functions that take in numeric inputs and work differently depending on the range that number is in should test the boundaries of those ranges.
  4. Even if your function does not work as expected, you can still write examples/test cases for it and receive full testing credit.

Example of good testing:

fun abs-val(x :: Number) -> Number:
  doc: ``` description: give the absolute value of the input number
       inputs : x, a number
       output : the absolute value of x ```
  ... # i never figured this function out
where:
  abs-val(-4) is 4 # test the first branch of the if
  abs-val(0) is 0  # 0 is an "edge case"
  abs-val(3) is 3  # test the else branch of the if
end

A handin with this work would receive full testing points.

Design

  1. Use constants where appropriate. rectangle(500, 300, "solid", "green") should be rewritten to:
    width = 500
    rectangle(width, 3/5 * width, "solid", "green")
    
  2. Use helper functions where appropriate.
    stairs = beside-align("bottom", rectangle(50, 50, "solid", "gray"),
     beside-align("bottom", rectangle(50, 100, "solid", "gray"),
       beside-align("bottom", rectangle(50, 150, "solid", "gray"),
         rectangle(50, 200, "solid", "gray"))))
    

could be rewritten as:

stair-width = 50
fun stair(height :: Number) -> Image:
  doc: "make an individual stair given its height"
  rectangle(stair-width, height, "solid", "gray")
end

stairs = beside-align("bottom", stair(50),
  beside-align("bottom", stair(100),
    beside-align("bottom", stair(150), stair(200))))
  1. Make sure your helper functions and constants are not redundant.
    There is no point in making this function:
    fun string-lower(s :: String) -> String:
      string-to-lower(s)
    end
    
    since you could always use string-to-lower instead.

Clarity

  1. Write docstrings for all functions, including helper and nested functions.
    A good docstring gives a description of the function, including its input(s) and output. Ideally, by looking at the docstring you know what the function does and how to use it without looking at the function body itself.

    fun three-stripes(bot-col :: String, mid-col :: String,
      top-col :: String) -> Image:
      doc: ```
      description: make a three-striped image with the input colors
      inputs:
        - bot-col : the color of the bottom stripe
        - mid-col : the color of the middle stripe
        - top-col : the color of the top stripe
      output: a three-striped image with the colors of the stripes
      corresponding to the input colors
    

    Bad docstring descriptions

    1. doc: "helper function for flag making"
    2. doc: "output the right image for this problem"
    3. doc: "uses above to stack images" - how your function body actually works is irrelevant to the docstring.
  2. Give constants and helper functions useful names.

    n1 = 3.1415
    n2 = 500
    n3 = 30
    fun helper(x):
      x + 3
    end
    

    should be rewritten as (for example)

    pi = 3.1415
    flag-width = 500
    circle-radius = 30
    fun add-3(x):
      doc: ```
      description: add three to the input
      inputs : x, an integer
      output : the result of adding 3 to x
      x + 3
    end
    
  3. All functions require type annotations on both inputs and output.

  4. Names of constants and functions should be lower case and dash separated. For configuration constants (for example the height of a character) it is acceptable to use all caps dash-separated names.

    # Good Pyret Style:
    flag-width = 500
    width-to-height-ratio = 3/5
    CHARACTER-HEIGHT = 40
    
    # Bad Pyret Style:
    flag_width = 500
    widthHeightRatio = 3/5
    CharacterHeight = 40
    
  5. Keep lines under 80 characters. You will at some point see a vertical dashed blue line in Pyret.

    If you see this line, your lines are too long and you need to add linebreaks (press enter at places in your code).

    The 80 character limit applies even when you have long strings and/or docstrings. Long docstrings can be written using the backtick (`) button:

    fun f()
      doc: ``` return 2; no inputs, always outputs 2 ```
      2
    end
    
  6. Indent your code properly. You can do this by pressing ctrl-a then tab on Windows and Linux, or cmd-a then tab on Mac.