hw

abstract class hw(val courseName: String) extends Actions

Base class for a homework.

The code for a typical homework will begin

 object hw7 extends hwtest.hw("CS123"):
   ...

The hw class defines a main method that runs all the tests, so the homework code (object hw7 above) does not, and in fact cannot, define a new main because the main method is final.

trait Actions
class Object
trait Matchable
class Any

Type members

Classlikes

class ConfigurableTest(name: String)

Provides a way to configure the timeLimit and failureLimit for automated tests (test(...) or testV(...)), which return a ConfigurableTest.

Provides a way to configure the timeLimit and failureLimit for automated tests (test(...) or testV(...)), which return a ConfigurableTest.

Should typically be called with named parameters, as in

 test(...)(timeLimit = 1000)
 test(...)(failureLimit = 5)
 test(...)(timeLimit = 1000, failureLimit = 1)

Also works with testV, as in

 testV(...){
   ... // validation code
 }(timeLimit = 1000)

Note that, if you are satisfied with the default time limit and failure limit you can simply say

 test(...)

rather than

 test(...)()

For compatibility, ignoretest/ignoretestV also return a ConfigurableTest, so that you can safely change back and forth between (for example)

 test(...)(failureLimit = 5)

and

 ignoretest(...)(failureLimit = 5)

However, as would be expected, ignoretest/ignoretestV do not actually use the timeLimit or failureLimit.

The default time limit is 500ms and the default failure limit is 2.

class Params1[A](TA: Testable[A])
case class Params2[A, B](TA: Testable[A], TB: Testable[B])
case class Params3[A, B, C](TA: Testable[A], TB: Testable[B], TC: Testable[C])
case class Params4[A, B, C, D](TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D])
case class Params5[A, B, C, D, E](TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E])
case class Params6[A, B, C, D, E, F](TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TF: Testable[F])

Value members

Abstract methods

def userName: String

Returns the user's name as a string, which will be displayed in the program's output.

Returns the user's name as a string, which will be displayed in the program's output.

Starter code distributed to students for a typical homework will begin as

 object hw7 extends hwtest.hw("CS123"):
   def userName = ???
   ...

and the student should replace the ??? with their name, as in

   def userName = "Margaret Hamilton"

This method helps to reduce the common occurrence of a student forgetting to put their name on a homework.

  • If they forget to change the ???, they will discover this during testing because their program will immediately fail with a NotImplementedError.
  • If they accidentally delete the entire userName line, their code will not compile because the object cannot be instantiated without userName being defined.

In addition, it helps ensure that the student's name is in both the program and the program's output.

Concrete methods

def debug(msg: String)(action: => Any): Unit

Encapsulates debugging code.

Encapsulates debugging code.

The intent of this library is that most debugging should be based on failed test cases, using the clues provided by the failure message. However, there can be times when you want to fall back on old-fashioned println debugging.

Before doing so, first change any test/testV commands for the function you are debugging to ignoretest/ignoreV. Then, just above or below the ignoretest/ignoreV line, put

 debug("some message") {
   ...debugging code...
 }

Usually, the debugging code will call the function in question on some known input. You can also put printlns inside the body of the function in question to trace what is happening internally. Those internal printlns should not be wrapped in a debug command.

def ignoredebug(msg: String)(action: => Any): Unit
def ignoretest[A, R](name: String, f: A => R, param1: String)(using TA: Testable[A], TR: Testable[R]): ConfigurableTest

Skips this test.

Skips this test.

Never comment-out or delete a test command. Among other things, this would cause the tests and the test data to become out of sync.

Instead, if you don't want a test to run for some reason, change it to ignoretest, which will prevent the test from running but still keep the test data in sync.

Returns a hwtest.ConfigurableTest, like test does, so you can switch freely between test and ignoretest. However, ignoretest will ignore any such configuration.

Value parameters:
f

the function to test

name

the name to be displayed for the function being tested

param1

the name of the function's parameter

def ignoretest[A, B, R](name: String, f: (A, B) => R, param1: String, param2: String)(using TA: Testable[A], TB: Testable[B], TR: Testable[R]): ConfigurableTest

Like ignoretest[A,R] but for two-parameter functions.

Like ignoretest[A,R] but for two-parameter functions.

def ignoretest[A, B, C, R](name: String, f: (A, B, C) => R, param1: String, param2: String, param3: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TR: Testable[R]): ConfigurableTest

Like ignoretest[A,R] but for three-parameter functions.

Like ignoretest[A,R] but for three-parameter functions.

def ignoretest[A, B, C, D, R](name: String, f: (A, B, C, D) => R, param1: String, param2: String, param3: String, param4: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TR: Testable[R]): ConfigurableTest

