Creating Test Data from Input Files

There are cases in which you wish to test a large or complex data structure, and it is difficult or just irritating to supply it directly in Scheme code. In these cases, it may be simpler to read in an input file.

For example, this could be the module code for a structure provided to students in a teachpack, where the value consumed by functions is a list of nameinfo structs:

#lang scheme
(require lang/prim)

  ; Define a structure for information about a baby name.
  (define-struct nameinfo (name decade rank pct gender) #:transparent)
  ; A nameinfo structure is (make-nameinfo n d r p g) where:
  ; * n is a string (the name)
  ; * d is a nat (the decade the stats apply to)
  ; * r is a nat in [1,1000] (the name's popularity ranking, 1 being the most popular)
  ; * p is a num in (0, 1) (the percentage of all names for that decade)
  ; * g is one of {'Male, 'Female} (the gender of the babies named with this name)
  
  ; get-token: (listof char) -> string
  ; Produce the next token from loc.
  (define (get-token loc)
    (cond
      [(empty? loc) empty]
      [(char-whitespace? (first loc)) empty]
      [else (cons (first loc) (get-token (rest loc)))]))
  
  ; split-loc: (listof char) -> (listof string)
  ; Split a list of characters into a list of tokens separated by
  ; whitespace.
  ; Example:  (split-loc (string->list "one two three")) ==> 
  ;              (list "one" "two" "three")
  (define (split-loc loc)
    (cond
      [(empty? loc) empty]
      [(char-whitespace? (first loc)) (split-loc (rest loc))]
      [else (cons (list->string (get-token loc)) (split-loc (after-token loc)))]))
  
  ; after-token: (listof char) -> (listof char)
  ; Strip the first token from loc; return the rest of the list.
  (define (after-token loc)
    (cond
      [(empty? loc) empty]
      [(char-whitespace? (first loc)) loc]
      [else (after-token (rest loc))]))
  
  ; parse-line: string (listof name) -> name
  ; Consume one line of text, parse it, and produce the corresponding
  ; name structure.
  (define (parse-line line)
    (local [(define fields (split-loc (string->list line)))]
      (make-nameinfo (first fields)
                     (string->number (second fields))
                     (string->number (third fields))
                     (string->number (fourth fields))
                     (string->symbol (fifth fields)))
      ))
  
  ; lines: file -> (listof name)
  ; Read a file, parsing each line to build a list of name structures.
  (define (lines in)
    (local [(define (lines lst)
              (local [(define line (read-line in))]
                (cond [(eof-object? line) lst]
                      [else (lines (cons (parse-line line) lst))])))
            ]
      (lines empty)))
  
  (define (grab-list-for-course-account-testing)
    (lines (current-input-port)))

  ; provide as few new names as possible
  ; Use provide-primitive for this to ensure beginning student language varieties behave
  ; and allow a call to a function with zero parameters.
  (provide-primitive grab-list-for-course-account-testing)

  ; but still be sure to provide everything that was originally provided to students.
  (provide make-nameinfo 
           nameinfo-name nameinfo-decade nameinfo-rank nameinfo-pct nameinfo-gender 
           nameinfo?)

Then, you could specify an input file containing lines such as

Amanda 1980 3 2.0042 Female
Robert 1940 2 5.0016 Male
Carleton 1950 900 0.0029 Male
Titus 1960 900 0.0031 Male
Dayton 1940 900 0.0031 Male

without needing to worry about explicitly creating a list with all of the make-struct calls properly in place.

BitterSuite pointers

The module code should be in a module in the provided directory, and then it needs to be part of a declared module list before a loadcode is reached, as with all other modules.

The input data would then be in the hierarchy as a file named input. It will be re-read automatically for each test it's visible to, in case the same input data needs to be used for multiple tests (of course, other input files can exist elsewhere in the testing hierarchy).

The tests can then just make use of (grab-list-for-course-account-testing) as a value to be consumed for a test, instead of having to write a list of structures inline.

Topic revision: r1 - 2010-02-09 - TerryVaskor
 
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback