2022-10-31 15:23:26 +01:00
|
|
|
;;;; SPDX-License-Identifier AGPL-3.0-or-later
|
|
|
|
|
|
|
|
;;;; file-system.lisp File system implementation tests
|
|
|
|
;;;; Copyright (C) 2022 Alejandro "HiPhish" Sanchez
|
|
|
|
;;;;
|
|
|
|
;;;; This file is part of CL-HSSG.
|
|
|
|
;;;;
|
|
|
|
;;;; CL-HSSG is free software: you can redistribute it and/or modify it under
|
|
|
|
;;;; the terms of the GNU Affero General Public License as published by the
|
|
|
|
;;;; Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
;;;; option) any later version.
|
|
|
|
;;;;
|
|
|
|
;;;; CL-HSSG 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 Affero General Public License for
|
|
|
|
;;;; more details.
|
|
|
|
;;;;
|
|
|
|
;;;; You should have received a copy of the GNU Affero General Public License
|
|
|
|
;;;; along with CL-HSSG If not, see <https://www.gnu.org/licenses/>
|
|
|
|
(defpackage #:hssg/test/filesystem
|
|
|
|
(:documentation "File system implementation tests")
|
|
|
|
(:use #:cl)
|
|
|
|
(:import-from #:clunit
|
|
|
|
#:defsuite #:deffixture #:deftest #:assert-true #:assert-eql #:assert-equal)
|
|
|
|
(:import-from #:hssg/test-mocking #:with-mock)
|
|
|
|
(:export test-all all-tests hssg))
|
|
|
|
(in-package #:hssg/test/filesystem)
|
|
|
|
|
|
|
|
(defsuite hssg.filesystem (hssg/test:hssg))
|
|
|
|
(defsuite hssg.filesystem.get-path (hssg.filesystem))
|
|
|
|
(defsuite hssg.filesystem.write (hssg.filesystem))
|
|
|
|
|
|
|
|
|
|
|
|
;;; --- FAKES -----------------------------------------------------------------
|
|
|
|
(defclass dummy-instruction (hssg:file-system-instruction)
|
|
|
|
((on-write :initarg :on-write :initform (lambda (self) self)
|
|
|
|
:documentation "Callback to execute when the instruction is written."))
|
|
|
|
(:documentation "A fake file system instruction"))
|
|
|
|
|
|
|
|
(defclass dummy-filesystem (hssg:file-system)
|
|
|
|
()
|
|
|
|
(:documentation "A fake file system"))
|
|
|
|
|
|
|
|
(defmethod hssg:write-to-filesystem ((instruction dummy-instruction)
|
|
|
|
file-system)
|
|
|
|
(funcall (slot-value instruction 'on-write) instruction))
|
|
|
|
|
|
|
|
(defmethod hssg:write-to-filesystem (instruction
|
|
|
|
(file-system dummy-filesystem))
|
|
|
|
nil)
|
|
|
|
|
|
|
|
|
|
|
|
;;; --- FILE SYSTEM TESTS -----------------------------------------------------
|
|
|
|
(deffixture hssg.filesystem.get-path (@body)
|
|
|
|
"Creates a dummy base file system"
|
|
|
|
(let* ((base
|
|
|
|
(make-instance 'hssg:base-file-system :directory #p"base"))
|
|
|
|
(overlay1
|
|
|
|
(make-instance
|
|
|
|
'hssg:overlay-file-system :directory #p"overlay-1" :parent base))
|
|
|
|
(overlay2
|
|
|
|
(make-instance
|
|
|
|
'hssg:overlay-file-system :directory #p"overlay-2" :parent overlay1)))
|
|
|
|
(declare (ignorable base overlay1 overlay2))
|
|
|
|
@body))
|
|
|
|
|
|
|
|
(deftest base-file-system-path (hssg.filesystem.get-path)
|
|
|
|
"The path of a base file system is its slot value."
|
|
|
|
(let ((path (hssg.filesystem::file-system-path base)))
|
|
|
|
(assert-equal #p"base/" path)))
|
|
|
|
|
|
|
|
(deftest single-overlay-file-system-path (hssg.filesystem.get-path)
|
|
|
|
"The path of an overlay file system is the merge of its own and the parent's path."
|
|
|
|
(let ((path (hssg.filesystem::file-system-path overlay1)))
|
|
|
|
(assert-equal #p"base/overlay-1/" path)))
|
|
|
|
|
|
|
|
(deftest multi-overlay-file-system-path (hssg.filesystem.get-path)
|
|
|
|
"An overlay on top of an overlay merges all its parent file systems"
|
|
|
|
(let ((path (hssg.filesystem::file-system-path overlay2)))
|
|
|
|
(assert-equal #p"base/overlay-1/overlay-2/" path)))
|
|
|
|
|
|
|
|
|
|
|
|
;;; ---------------------------------------------------------------------------
|
|
|
|
(deffixture hssg.filesystem.write (@body)
|
|
|
|
"Creates a counter variable to keep track of mock calls."
|
|
|
|
(let ((counter 0))
|
|
|
|
(declare (ignorable counter))
|
|
|
|
@body))
|
|
|
|
|
|
|
|
(deftest write/copy-file/base-filesystem (hssg.filesystem.write)
|
|
|
|
"Copying a file verbatim to a base file system"
|
|
|
|
(let ((file-system (make-instance 'hssg:base-file-system :directory #p"output"))
|
|
|
|
(instruction (make-instance 'hssg:copy-file :path #p"css/main.css" :base-path #p"content")))
|
|
|
|
(with-mock ((hssg.filesystem::copy-file
|
|
|
|
(lambda (original copy)
|
|
|
|
(incf counter)
|
|
|
|
(assert-equal #p"content/css/main.css" original)
|
|
|
|
(assert-equal #p"output/css/main.css" copy))))
|
|
|
|
(hssg:write-to-filesystem instruction file-system)
|
|
|
|
(assert-eql 1 counter))))
|
|
|
|
|
|
|
|
(deftest write/write-string/base-filesystem (hssg.filesystem.write)
|
|
|
|
"Writing a string to a file in a base file system"
|
|
|
|
(let ((file-system (make-instance 'hssg:base-file-system
|
|
|
|
:directory #p"output"))
|
|
|
|
(instruction (make-instance 'hssg:write-string-contents
|
|
|
|
:path #p"css/main.css"
|
|
|
|
:contents "Hello world")))
|
|
|
|
(with-mock ((hssg.filesystem::write-string-to-file
|
|
|
|
(lambda (contents path)
|
|
|
|
(incf counter)
|
|
|
|
(assert-equal "Hello world" contents)
|
|
|
|
(assert-equal #p"output/css/main.css" path))))
|
|
|
|
(hssg:write-to-filesystem instruction file-system)
|
|
|
|
(assert-eql 1 counter))))
|
|
|
|
|
|
|
|
(deftest write/write-string/overlay-filesystem (hssg.filesystem.write)
|
|
|
|
"Writing a string to an overlay file system resolved the path correctly."
|
|
|
|
(let ((file-system (make-instance 'hssg:overlay-file-system
|
|
|
|
:directory #p"blog"
|
|
|
|
:parent (make-instance 'hssg:base-file-system
|
|
|
|
:directory #p"output")))
|
|
|
|
(instruction (make-instance 'hssg:write-string-contents
|
|
|
|
:path #p"css/main.css"
|
|
|
|
:contents "Hello world")))
|
|
|
|
(with-mock ((hssg.filesystem::write-string-to-file
|
|
|
|
(lambda (contents path)
|
|
|
|
(incf counter)
|
|
|
|
(assert-equal "Hello world" contents)
|
|
|
|
(assert-equal #p"output/blog/css/main.css" path))))
|
|
|
|
(hssg:write-to-filesystem instruction file-system)
|
|
|
|
(assert-eql 1 counter))))
|
|
|
|
|
|
|
|
(deftest write/copy-directory/base-filesystem (hssg.filesystem.write)
|
|
|
|
"Copying a directory recursively to a base file system copies the entire directory"
|
|
|
|
(let ((file-system (make-instance 'hssg:base-file-system :directory #p"output"))
|
|
|
|
(instruction (make-instance 'hssg:copy-directory :path #p"assets/images" :base-path #p"content"))
|
|
|
|
(counter 0))
|
|
|
|
(with-mock ((hssg.filesystem::copy-directory
|
|
|
|
(lambda (original copy)
|
|
|
|
(incf counter)
|
|
|
|
(assert-equal #p"content/assets/images/" original)
|
|
|
|
(assert-equal #p"output/assets/images/" copy)
|
|
|
|
nil)))
|
|
|
|
(hssg:write-to-filesystem instruction file-system)
|
|
|
|
(assert-eql 1 counter))))
|
|
|
|
|
|
|
|
(deftest write/compound-instruction/base-filesystem (hssg.filesystem.write)
|
|
|
|
"Writing a compound instruction writes all its children in order."
|
|
|
|
(let* ((file-system (make-instance 'dummy-filesystem))
|
|
|
|
(written (list))
|
|
|
|
(dummy1 (make-instance 'dummy-instruction
|
|
|
|
:on-write (lambda (self) (push self written))))
|
|
|
|
(dummy2 (make-instance 'dummy-instruction
|
|
|
|
:on-write (lambda (self) (push self written))))
|
|
|
|
(instruction (make-instance 'hssg:compound-instruction
|
|
|
|
:instructions (list dummy1 dummy2))))
|
|
|
|
(hssg:write-to-filesystem instruction file-system)
|
|
|
|
(assert-eql 2 (length written))
|
|
|
|
(loop for instruction in written
|
|
|
|
for dummy in (list dummy2 dummy1) ; Reverse order because of push!
|
|
|
|
do (assert-true (eq dummy instruction)))))
|
2022-11-05 13:09:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
;;; --- HELPER FUNCTIONS ------------------------------------------------------
|
|
|
|
;;; Here we have to actually access the file system, so we will be working with
|
|
|
|
;;; temporary files
|
|
|
|
|
|
|
|
(defsuite hssg.filesystem.helper (hssg.filesystem))
|
|
|
|
(defsuite hssg.filesystem.helper.copy-directory (hssg.filesystem.helper))
|
|
|
|
|
|
|
|
|
|
|
|
(defun file-equal-p (p1 p2)
|
|
|
|
"Whether the contents of two files are the same."
|
|
|
|
(declare (type pathname p1 p2))
|
|
|
|
(let ((s1 (uiop/stream:read-file-string p1))
|
|
|
|
(s2 (uiop/stream:read-file-string p2)))
|
|
|
|
(string= s1 s2)))
|
|
|
|
|
|
|
|
(deffixture hssg.filesystem.helper.copy-directory (@body)
|
|
|
|
(let ((original (uiop/pathname:ensure-directory-pathname
|
|
|
|
#p"test/hssg/sample-files/directory-tree/"))
|
|
|
|
(copy (uiop/pathname:ensure-directory-pathname
|
|
|
|
#p"test/hssg/sample-files/copy-tree/")))
|
|
|
|
(declare (ignorable original copy))
|
|
|
|
(unwind-protect
|
|
|
|
(progn
|
|
|
|
@body)
|
|
|
|
(uiop/filesystem:delete-directory-tree
|
|
|
|
copy :if-does-not-exist :ignore :validate t))))
|
|
|
|
|
|
|
|
(deftest copy-directory (hssg.filesystem.helper.copy-directory)
|
|
|
|
(hssg.filesystem::copy-directory original copy)
|
|
|
|
(dolist (p '(#p"a.txt" #p"b.txt" #p"x/c.txt" #p"x/y/d.txt" #p"x/y/e.txt"))
|
|
|
|
(assert-true (file-equal-p
|
|
|
|
(fad:merge-pathnames-as-file original p)
|
|
|
|
(fad:merge-pathnames-as-file copy p)))))
|