test/tests.lisp @ fb0afda02c8a

Add fuzz tests and fix bugs
author Steve Losh <steve@stevelosh.com>
date Mon, 24 Dec 2018 19:12:09 -0500
parents 1320162a9ed0
children 1e155f658715
(in-package :trivial-ppm/test)


;;;; Utils --------------------------------------------------------------------
(defmacro define-test (name &body body)
  `(test ,(symb 'test- name)
    (let ((*package* ,*package*)
          (r #(255 0 0))
          (g #(0 255 0))
          (b #(0 0 255))
          (k #(0 0 0))
          (w #(255 255 255)))
      (declare (ignorable r g b k w))
      ,@body)))

(defun make-image-array (initial-data)
  (make-array (list (length (elt initial-data 0))
                    (length initial-data))
              :initial-contents (transpose initial-data)))

(defmacro check (form expected-data expected-format expected-bit-depth)
  (with-gensyms (data format bit-depth)
    `(multiple-value-bind (,data ,format ,bit-depth) ,form
       (is (equalp (make-image-array ,expected-data) ,data))
       (is (eql ,expected-format ,format))
       (is (= ,expected-bit-depth ,bit-depth)))))

(defun run-tests ()
  (1am:run))


;;;; Tests --------------------------------------------------------------------
(define-test 1x1-black-ascii-pbm
  (check (trivial-ppm:read-from-file "test/data/1x1-black.ascii.pbm")
         '((0))
         :pbm
         1))

(define-test 1x1-black-ascii-pgm
  (check (trivial-ppm:read-from-file "test/data/1x1-black.ascii.pgm")
         '((0))
         :pgm
         255))

(define-test 1x1-black-ascii-ppm
  (check (trivial-ppm:read-from-file "test/data/1x1-black.ascii.ppm")
         `((,k))
         :ppm
         255))

(define-test 4x3-rgb.ascii-ppm
  (check (trivial-ppm:read-from-file "test/data/4x3-rgb.ascii.ppm")
         `((,r ,r ,r ,r)
           (,g ,g ,g ,g)
           (,b ,b ,b ,b))
         :ppm
         255))


(define-test 1x1-black-binary-pbm
  (check (trivial-ppm:read-from-file "test/data/1x1-black.binary.pbm")
         '((0))
         :pbm
         1))

(define-test 1x1-black-binary-pgm
  (check (trivial-ppm:read-from-file "test/data/1x1-black.binary.pgm")
         '((0))
         :pgm
         255))

(define-test 1x1-black-binary-ppm
  (check (trivial-ppm:read-from-file "test/data/1x1-black.binary.ppm")
         `((,k))
         :ppm
         255))

(define-test 4x3-rgb.binary-ppm
  (check (trivial-ppm:read-from-file "test/data/4x3-rgb.binary.ppm")
         `((,r ,r ,r ,r)
           (,g ,g ,g ,g)
           (,b ,b ,b ,b))
         :ppm
         255))


;;;; Fuzzer -------------------------------------------------------------------
(defparameter *fuzz-test-count* 500)


(defun random-bit ()
  (random 2))

(defun random-gray ()
  (random 256))

(defun random-color ()
  (make-array 3 :initial-contents (list (random 256)
                                        (random 256)
                                        (random 256))))

(defun random-format ()
  (ecase (random 3)
    (0 :pbm)
    (1 :pgm)
    (2 :ppm)))

(defun make-random-array ()
  (let* ((width (1+ (random 50)))
         (height (1+ (random 50)))
         (format (random-format))
         (data (make-array (list width height))))
    (dotimes (x width)
      (dotimes (y height)
        (setf (aref data x y)
              (ecase format
                (:pbm (random-bit))
                (:pgm (random-gray))
                (:ppm (random-color))))))
    (values data format)))


(define-test fuzz-ascii
  (dotimes (i *fuzz-test-count*)
    (multiple-value-bind (original original-format) (make-random-array)
      (write-to-file "test/data/fuzz.ascii" original
                     :if-exists :supersede
                     :format original-format
                     :encoding :ascii)
      (multiple-value-bind (new new-format)
          (read-from-file "test/data/fuzz.ascii")
        (is (eql original-format new-format))
        (is (equalp original new))))))

(define-test fuzz-binary
  (dotimes (i *fuzz-test-count*)
    (multiple-value-bind (original original-format) (make-random-array)
      (write-to-file "test/data/fuzz.binary" original
                     :if-exists :supersede
                     :format original-format
                     :encoding :binary)
      (multiple-value-bind (new new-format)
          (read-from-file "test/data/fuzz.binary")
        (is (eql original-format new-format))
        (is (equalp original new))))))

(define-test fuzz-convert
  (dotimes (i *fuzz-test-count*)
    (multiple-value-bind (original original-format) (make-random-array)
      (write-to-file "test/data/fuzz.convert.in" original
                     :if-exists :supersede
                     :format original-format
                     :encoding :ascii)
      (uiop:run-program (list "convert" "-format" (ecase original-format
                                                    (:ppm "ppm")
                                                    (:pgm "pgm")
                                                    (:pbm "pbm"))
                              "test/data/fuzz.convert.in"
                              "test/data/fuzz.convert.out"))
      (multiple-value-bind (new new-format)
          (read-from-file "test/data/fuzz.convert.out")
        (is (eql original-format new-format))
        (is (equalp original new))))))