;;; Gash -- Guile As SHell ;;; Copyright © 2018, 2019, 2021 Timothy Sample ;;; ;;; 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 . (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 '( "x"))))) (test-equal "Splits parameter results" '("foo" "bar") (with-variables '(("x" . "foo bar")) (lambda () (eval-word '( "x"))))) (test-equal "Resolves quoted parameters" '("foo") (with-variables '(("x" . "foo")) (lambda () (eval-word '( ( "x")))))) (test-equal "Ignores spaces in quoted parameters" '("foo bar") (with-variables '(("x" . "foo bar")) (lambda () (eval-word '( ( "x")))))) (test-equal "Treats empty variables as nothing" '() (with-variables '(("x" . "")) (lambda () (eval-word '( "x"))))) (test-equal "Treats unset variables as nothing" '() (with-variables '() (lambda () (eval-word '( "x"))))) (test-equal "Preserves empty variables when quoted" '("") (with-variables '(("x" . "")) (lambda () (eval-word '( ( "x")))))) (test-equal "Preserves unset variables when quoted" '("") (with-variables '() (lambda () (eval-word '( ( "x")))))) ;;; Parameter operations. ;;; or (test-equal "Handles 'or' when parameter is set" '("foo") (with-variables '(("x" . "foo")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'or' when parameter is set and empty" '() (with-variables '(("x" . "")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'or' when parameter is unset" '("bar") (with-variables '() (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'or' fall-through without default" '() (with-variables '() (lambda () (eval-word '( "x" #f))))) ;;; or* (test-equal "Handles 'or*' when parameter is set" '("foo") (with-variables '(("x" . "foo")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'or*' when parameter is set and empty" '("bar") (with-variables '(("x" . "")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'or*' when parameter is unset" '("bar") (with-variables '() (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'or*' fall-through without default" '() (with-variables '() (lambda () (eval-word '( "x" #f))))) ;;; or! (test-equal "Handles 'or!' when parameter is set" '(("foo") "foo") (with-variables '(("x" . "foo")) (lambda () (list (eval-word '( "x" "bar")) (getvar "x"))))) (test-equal "Handles 'or!' when parameter is set and empty" '(() "") (with-variables '(("x" . "")) (lambda () (list (eval-word '( "x" "bar")) (getvar "x"))))) (test-equal "Handles 'or!' when parameter is unset" '(("bar") "bar") (with-variables '() (lambda () (list (eval-word '( "x" "bar")) (getvar "x"))))) (test-equal "Handles 'or!' fall-through without default" '(() "") (with-variables '() (lambda () (list (eval-word '( "x" #f)) (getvar "x"))))) ;;; or!* (test-equal "Handles 'or!*' when parameter is set" '(("foo") "foo") (with-variables '(("x" . "foo")) (lambda () (list (eval-word '( "x" "bar")) (getvar "x"))))) (test-equal "Handles 'or!*' when parameter is set and empty" '(("bar") "bar") (with-variables '(("x" . "")) (lambda () (list (eval-word '( "x" "bar")) (getvar "x"))))) (test-equal "Handles 'or!*' when parameter is unset" '(("bar") "bar") (with-variables '() (lambda () (list (eval-word '( "x" "bar")) (getvar "x"))))) (test-equal "Handles 'or!*' fall-through without default" '(() "") (with-variables '() (lambda () (list (eval-word '( "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 '( "x" ( "y"))) (getvar "x"))))) ;;; FIXME: Test 'assert'. ;;; and (test-equal "Handles 'and' when parameter is set" '("bar") (with-variables '(("x" . "foo")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'and' when parameter is set and empty" '("bar") (with-variables '(("x" . "")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'and' when parameter is unset" '() (with-variables '() (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'and' fall-through without default" '() (with-variables '(("x" . "foo")) (lambda () (eval-word '( "x" #f))))) ;;; and* (test-equal "Handles 'and*' when parameter is set" '("bar") (with-variables '(("x" . "foo")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'and*' when parameter is set and empty" '() (with-variables '(("x" . "")) (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'and*' when parameter is unset" '() (with-variables '() (lambda () (eval-word '( "x" "bar"))))) (test-equal "Handles 'and*' fall-through without default" '() (with-variables '(("x" . "foo")) (lambda () (eval-word '( "x" #f))))) ;;; length (test-equal "Handles 'length' when parameter is set" '("3") (with-variables '(("x" . "foo")) (lambda () (eval-word '( "x"))))) (test-equal "Handles 'length' when parameter is unset" '("0") (with-variables '() (lambda () (eval-word '( "x"))))) ;;; Command substition. (test-equal "Resolves commands" '("foo") (eval-word '( ( "echo" "foo")))) (test-equal "Splits command results" '("foo" "bar") (eval-word '( ( "echo" "foo bar")))) (test-equal "Resolves quoted commands" '("foo") (eval-word '( ( ( "echo" "foo"))))) (test-equal "Ignores spaces in quoted commands" '("foo bar") (eval-word '( ( ( "echo" "foo bar"))))) ;;; Arithmetic expansion. (test-equal "Evaluates arithmetic constant (decimal)" '("42") (eval-word '( "42"))) (test-equal "Evaluates arithmetic constant (octal)" '("34") (eval-word '( "042"))) (test-equal "Evaluates arithmetic constant (hexadecimal)" '("66") (eval-word '( "0x42"))) ;;; Binary (and tertiary) operators (test-equal "Evaluates arithmetic addition" '("6") (eval-word '( "2 + 4"))) (test-equal "Evaluates arithmetic subtraction" '("2") (eval-word '( "4 - 2"))) (test-equal "Evaluates arithmetic multiplication" '("12") (eval-word '( "3 * 4"))) (test-equal "Evaluates arithmetic division" '("6") (eval-word '( "19 / 3"))) (test-equal "Evaluates arithmetic modulo" '("2") (eval-word '( "32 % 3"))) (test-equal "Evaluates arithmetic left shift" '("12") (eval-word '( "3 << 2"))) (test-equal "Evaluates arithmetic right shift" '("3") (eval-word '( "15 >> 2"))) (test-equal "Evaluates arithmetic greater than" '("1") (eval-word '( "5 > 3"))) (test-equal "Evaluates arithmetic greater than or equal to" '("0") (eval-word '( "3 >= 5"))) (test-equal "Evaluates arithmetic less than" '("0") (eval-word '( "5 < 3"))) (test-equal "Evaluates arithmetic less than or equal to" '("1") (eval-word '( "3 <= 5"))) (test-equal "Evaluates arithmetic equals" '("0") (eval-word '( "0 == 1"))) (test-equal "Evaluates arithmetic not equals" '("1") (eval-word '( "0 != 1"))) (test-equal "Evaluates arithmetic bitwise and" '("4") (eval-word '( "12 & 7"))) (test-equal "Evaluates arithmetic bitwise inclusive or" '("9") (eval-word '( "8 | 1"))) (test-equal "Evaluates arithmetic bitwise exclusive or" '("5") (eval-word '( "15 ^ 10"))) (test-equal "Evaluates arithmetic logical and" '("0") (eval-word '( "0 && 1"))) (test-equal "Evaluates arithmetic logical or" '("1") (eval-word '( "0 || 1"))) (test-equal "Evaluates arithmetic conditional" '("3") (eval-word '( "0 ? 5 : 3"))) ;;; Variables (test-equal "Evaluates variables in arithmetic" '("5") (with-variables '(("x" . "3")) (lambda () (eval-word `( "x + 2"))))) (test-equal "Evaluates non-numeric variables as zero in arithmetic" '("0") (with-variables '(("x" . "hello")) (lambda () (eval-word `( "x"))))) ;;; Assignments (for-each (match-lambda ((op . result) (test-equal (string-append "Evaluates arithmetic " op) result (with-variables '(("x" . "7")) (lambda () (eval-word `( ,(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 `( "-x"))))) (test-equal "Evaluates arithmetic unary plus" '("3") (with-variables '(("x" . "3")) (lambda () (eval-word `( "+x"))))) (test-equal "Evaluates arithmetic bitwise complement" '("-6") (eval-word `( "~5"))) (test-equal "Evaluates arithmetic logical complement" '("0") (eval-word `( "!1"))) (test-equal "Evaluates arithmetic negation on the left" '("-12") (with-variables '(("x" . "3")) (lambda () (eval-word `( "-x * 4"))))) (test-equal "Evaluates arithmetic negation on the right" '("0") (with-variables '(("x" . "3")) (lambda () (eval-word `( "3 + -x"))))) (test-end "eval")