Crafting happiness with Free Software & Hardware

Test driving the development of a testing framework using itself in Guile

Guile Logo

Previously :

My last post was full of errors ! I guess I got lost in the REPL state and faced ghosts, like variables and procedures no more defined… In this post, I give you a new version of the code without errors. You also get a bonus feature : the failing tests are named in the summary.
Enjoy !

The steps followed are almost the same as the one in the previous article of this serie.
The difference lies in the way I encapsulate everything inside functions :
* gunit-test is the function responsible for running the tests.
* runner and summary are the functions under test.
* gunit-project is the function that ensure my REPL is not relying on the state of old evaluations.

(use-modules ((debugging assert) #:select (assert))
             (ice-9 exceptions))

(define (gunit-project)

  (define* (runner #:key (setup (const #f)) (teardown (const #f)))
    (let ([count (vector 0 0)]
          [failures '()])
      (lambda* (#:rest procs)
        (map
         (lambda (proc)
           (vector-set! count 0 (1+ (vector-ref count 0)))
           (guard (e
                   ((exception? e)
                    (vector-set! count 1 (1+ (vector-ref count 1)))
                    (set! failures (cons (symbol->string (procedure-name proc)) failures))))
             (dynamic-wind setup proc teardown)))
         procs)
        (values count failures))))

  (define (summary count failures)
    (format #f "~A run, ~A failed~A"
            (vector-ref count 0)
            (vector-ref count 1)
            (string-concatenate (map
                                 (lambda (failure)
                                   (format #f "\n- ~A" failure))
                                 failures))))

  
  (define (gunit-test)

    (define log "")
    (define (test-proc) (set! log (string-append log "proc ")))
    (define (test-proc-broken) (raise-exception (make-exception)))
    (define (setup-proc) (set! log (string-append log "setup ")))
    (define (teardown-proc) (set! log (string-append log "teardown ")))
    
    (define (test-template)
      ((runner #:setup setup-proc #:teardown teardown-proc) test-proc)
      (assert (string=? log "setup proc teardown ")))

    (define (test-result)
      (call-with-values (lambda () ((runner) test-proc))
        (lambda (count failures)
          (assert (string=? "1 run, 0 failed" (summary count failures))))))

    (define (test-failed-result)
      (call-with-values (lambda () ((runner) test-proc-broken))
        (lambda (count failures)
          (assert (string=? "1 run, 1 failed\n- test-proc-broken" (summary count failures))))))

    (define (test-suite)
      (call-with-values (lambda () ((runner) test-proc test-proc-broken))
        (lambda (count failures)
          (assert (string=? "2 run, 1 failed\n- test-proc-broken" (summary count failures))))))
    
    (let* ([setup (lambda () (set! log ""))]
           [run (runner #:setup setup)])
      (call-with-values (lambda ()
                          (run test-template
                               test-result
                               test-failed-result
                               test-suite))
        (lambda (count failures)
          (summary count failures)))))

  
  (gunit-test))

(gunit-project)

Thank you very much for reading this article!

Don't hesitate to give me your opinion, suggest an idea for improvement, report an error, or ask a question ! I would be so glad to discuss about the topic covered here with you ! You can reach me here.

Don't miss out on the next ones ! Either via RSS or via e-mail !

And more importantly, share this blog and tell your friends why they should read this post!

#gnu #guile #tdd #book #english

GPG: 036B 4D54 B7B4 D6C8 DA62 2746 700F 5E0C CBB2 E2D1