Testing And Benchmarking in Lisp

Here’s the translation of the Go testing and benchmarking example to Lisp (Common Lisp), formatted for Hugo:

(defpackage :testing-and-benchmarking
  (:use :cl :fiveam))

(in-package :testing-and-benchmarking)

;; We'll be testing this simple implementation of an
;; integer minimum. Typically, the code we're testing
;; would be in a source file named something like
;; `intutils.lisp`, and the test file for it would then
;; be named `intutils-test.lisp`.

(defun int-min (a b)
  (if (< a b)
      a
      b))

;; A test is created by defining a function and using the
;; `test` macro from the FiveAM testing framework.

(test int-min-basic
  (let ((ans (int-min 2 -2)))
    (is (= ans -2) "int-min(2, -2) = ~A; want -2" ans)))

;; Writing tests can be repetitive, so it's idiomatic to
;; use a table-driven style, where test inputs and
;; expected outputs are listed in a table and a single loop
;; walks over them and performs the test logic.

(test int-min-table-driven
  (let ((tests '((0 1 0)
                 (1 0 0)
                 (2 -2 -2)
                 (0 -1 -1)
                 (-1 0 -1))))
    (loop for (a b want) in tests
          for testname = (format nil "~A,~A" a b)
          do (is (= (int-min a b) want)
                 "~A: got ~A, want ~A"
                 testname (int-min a b) want))))

;; Benchmark tests in Common Lisp are typically implemented
;; using a timing function and running the code multiple times.
;; Here's a simple benchmark implementation:

(defun benchmark-int-min (n)
  (let ((start-time (get-internal-real-time)))
    (dotimes (i n)
      (int-min 1 2))
    (let ((end-time (get-internal-real-time)))
      (float (/ (- end-time start-time) internal-time-units-per-second)))))

;; To run the tests and benchmark:

(defun run-tests-and-benchmark ()
  (run! 'int-min-basic)
  (run! 'int-min-table-driven)
  (format t "Benchmark: ~A seconds~%" (benchmark-int-min 1000000)))

This Lisp code demonstrates testing and benchmarking concepts similar to the original Go example. Here’s a brief explanation of the changes:

  1. We use the FiveAM testing framework, which is a popular choice for Common Lisp.
  2. Tests are defined using the test macro instead of functions starting with “Test”.
  3. The is macro is used for assertions, similar to t.Error in Go.
  4. Table-driven tests are implemented using a list of lists and a loop.
  5. Benchmarking is implemented manually using get-internal-real-time to measure execution time.
  6. The run-tests-and-benchmark function combines running tests and the benchmark.

To run the tests and benchmark, you would typically load this file into your Lisp environment and call (run-tests-and-benchmark). The output would show the test results and the benchmark timing.

Note that Common Lisp doesn’t have a built-in testing or benchmarking tool like Go’s go test command. Instead, we use external libraries like FiveAM for testing, and implement benchmarking manually or use other profiling tools available in the Lisp environment.