DEV Community

Red Nose Hacker
Red Nose Hacker

Posted on • Originally published at rednosehacker.com

Code Kata with SRFI-64

Alt Text

I got into the habit of doing about 30 minutes of Code Kata every morning. I practice these Kata following the test-driven development (TDD) approach.

Guile distribution includes the SRFI-64 module: a unit testing framework. In this article, I explain how to use it and how to configure it in order to practice code kata.

Basic usage

The SRFI-64 module has a default configuration. Let me show you what it looks like here.

Below is a Guile program that creates a test suite with only one test (doomed to fail) :

(use-modules (srfi srfi-64))
(test-begin "demo")
(test "Hookup" #f #t)
(test-end "demo")

Explanation of the program :

  • line 1 : load the module named (srfi srfi-64).
  • line 2 : indicate the beginning of the test group demo.
  • line 3: define a test, named “Hookup”, with the procedure test-equal whose first parameter is the result expected by the test (here, #t ) and the second parameter is the tested expression (here, #t).
  • line 4: indicate the end of the demo test group.

It is relatively simple, there are few frills.

This is the result of the execution of this suite if you save it in a /tmp/tests.scm file and execute the :

$ guile --no-auto-compile test.scm 
%%%% Starting test demo (Writing full log to "demo.log")
/tmp/test.scm:3: FAIL Hookup
# of unexpected failures 1

The console displays only a summary of the result: line 3 shows an unexpected failure from the “Hookup” test. It's very brief but it gives a quick idea of the state of the program.

The complete detail is in the /tmp/demo.log file:

cat demo.log
%%%% Starting test demo
Group begin: demo
Test begin:
  test-name: "Hookup"
  source-file: "/tmp/test.scm".
  source-line: 3
  source-form: (test-equal "Hookup" #f #t)
Test end:
  result-kind: fail
  actual-value: #t
  expected-value: #f
Group end: demo
# of unexpected failures 1

This report gives me two pieces of information that I use a lot : actual-value and expected-value.

This default configuration, although ready to use, has the disadvantage of providing informations in two places : in the console (list of tests and their states) and in a file (test results). Not very practical...

Fortunately, the test framework is easily configurable. In the following article, I present you my configuration dedicated to code kata.

Configuration for Code Kata

The idea here is to configure the SRFI-64 module to display all the information I need directly in the console (and I don't need to report in a file). I want to see at a glance :

  • the list of executed tests ;
  • the status of each of these tests;
  • for failed tests, display the expected value and the value obtained.

According to the SRFI-64 reference, the objects that maintain the statistics on the test suite (total number of tests, number of successful, failed, passed, ...) are the test-runners.

Thanks to the section Writing a new test-runner and some examples gleaned from the web, you should be able to create the test-runner that suits you!

Here is the one I made for myself (based on the work of Mathieu Lirzin and Alex Sassmannshausen) :

;;;; kata-driver.scm - Guile test driver for code kata practice

(define script-version "2020-10-04") ;UTC

;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
;;; Copyright © 2019 Alex Sassmannshausen <alex@pompo.co>
;;; Copyright © 2019 Jérémy Korwin-Zmijowski <jeremy@korwin-zmijowski.fr>
;;;
;;; This program is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;;; Commentary:
;;;
;;; This script provides a Guile test driver using the SRFI-64 Scheme API for
;;; test suites.  SRFI-64 is distributed with Guile since version 2.0.9.
;;;
;;; This script is a very cleaned up version of the Alex Sassmannshausen 's 
;;; version. The changes make it suitable for use in a code kata practice.
;;;
;;;; Code:

(define-module (runner-kata)
  #:export (test-runner-kata))

(use-modules (srfi srfi-64)
             (ice-9 pretty-print)
             (srfi srfi-26))

(define* (test-display field value  #:optional (port (current-output-port))
                       #:key pretty?)
  "Display 'FIELD: VALUE\n' on PORT."
  (if pretty?
      (begin
        (format port "~A:~%" field)
        (pretty-print value port #:per-line-prefix "+ "))
      (format port "~A: ~S~%" field value)))

(define* (result->string symbol)
  "Return SYMBOL as an upper case string.  Use colors when COLORIZE is #t."
  (let ((result (string-upcase (symbol->string symbol))))
    (string-append (case symbol
                     ((pass)       "")  ;green
                     ((xfail)      "")  ;light green
                     ((skip)       "")  ;blue
                     ((fail xpass) "")  ;red
                     ((error)      "")) ;magenta
                   result
                   "")))

(define* (test-runner-kata)

  (define (test-on-test-end-kata runner)
    (let* ((results (test-result-alist runner))
           (result? (cut assq <> results))
           (result  (cut assq-ref results <>)))
      (if (equal? 'fail (result 'result-kind))
      (begin
        (newline)
        (format #t "~a ~A~%"
            (result->string (result 'result-kind))
            (result 'test-name))
        (when (result? 'expected-value)
              (test-display "expected-value" (result 'expected-value)))
        (when (result? 'expected-error)
              (test-display "expected-error" (result 'expected-error) #:pretty? #t))
        (when (result? 'actual-value)
              (test-display "actual-value" (result 'actual-value)))
        (newline))
      (begin
        (format #t "~a ~A~%"
            (result->string (result 'result-kind))
            (result 'test-name))))))

  (let ((runner (test-runner-null)))
    (test-runner-on-test-end! runner test-on-test-end-kata)
    runner))

I created a git repository which contains a project template for a code kata. Just follow the README instructions and code !

Here is an example of feedback from your terminal while the watch.sh script is running :

FAIL Hookup
expected-value: #f
actual-value: #t

No more need to look at the content of the report (which is no longer generated) to find out what the problem is. So I can execute this script in a dedicated emacs buffer, every time I save, and act according to the test results.

Now it's up to you!

Choose a code kata exercise, work on it for about 30 minutes, share the link to your code and your tests in comments below!

Then, I give you my 2 cents for your next session!

Thank you so much for reading this article!

Don't hesitate to give me your opinion or ask a question !
To do so, please leave a comment below or contact me.

And most importantly, share the blog and tell your friends it's the best blog in the history of Free Software! No shit!

Top comments (0)