Instructions for functionTesting

(produced at 03:53 a.m. UTC on 2021-11-08)

This task is part of ps07 which is due at 23:59 EST on 2021-11-09.

You have the option to work with a partner on this task if you wish. Working with a partner requires more work to coordinate schedules, but if you work together and make sure that you are both understanding the code you write, you will make progress faster and learn more.

You can submit this task using this link.

Put all of your work for this task into the file functionTesting.py
(which is provided among the starter files)

General Overview

Note: This problem is now complete.

In this problem, you will perform so-called "glass-box" testing on functions, as described in the Testing and Debugging lecture. That is, you will be able to see the definition of each function, and your goal is to determine whether it is correct or incorrect. In the case where it is incorrect, you need to provide a counterexample input on which it behaves incorrectly.

In particular, you will be studying many versions of a replaceVowelSequences that loops over the characters of a word. This function is defined at the end of the notebook on from the Nested Loops Lecture.

Background: The replaceVowelSequences function specification

We'll begin by reviewing the specification for the replaceVowelSequences function, since understanding precisely what it is supposed to do is essential for testing it.

Just to be very clear, you are not being asked to define this function. Rather you will be asked to write test cases for various definitions of this function.

Because vowels are more likely to change over time than consonants, linguists sometimes describe words in terms of just their consonants, putting in a star (asterisk) for a sequence of consecutive vowels. The function replaceVowelSequences takes a word and returns a string that replaces each sequence of vowels in the word with an asterisk. For example:

wordreplaceVowelSequences(word)
'dog''d*g'
'seafood''s*f*d'
'audacious''*d*c*s'
'amnesia''*mn*s*'
'stethoscope''st*th*sc*p*'
'dry''dry'
''''

Here are iteration tables that show the function working on some examples:

Example 1: 'dog'

charresultresult
''False
'd''d'False
'o''d*'True
'g''d*g'False

Example 2: 'audacious'

charresultresult
''False
'a''*'True
'u''*'True
'd''*d'False
'a''*d*'True
'c''*d*c'False
'i''*d*c*'True
'o''*d*c*'True
'u''*d*c*'True
's''*d*c*s'False

Recall that the first row of an iteration table shows the values of state variables before the loop begins, and each other row shows their values after of executing all the loop body statements in each iteration of the loop. In the above tables, char has no value in the first row because it is only defined during each iteration of the loop.

Background: A correct definition for replaceVowelSequences

The replaceVowelSequences function can be defined by using a for loop with the state variables shown in the above iteration tables. We'll assume there is a correctly defined isVowel predicate that can be used in this function

Below is one of many possible correct solutions to replaceVowelSequences. We give it the short name rVSCorrect to allow us to distinguish many versions of function definitions for this problem.

def rVSCorrect(word):
    result = '' # initialize state variable for accumulating result string
    inVowelSequence = False # initialize state variable that determines when to add '*'
    for char in word: # iterate over each character in word, using char as iteration variable
        if isVowel(char):
            if not inVowelSequence: # add '*' only when previous char was not vowel
                result += '*'
                inVowelSequence = True # For next time, indicate previous char *was* a vowel
        else: # use else rather than testing `not isVowel(char)`
            result += char # always add a nonvowel to result
            inVowelSequence = False # For next time, indicate previous char was *not* a vowel
    return result

Background: counterexamples

In this task, you will study some correct and incorrect definitions for replaceVowelSequences and determine why the incorrect ones don't work. Incorrect programs are said to be buggy because they contain bugs = reasons why they don't work. So your goal is to track down the bugs in each of the buggy function definitions, or convince yourself that a definition is correct.

One way to show that a function definition is buggy is to provide a counterexample, which is a particular input on which the function does not behave correctly. For a counterexample, the buggy function might return a wrong answer, or it might crash with an error when the correct function does not.

First buggy function example: rVSBuggy1

Here is a function rVSBuggy1 that is an incorrect version of replaceVowelSequences:

def rVSBuggy1(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if isVowel(char):
            result += '*'
            inVowelSequence = True
        else:
            result += char
            inVowelSequence = False
    return result

The bug in this function is that it doesn't check the state variable inVowelSequence to determine whether * should be added to result, so it replaces every vowel by *. For example: rVSBuggy1('seafood') returns the incorrect result 's**f**d' rather than the correct result 's*f*d'. 'seafood' is a counterexample for rVSBuggy1 because it illustrates that the function does not behave correctly. Any string with two or more consecutive vowels is a counterexample for rVSBuggy1, such as 'loud', 'amnesia', or 'aa'.

rVSBuggy1 will work correctly on strings that do not contain two vowels in a row, so such strings are not counterexamples. For example, 'dog', 'stethoscope', 'a', 'dry', and '' are not counterexamples for rVSBuggy1.

A counterexample is said to be minimal if it the shortest counterexample for a function. For rVSBuggy1, any string consisting of exactly two vowels is a minimal counterexample, such as 'aa' or 'ou'.

Second buggy function example: rVSBuggy2

Here is another incorrect definition of replaceVowelSequences :

def rVSBuggy2(word):
    i = 0 # i is index of current char                                                                       
    while i < len(word):
        if isVowel(word[i]):
            result += '*' # Add * for first vowel in sequence                                                
            i += 1
            while isVowel(word[i]) and i < len(word): 
                # Skip to next nonvowel or end of word                                                       
                i += 1
        else:
            result += word[i] # Add nonvowel                                                                 
            i += 1
    return result

This function ignores the strategy illustrated by the iteration table, and uses nested while loops rather than a single for loop. Nevertheless, it is close to having the correct behavior. It uses an index variable i to traverse the characters word[i] in the string. It copies non-vowels to result. When it encounters a vowel, it adds '*' to result, and then uses the nested while loop to find the next non-vowel or end of the word. Because it checks isVowel(word[i]) before i < len(word), it will crash with an Index Error when i becomes equal to len(word). This will happen for any string that ends in a vowel, such as 'like', 'amnesia', 'sequoia', and 'a'. Minimal counterexamples for rVSBuggy2 are strings with a single vowel, such as 'a' or 'e'.

Interestingly, rVSBuggy2 behaves correctly for strings in which at least one non-vowel follows every sequence of values, such as 'dog', 'seafood', and 'audacious'. And it would have the correct behavior on all strings if the inner while loop test were changed to i < len(word) and isVowel(word[i])

Your Task

The file replaceVowelSequences.py contains 12 definitions of the replaceVowelSequences function, which are named rVS01 through rVS12. Some of these definitions are correct and some are buggy. These 12 functions are shown below.

At the bottom of the file are 12 variables named counterexample01 through counterexample12 that are by default assigned to be None and 12 variables named explanation01 through explanation12 that are by default assigned to be None.

Your task is to change the assignment of these 12 pairs of variables as follows.

  • If rVSNN (where NN is a two-digit sequence between 01 and 12) is a buggy definition of replaceVowelSequences, then you must assign counterexampleNN to a counterexample input string for rVSNN and explanationNN to be a triple-quoted string that briefly explains why the function is buggy. As an extra goal, your counterexample input string should be minimal.

    For example, if rVS20 had the same definition as rVSBuggy1, then you could use the assignments

    counterexample20 = 'seafood'
    explanation20 = '''Replaces every vowel by *.'''
    

    to satisfy the core goal and

    counterexample20 = 'aa'
    explanation20 = '''Replaces every vowel by *.'''
    

    to satisfy both the core and extra goals.

    As another example, if rVS21 had the same definition as rVSBuggy2, then you could use the assignments:

    counterexample21 = 'like'
    explanation21 = '''Raises an IndexError for any string ending in a vowel.'''
    

    to satisfy the core goal and

    counterexample21 = 'a'
    explanation21 = '''Raises an IndexError for any string ending in a vowel.'''
    

    to satisfy both the core and extra goals.

  • If rVSNN is a correct definition of replaceVowelSequences, then you must assign both counterexampleNN and explanationNN to the string 'correct'. For example, if rVS22 had the same definition as rVSCorrect, then you would use the assignments

    counterexample22 = 'correct'
    ounterexample22 = 'correct'
    

    Note that the string 'correct' is treated specially here; it is not treated as an counterexample string on which the function has the wrong behavior.

For the purposes of testing and the rubric, the 12 versions of replaceVowelSequences have been divided in three chunks of 4 definitions each, but there are similar descriptions for each chunk. For instance, for rVSNN, where NN ranges from 01 to 04, there is a core goal of finding any counterexample string for each function in this chunk (or stating it's 'correct'). But finding a minimal counterexample string for each function in this chunk (or stating it's correct) is an extra goal. And providing an explanation why incorrect definitions are incorrect is another extra goal for this chunk..

The 12 versions of replaceVowelSequences

Below are the 12 versions of replaceVowelSequences you will study in this problem. Almost all of these functions are based on actual student solutions from a previous midterm exam, though in many cases they have been edited to simplify them, correct bugs, rename variables consistently, etc.

def rVS01(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if isVowel(char) and inVowelSequence:
            result += '*'
            inVowelSequence = True
        elif not isVowel(char):
            result += char
            inVowelSequence = False
    return result

def rVS02(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if isVowel(char):
            result += ''
            inVowelSequence = True
            if inVowelSequence:
                result += '*'
                else:
            result += char
    return result

def rVS03(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if inVowelSequence:
            if not isVowel(char):
                result += '*'
                inVowelSequence = False
        elif isVowel(char):
            inVowelSequence = True
        else:
            result += char
    return result

def rVS04(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if inVowelSequence:
            if not isVowel(char):
                result += char
                inVowelSequence = False
        elif isVowel(char):
            result += '*'
            inVowelSequence = True
        else:
            result += char
            inVowelSequence = False
    return result

def rVS05(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if not isVowel(char):
            result += char
            inVowelSequence = False
        else:
            inVowelSequence = True
        if inVowelSequence:
            if result[-1] != '*':
                result += '*'
    return result

def rVS06(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if inVowelSequence:
            if not isVowel(char):
                inVowelSequence = False
                result += char
        elif isVowel(char):
            inVowelSequence = True
            result += '*'
    return result

def rVS07(word):
    result = ''
    inVowelSequence = False
    for char in word:
        if not isVowel(char):
            result += char
        elif inVowelSequence:
            result += '*'
            inVowelSequence = True
        else:
            inVowelSequence = True
    return result

def rVS08(word):
    result = ''
    for char in word:
        if not isVowel(char):
            result += char
        else:
            inVowelSequence = '*' in result
            if not inVowelSequence:
                result += '*'
    return result

def rVS09(word):
    result = ''
    inVowelSequence = False
    counter = 0
    for char in word:
        if isVowel(char):
            inVowelSequence = True
        else:
            inVowelSequence = False
        if not inVowelSequence:
            result += char
        elif counter == 0 or not isVowel(word[counter-1]):
            result += '*'
        counter += 1
    return result

def rVS10(word):
    result = ''
    vowels = ''
    for char in word:
        if isVowel(char):
            vowels = '*'
        else:
            result += vowels
            vowels = ''
            result += char
    return result

def rVS11(word):
    result = ''
    vowelSequence = ''
    for char in word:
        if isVowel(char):
            vowelSequence += char
        else:
            vowelSequence = ''
        for element in vowelSequence:
            result += '*'
        if not isVowel(char):
            result += char
    return result

def rVS12(word):
    result = ''
    i = 0 # i is index of current char                                                                       
    while i < len(word):
        if isVowel(word[i]):
            if not isVowel(word[i-1]):
                result += '*' # Add * only for first vowel in sequence                                       
        else:
            result += word[i] # Add nonvowel                                                                 
        i += 1
    return result

Examples

Counterexample structure

Below are examples of how nonminimal counterexamples and explanations are expressed as strings. They involve a bogus function definition rVS20 which does not exist. You will define similar variables whose number suffice ranges from 00 to 12.

In []:
counterexample20 = 'seafood' explanation20 = '''Replaces every vowel by *.'''
In []:
counterexample21 = 'like' explanation21 = '''Raises an IndexError for any string ending in a vowel.'''

To satify the extra goal, your counterexamples should all be minimal Below are minimal counterexamples corresponding to the above non-minimial ones.

In []:
counterexample20 = 'aa' explanation20 = '''Replaces every vowel by *.'''
In []:
counterexample21 = 'a' explanation21 = '''Raises an IndexError for any string ending in a vowel.'''

Rubric

 
unknown Product Requirements
Your code's result values.
 
unknown Core goals
Complete all core goals for core credit. Get partial credit for completing at least half, and more partial credit for completing at least 90%.
 
unknown counterexamples 01 through 04
The variables counterexampleNN, where NN ranges from 01 to 04, must be defined as strings. If the corresponding function rVSNN is correct, then counterexampleNN must have the value 'correct'. If the corresponding function rVSNN is incorrect, then counterexampleNN must have a value such that rVSNN(counterexampleNN) generates an incorrect result or causes and error.
 
unknown counterexamples 05 through 08
The variables counterexampleNN, where NN ranges from 05 to 08, must be defined as strings. If the corresponding function rVSNN is correct, then counterexampleNN must have the value 'correct'. If the corresponding function rVSNN is incorrect, then counterexampleNN must have a value such that rVSNN(counterexampleNN) generates an incorrect result or causes and error.
 
unknown counterexamples 09 through 12
The variables counterexampleNN, where NN ranges from 09 to 12, must be defined as strings. If the corresponding function rVSNN is correct, then counterexampleNN must have the value 'correct'. If the corresponding function rVSNN is incorrect, then counterexampleNN must have a value such that rVSNN(counterexampleNN) generates an incorrect result or causes and error.
 
unknown Extra goals
Complete all extra goals in addition to the core goals for a perfect score.
 
unknown counterexamples 01 through 04
The variables counterexampleNN, where NN ranges from 01 to 04, must be defined as strings. If the corresponding function rVSNN is correct, then counterexampleNN must have the value 'correct'. If the corresponding function rVSNN is incorrect, then counterexampleNN must have a value such that rVSNN(counterexampleNN) generates an incorrect result or causes an error and counterexampleNN must be minimal in the sense that has the shortest length of any counterexample string.
 
unknown explanations 01 through 04
The variables explanationNN, where NN ranges from 01 to 04, must be defined as strings. If the corresponding function rVSNN is correct, then explanationNN must have the value 'correct'. If the corresponding functionrVSNN is incorrect, then explanationNN must have a value that is a string that briefly explains (at a high level) **why** the function is incorrect, and describe the kinds of kinds of counterexample strings on which it fails.
 
unknown counterexamples 05 through 08
The variables counterexampleNN, where NN ranges from 05 to 08, must be defined as strings. If the corresponding function rVSNN is correct, then counterexampleNN must have the value 'correct'. If the corresponding function rVSNN is incorrect, then counterexampleNN must have a value such that rVSNN(counterexampleNN) generates an incorrect result or causes an error and counterexampleNN must be minimal in the sense that has the shortest length of any counterexample string.
 
unknown explanations 05 through 08
The variables explanationNN, where NN ranges from 05 to 08, must be defined as strings. If the corresponding function rVSNN is correct, then explanationNN must have the value 'correct'. If the corresponding functionrVSNN is incorrect, then explanationNN must have a value that is a string that briefly explains (at a high level) **why** the function is incorrect, and describe the kinds of kinds of counterexample strings on which it fails.
 
unknown counterexamples 09 through 12
The variables counterexampleNN, where NN ranges from 09 to 12, must be defined as strings. If the corresponding function rVSNN is correct, then counterexampleNN must have the value 'correct'. If the corresponding function rVSNN is incorrect, then counterexampleNN must have a value such that rVSNN(counterexampleNN) generates an incorrect result or causes an error and counterexampleNN must be minimal in the sense that has the shortest length of any counterexample string.
 
unknown explanations 09 through 12
The variables explanationNN, where NN ranges from 09 to 12, must be defined as strings. If the corresponding function rVSNN is correct, then explanationNN must have the value 'correct'. If the corresponding functionrVSNN is incorrect, then explanationNN must have a value that is a string that briefly explains (at a high level) **why** the function is incorrect, and describe the kinds of kinds of counterexample strings on which it fails.