Like ignoretest[A,R] but for four-parameter functions.

Like ignoretest[A,R] but for four-parameter functions.

def ignoretest[A, B, C, D, E, R](name: String, f: (A, B, C, D, E) => R, param1: String, param2: String, param3: String, param4: String, param5: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TR: Testable[R]): ConfigurableTest

Like ignoretest[A,R] but for five-parameter functions.

Like ignoretest[A,R] but for five-parameter functions.

def ignoretest[A, B, C, D, E, F, R](name: String, f: (A, B, C, D, E, F) => R, param1: String, param2: String, param3: String, param4: String, param5: String, param6: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TF: Testable[F], TR: Testable[R]): ConfigurableTest

Like ignoretest[A,R] but for six-parameter functions.

Like ignoretest[A,R] but for six-parameter functions.

def ignoretestV[A, R](name: String, f: A => R, param1: String)(validate: (A, R) => Unit)(using TA: Testable[A], TR: Testable[R]): ConfigurableTest

Skips this testV.

Skips this testV.

Never comment-out or delete a testV command. Among other things, this would cause the tests and the test data to become out of sync.

Instead, if you don't want a testV to run for some reason, change it to ignoretestV, which will prevent the test from running but still keep the test data in sync.

Returns a hwtest.ConfigurableTest, like testV does, so you can switch freely between testV and ignoretestV. However, ignoretestV will ignore any such configuration.

Value parameters:
f

the function to test

name

the name to be displayed for the function being tested

param1

the name of the function's parameter

def ignoretestV[A, B, R](name: String, f: (A, B) => R, param1: String, param2: String)(validate: (A, B, R) => Unit)(using TA: Testable[A], TB: Testable[B], TR: Testable[R]): ConfigurableTest

Like ignoretestV[A,R] but for two-parameter functions.

Like ignoretestV[A,R] but for two-parameter functions.

