gash/tests/unit/eval.scm

461 lines
11 KiB
Scheme
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; Gash -- Guile As SHell
;;; Copyright © 2018, 2019, 2021 Timothy Sample <samplet@ngyro.com>
;;;
;;; This file is part of Gash.
;;;
;;; Gash 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.
;;;
;;; Gash 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 Gash. If not, see <http://www.gnu.org/licenses/>.
(define-module (test-eval)
#:use-module (gash environment)
#:use-module (gash eval)
#:use-module (ice-9 match)
#:use-module (srfi srfi-64)
#:use-module (tests unit automake))
;;; Commentary:
;;;
;;; Tests for the eval module.
;;;
;;; Code:
(test-begin "eval")
;;; Basic parameter references.
;;;
;;; FIXME: Test "nounset" ("set -u").
(test-equal "Resolves parameters"
'("foo")
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref> "x")))))
(test-equal "Splits parameter results"
'("foo" "bar")
(with-variables '(("x" . "foo bar"))
(lambda ()
(eval-word '(<sh-ref> "x")))))
(test-equal "Resolves quoted parameters"
'("foo")
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-quote> (<sh-ref> "x"))))))
(test-equal "Ignores spaces in quoted parameters"
'("foo bar")
(with-variables '(("x" . "foo bar"))
(lambda ()
(eval-word '(<sh-quote> (<sh-ref> "x"))))))
(test-equal "Treats empty variables as nothing"
'()
(with-variables '(("x" . ""))
(lambda ()
(eval-word '(<sh-ref> "x")))))
(test-equal "Treats unset variables as nothing"
'()
(with-variables '()
(lambda ()
(eval-word '(<sh-ref> "x")))))
(test-equal "Preserves empty variables when quoted"
'("")
(with-variables '(("x" . ""))
(lambda ()
(eval-word '(<sh-quote> (<sh-ref> "x"))))))
(test-equal "Preserves unset variables when quoted"
'("")
(with-variables '()
(lambda ()
(eval-word '(<sh-quote> (<sh-ref> "x"))))))
;;; Parameter operations.
;;; or
(test-equal "Handles 'or' when parameter is set"
'("foo")
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref-or> "x" "bar")))))
(test-equal "Handles 'or' when parameter is set and empty"
'()
(with-variables '(("x" . ""))
(lambda ()
(eval-word '(<sh-ref-or> "x" "bar")))))
(test-equal "Handles 'or' when parameter is unset"
'("bar")
(with-variables '()
(lambda ()
(eval-word '(<sh-ref-or> "x" "bar")))))
(test-equal "Handles 'or' fall-through without default"
'()
(with-variables '()
(lambda ()
(eval-word '(<sh-ref-or> "x" #f)))))
;;; or*
(test-equal "Handles 'or*' when parameter is set"
'("foo")
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref-or*> "x" "bar")))))
(test-equal "Handles 'or*' when parameter is set and empty"
'("bar")
(with-variables '(("x" . ""))
(lambda ()
(eval-word '(<sh-ref-or*> "x" "bar")))))
(test-equal "Handles 'or*' when parameter is unset"
'("bar")
(with-variables '()
(lambda ()
(eval-word '(<sh-ref-or*> "x" "bar")))))
(test-equal "Handles 'or*' fall-through without default"
'()
(with-variables '()
(lambda ()
(eval-word '(<sh-ref-or*> "x" #f)))))
;;; or!
(test-equal "Handles 'or!' when parameter is set"
'(("foo") "foo")
(with-variables '(("x" . "foo"))
(lambda ()
(list (eval-word '(<sh-ref-or!> "x" "bar"))
(getvar "x")))))
(test-equal "Handles 'or!' when parameter is set and empty"
'(() "")
(with-variables '(("x" . ""))
(lambda ()
(list (eval-word '(<sh-ref-or!> "x" "bar"))
(getvar "x")))))
(test-equal "Handles 'or!' when parameter is unset"
'(("bar") "bar")
(with-variables '()
(lambda ()
(list (eval-word '(<sh-ref-or!> "x" "bar"))
(getvar "x")))))
(test-equal "Handles 'or!' fall-through without default"
'(() "")
(with-variables '()
(lambda ()
(list (eval-word '(<sh-ref-or!> "x" #f))
(getvar "x")))))
;;; or!*
(test-equal "Handles 'or!*' when parameter is set"
'(("foo") "foo")
(with-variables '(("x" . "foo"))
(lambda ()
(list (eval-word '(<sh-ref-or!*> "x" "bar"))
(getvar "x")))))
(test-equal "Handles 'or!*' when parameter is set and empty"
'(("bar") "bar")
(with-variables '(("x" . ""))
(lambda ()
(list (eval-word '(<sh-ref-or!*> "x" "bar"))
(getvar "x")))))
(test-equal "Handles 'or!*' when parameter is unset"
'(("bar") "bar")
(with-variables '()
(lambda ()
(list (eval-word '(<sh-ref-or!*> "x" "bar"))
(getvar "x")))))
(test-equal "Handles 'or!*' fall-through without default"
'(() "")
(with-variables '()
(lambda ()
(list (eval-word '(<sh-ref-or!*> "x" #f))
(getvar "x")))))
(test-equal "Does not split fields on assignment"
'(("foo" "bar") "foo bar")
(with-variables '(("y" . "foo bar"))
(lambda ()
(list (eval-word '(<sh-ref-or!*> "x" (<sh-ref> "y")))
(getvar "x")))))
;;; FIXME: Test 'assert'.
;;; and
(test-equal "Handles 'and' when parameter is set"
'("bar")
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref-and> "x" "bar")))))
(test-equal "Handles 'and' when parameter is set and empty"
'("bar")
(with-variables '(("x" . ""))
(lambda ()
(eval-word '(<sh-ref-and> "x" "bar")))))
(test-equal "Handles 'and' when parameter is unset"
'()
(with-variables '()
(lambda ()
(eval-word '(<sh-ref-and> "x" "bar")))))
(test-equal "Handles 'and' fall-through without default"
'()
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref-and> "x" #f)))))
;;; and*
(test-equal "Handles 'and*' when parameter is set"
'("bar")
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref-and*> "x" "bar")))))
(test-equal "Handles 'and*' when parameter is set and empty"
'()
(with-variables '(("x" . ""))
(lambda ()
(eval-word '(<sh-ref-and*> "x" "bar")))))
(test-equal "Handles 'and*' when parameter is unset"
'()
(with-variables '()
(lambda ()
(eval-word '(<sh-ref-and*> "x" "bar")))))
(test-equal "Handles 'and*' fall-through without default"
'()
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref-and*> "x" #f)))))
;;; length
(test-equal "Handles 'length' when parameter is set"
'("3")
(with-variables '(("x" . "foo"))
(lambda ()
(eval-word '(<sh-ref-length> "x")))))
(test-equal "Handles 'length' when parameter is unset"
'("0")
(with-variables '()
(lambda ()
(eval-word '(<sh-ref-length> "x")))))
;;; Command substition.
(test-equal "Resolves commands"
'("foo")
(eval-word '(<sh-cmd-sub> (<sh-exec> "echo" "foo"))))
(test-equal "Splits command results"
'("foo" "bar")
(eval-word '(<sh-cmd-sub> (<sh-exec> "echo" "foo bar"))))
(test-equal "Resolves quoted commands"
'("foo")
(eval-word '(<sh-quote> (<sh-cmd-sub> (<sh-exec> "echo" "foo")))))
(test-equal "Ignores spaces in quoted commands"
'("foo bar")
(eval-word '(<sh-quote> (<sh-cmd-sub> (<sh-exec> "echo" "foo bar")))))
;;; Arithmetic expansion.
(test-equal "Evaluates arithmetic constant (decimal)"
'("42")
(eval-word '(<sh-arithmetic> "42")))
(test-equal "Evaluates arithmetic constant (octal)"
'("34")
(eval-word '(<sh-arithmetic> "042")))
(test-equal "Evaluates arithmetic constant (hexadecimal)"
'("66")
(eval-word '(<sh-arithmetic> "0x42")))
;;; Binary (and tertiary) operators
(test-equal "Evaluates arithmetic addition"
'("6")
(eval-word '(<sh-arithmetic> "2 + 4")))
(test-equal "Evaluates arithmetic subtraction"
'("2")
(eval-word '(<sh-arithmetic> "4 - 2")))
(test-equal "Evaluates arithmetic multiplication"
'("12")
(eval-word '(<sh-arithmetic> "3 * 4")))
(test-equal "Evaluates arithmetic division"
'("6")
(eval-word '(<sh-arithmetic> "19 / 3")))
(test-equal "Evaluates arithmetic modulo"
'("2")
(eval-word '(<sh-arithmetic> "32 % 3")))
(test-equal "Evaluates arithmetic left shift"
'("12")
(eval-word '(<sh-arithmetic> "3 << 2")))
(test-equal "Evaluates arithmetic right shift"
'("3")
(eval-word '(<sh-arithmetic> "15 >> 2")))
(test-equal "Evaluates arithmetic greater than"
'("1")
(eval-word '(<sh-arithmetic> "5 > 3")))
(test-equal "Evaluates arithmetic greater than or equal to"
'("0")
(eval-word '(<sh-arithmetic> "3 >= 5")))
(test-equal "Evaluates arithmetic less than"
'("0")
(eval-word '(<sh-arithmetic> "5 < 3")))
(test-equal "Evaluates arithmetic less than or equal to"
'("1")
(eval-word '(<sh-arithmetic> "3 <= 5")))
(test-equal "Evaluates arithmetic equals"
'("0")
(eval-word '(<sh-arithmetic> "0 == 1")))
(test-equal "Evaluates arithmetic not equals"
'("1")
(eval-word '(<sh-arithmetic> "0 != 1")))
(test-equal "Evaluates arithmetic bitwise and"
'("4")
(eval-word '(<sh-arithmetic> "12 & 7")))
(test-equal "Evaluates arithmetic bitwise inclusive or"
'("9")
(eval-word '(<sh-arithmetic> "8 | 1")))
(test-equal "Evaluates arithmetic bitwise exclusive or"
'("5")
(eval-word '(<sh-arithmetic> "15 ^ 10")))
(test-equal "Evaluates arithmetic logical and"
'("0")
(eval-word '(<sh-arithmetic> "0 && 1")))
(test-equal "Evaluates arithmetic logical or"
'("1")
(eval-word '(<sh-arithmetic> "0 || 1")))
(test-equal "Evaluates arithmetic conditional"
'("3")
(eval-word '(<sh-arithmetic> "0 ? 5 : 3")))
;;; Variables
(test-equal "Evaluates variables in arithmetic"
'("5")
(with-variables '(("x" . "3"))
(lambda ()
(eval-word `(<sh-arithmetic> "x + 2")))))
(test-equal "Evaluates non-numeric variables as zero in arithmetic"
'("0")
(with-variables '(("x" . "hello"))
(lambda ()
(eval-word `(<sh-arithmetic> "x")))))
;;; Assignments
(for-each (match-lambda
((op . result)
(test-equal (string-append "Evaluates arithmetic " op)
result
(with-variables '(("x" . "7"))
(lambda ()
(eval-word `(<sh-arithmetic>
,(string-append "x " op " 3")))
(getvar "x"))))))
'(("=" . "3")
("*=" . "21")
("/=" . "2")
("%=" . "1")
("+=" . "10")
("-=" . "4")
("<<=" . "56")
(">>=" . "0")
("&=" . "3")
("^=" . "4")
("|=" . "7")))
;;; Unary operators
(test-equal "Evaluates arithmetic negation"
'("-3")
(with-variables '(("x" . "3"))
(lambda ()
(eval-word `(<sh-arithmetic> "-x")))))
(test-equal "Evaluates arithmetic unary plus"
'("3")
(with-variables '(("x" . "3"))
(lambda ()
(eval-word `(<sh-arithmetic> "+x")))))
(test-equal "Evaluates arithmetic bitwise complement"
'("-6")
(eval-word `(<sh-arithmetic> "~5")))
(test-equal "Evaluates arithmetic logical complement"
'("0")
(eval-word `(<sh-arithmetic> "!1")))
(test-equal "Evaluates arithmetic negation on the left"
'("-12")
(with-variables '(("x" . "3"))
(lambda ()
(eval-word `(<sh-arithmetic> "-x * 4")))))
(test-equal "Evaluates arithmetic negation on the right"
'("0")
(with-variables '(("x" . "3"))
(lambda ()
(eval-word `(<sh-arithmetic> "3 + -x")))))
(test-end "eval")