def ignoretestV[A, B, C, R](name: String, f: (A, B, C) => R, param1: String, param2: String, param3: String)(validate: (A, B, C, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TR: Testable[R]): ConfigurableTest

Like ignoretestV[A,R] but for three-parameter functions.

Like ignoretestV[A,R] but for three-parameter functions.

def ignoretestV[A, B, C, D, R](name: String, f: (A, B, C, D) => R, param1: String, param2: String, param3: String, param4: String)(validate: (A, B, C, D, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TR: Testable[R]): ConfigurableTest

Like ignoretestV[A,R] but for four-parameter functions.

Like ignoretestV[A,R] but for four-parameter functions.

def ignoretestV[A, B, C, D, E, R](name: String, f: (A, B, C, D, E) => R, param1: String, param2: String, param3: String, param4: String, param5: String)(validate: (A, B, C, D, E, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TR: Testable[R]): ConfigurableTest

Like ignoretestV[A,R] but for five-parameter functions.

Like ignoretestV[A,R] but for five-parameter functions.

def ignoretestV[A, B, C, D, E, F, R](name: String, f: (A, B, C, D, E, F) => R, param1: String, param2: String, param3: String, param4: String, param5: String, param6: String)(validate: (A, B, C, D, E, F, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TF: Testable[F], TR: Testable[R]): ConfigurableTest

Like ignoretestV[A,R] but for six-parameter functions.

Like ignoretestV[A,R] but for six-parameter functions.

final def main(args: Array[String]): Unit

The main method to be inherited by student code.

The main method to be inherited by student code.

Prints the results of testing. (The header information was already printed during initialization.)

As part of the turn-in process, a student will typically redirect this printed output to a file (from the command line, not programmatically).

Because the method is declared as final, a student cannot override it.

def test[A, R](name: String, f: A => R, param1: String)(using TA: Testable[A], TR: Testable[R]): ConfigurableTest

Tests a one-parameter function.

Tests a one-parameter function.

For each test case, compares the result of the function on a given argument to the expected result.

If there are multiple potential results of the function that should be considered correct, use testV instead of test.

Returns a hwtest.ConfigurableTest, which allows the timeLimit and failureLimit for the test to be configured.

Value parameters:
f

the function to test

name

the name to be displayed for the function being tested

param1

the name of the function's parameter

def test[A, B, R](name: String, f: (A, B) => R, param1: String, param2: String)(using TA: Testable[A], TB: Testable[B], TR: Testable[R]): ConfigurableTest

Like test[A,R] but for two-parameter functions.

Like test[A,R] but for two-parameter functions.

def test[A, B, C, R](name: String, f: (A, B, C) => R, param1: String, param2: String, param3: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TR: Testable[R]): ConfigurableTest

Like test[A,R] but for three-parameter functions.

Like test[A,R] but for three-parameter functions.

def test[A, B, C, D, R](name: String, f: (A, B, C, D) => R, param1: String, param2: String, param3: String, param4: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TR: Testable[R]): ConfigurableTest

Like test[A,R] but for four-parameter functions.

Like test[A,R] but for four-parameter functions.

def test[A, B, C, D, E, R](name: String, f: (A, B, C, D, E) => R, param1: String, param2: String, param3: String, param4: String, param5: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TR: Testable[R]): ConfigurableTest

Like test[A,R] but for five-parameter functions.

Like test[A,R] but for five-parameter functions.

def test[A, B, C, D, E, F, R](name: String, f: (A, B, C, D, E, F) => R, param1: String, param2: String, param3: String, param4: String, param5: String, param6: String)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TF: Testable[F], TR: Testable[R]): ConfigurableTest

Like test[A,R] but for six-parameter functions.

Like test[A,R] but for six-parameter functions.

def testV[A, R](name: String, f: A => R, param1: String)(validate: (A, R) => Unit)(using TA: Testable[A], TR: Testable[R]): ConfigurableTest

Tests a one-parameter function for which more than one result could be considered correct.

Tests a one-parameter function for which more than one result could be considered correct.

For each test case, uses the given validate function to check if the result is acceptable. The validate function should return unit when the result is acceptable and throw an exception when the result is not acceptable.

Often, the most convenient way to express your validate function is using one or more asserts. (Note that when you call assert inside a testV, you are actually calling an assert from org.scalatest.Assertions rather than from scala.Predef.)

If there is a unique result of the function that should be considered correct, it is usually more convenient to use test instead of testV. On the other hand, with testV, the validate function could throw different exceptions for different kinds of failures, which allows more informative error messages. More informative error messages may or may not be desirable (from the teacher's point of view) depending on the particular pedagogical goals for each problem.

Returns a hwtest.ConfigurableTest, which allows the timeLimit and failureLimit for the test to be configured.

Value parameters:
f

the function to test

name

the name to be displayed for the function being tested

param1

the name of the function's parameter

validate

a function that takes both the argument given to f and the result on that argument, and checks that the result was valid, throwing an exception if it was not valid

def testV[A, B, R](name: String, f: (A, B) => R, param1: String, param2: String)(validate: (A, B, R) => Unit)(using TA: Testable[A], TB: Testable[B], TR: Testable[R]): ConfigurableTest

Like testV[A,R] but for two-parameter functions.

Like testV[A,R] but for two-parameter functions.

def testV[A, B, C, R](name: String, f: (A, B, C) => R, param1: String, param2: String, param3: String)(validate: (A, B, C, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TR: Testable[R]): ConfigurableTest

Like testV[A,R] but for three-parameter functions.

Like testV[A,R] but for three-parameter functions.

def testV[A, B, C, D, R](name: String, f: (A, B, C, D) => R, param1: String, param2: String, param3: String, param4: String)(validate: (A, B, C, D, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TR: Testable[R]): ConfigurableTest

Like testV[A,R] but for four-parameter functions.

Like testV[A,R] but for four-parameter functions.

def testV[A, B, C, D, E, R](name: String, f: (A, B, C, D, E) => R, param1: String, param2: String, param3: String, param4: String, param5: String)(validate: (A, B, C, D, E, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TR: Testable[R]): ConfigurableTest

Like testV[A,R] but for five-parameter functions.

Like testV[A,R] but for five-parameter functions.

def testV[A, B, C, D, E, F, R](name: String, f: (A, B, C, D, E, F) => R, param1: String, param2: String, param3: String, param4: String, param5: String, param6: String)(validate: (A, B, C, D, E, F, R) => Unit)(using TA: Testable[A], TB: Testable[B], TC: Testable[C], TD: Testable[D], TE: Testable[E], TF: Testable[F], TR: Testable[R]): ConfigurableTest

Like testV[A,R] but for six-parameter functions.

Like testV[A,R] but for six-parameter functions.

Inherited methods

def registerAction(action: => Any): Unit

Delays arbitrary code to be executed later by runActions.

Delays arbitrary code to be executed later by runActions.

The delayed code from multiple calls to registerAction will be executed in FIFO order. Because of this FIFO order, nested calls of registerAction may behave in unexpected ways, especially when recursion is involved.

Note that values produced by the delayed code are discarded, so such code should always be effectful. Contrariwise, any effectful code in the body of a normal homework should usually be wrapped a registerAction.

Value parameters:
action

arbitrary code to be executed later

Inherited from:
Actions

Concrete fields

val courseName: String