#!/bin/sh exec guile --no-auto-compile -e main -s "$0" "$@" !# ;;;; test-driver.scm - Guile test driver for Automake testsuite harness (define script-version "2021-02-02.05") ;UTC ;;; Copyright © 2015, 2016 Mathieu Lirzin ;;; Copyright © 2021 Maxim Cournoyer ;;; ;;; This program 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. ;;; ;;; This program 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 this program. If not, see . ;;;; Commentary: ;;; ;;; This script provides a Guile test driver using the SRFI-64 Scheme API for ;;; test suites. SRFI-64 is distributed with Guile since version 2.0.9. ;;; ;;;; Code: (use-modules (ice-9 format) (ice-9 getopt-long) (ice-9 pretty-print) (ice-9 regex) (srfi srfi-1) (srfi srfi-19) (srfi srfi-26) (srfi srfi-64)) (define (show-help) (display "Usage: test-driver --test-name=NAME --log-file=PATH --trs-file=PATH [--expect-failure={yes|no}] [--color-tests={yes|no}] [--select=REGEXP] [--exclude=REGEXP] [--errors-only={yes|no}] [--enable-hard-errors={yes|no}] [--brief={yes|no}}] [--show-duration={yes|no}] [--] TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] The '--test-name' option is mandatory. The '--select' and '--exclude' options allow selecting or excluding individual test cases via a regexp, respectively. T
# GNU Guix --- Functional package management for GNU
# Copyright © 2020, 2022, 2024 Ludovic Courtès <ludo@gnu.org>
#
# This file is part of GNU Guix.
#
# GNU Guix 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.
#
# GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

#
# Test the 'guix git authenticate' command-line utility.
#

# Skip if we're not in a Git checkout.
[ -d "$abs_top_srcdir/.git" ] || exit 77

# Skip if there's no 'keyring' branch.
guile -c '(use-modules (git))
  (member "refs/heads/keyring" (branch-list (repository-open ".")))' || \
    exit 77

# Keep in sync with '%default-channels' in (guix channels)!
intro_commit="9edb3f66fd807b096b48283debdcddccfea34bad"
intro_signer="BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA"

cache_key="test-$$"

# This must fail because the end commit is not a descendant of $intro_commit.
guix git authenticate "$intro_commit" "$intro_signer"	\
     --cache-key="$cache_key" --stats			\
     --end=9549f0283a78fe36f2d4ff2a04ef8ad6b0c02604 && false

# The v1.2.0 commit is a descendant of $intro_commit and it satisfies the
# authorization invariant.
v1_2_0_commit="a099685659b4bfa6b3218f84953cbb7ff9e88063"
guix git authenticate "$intro_commit" "$intro_signer"	\
     --cache-key="$cache_key" --stats			\
     --end="$v1_2_0_commit"

# Check a commit that came soon after v1.2.0.  No need to repeat $intro_commit
# and $intro_signer because it should have been recorded in '.git/config'.
after_v1_2_0="be4d9527b55b6829e33a6e0727496af25927a786"
guix git authenticate				\
     --cache-key="$cache_key" --stats		\
     --end="$v1_2_0_commit"

rm "$XDG_CACHE_HOME/guix/authentication/$cache_key"

# Commit and signer of the 'v1.0.0' tag.
v1_0_0_commit="6298c3ffd9654d3231a6f25390b056483e8f407c"
v1_0_0_signer="3CE4 6455 8A84 FDC6 9DB4  0CFB 090B 1199 3D9A EBB5" # civodul
v1_0_1_commit="d68de958b60426798ed62797ff7c96c327a672ac"

# This should succeed because v1.0.0 is an ancestor of $intro_commit.
guix git authenticate "$intro_commit" "$intro_signer"	\
     --cache-key="$cache_key" --stats			\
     --end="$v1_0_0_commit"

# This should fail because these commits lack '.guix-authorizations'.
guix git authenticate "$v1_0_0_commit" "$v1_0_0_signer" \
       --cache-key="$cache_key" --end="$v1_0_1_commit" && false

# This should work thanks to '--historical-authorizations'.
guix git authenticate "$v1_0_0_commit" "$v1_0_0_signer" 	\
     --cache-key="$cache_key" --end="$v1_0_1_commit" --stats	\
     --historical-authorizations="$abs_top_srcdir/etc/historical-authorizations"
t assq <> results)) (result (cut assq-ref results <>)) (test-case-name (test-runner-test-name runner)) (start (hash-ref test-cases-start-time test-case-name)) (end (current-time time-monotonic)) (time-elapsed (time-difference end start)) (time-elapsed-seconds (+ (time-second time-elapsed) (* 1e-9 (time-nanosecond time-elapsed))))) (unless (or brief? (and errors-only? (test-skipped? runner))) ;; Display the result of each test case on the console. (format out-port "~a: ~a - ~a ~@[[~,3fs]~]~%" (result->string (test-result-kind runner) #:colorize? color?) test-name test-case-name (and show-duration? time-elapsed-seconds))) (unless (and errors-only? (not (test-failed? runner))) (format #t "test-name: ~A~%" (result 'test-name)) (format #t "location: ~A~%" (string-append (result 'source-file) ":" (number->string (result 'source-line)))) (test-display "source" (result 'source-form) #:pretty? #t) (when (result? 'expected-value) (test-display "expected-value" (result 'expected-value))) (when (result? 'expected-error) (test-display "expected-error" (result 'expected-error) #:pretty? #t)) (when (result? 'actual-value) (test-display "actual-value" (result 'actual-value))) (when (result? 'actual-error) (test-display "actual-error" (result 'actual-error) #:pretty? #t)) (format #t "result: ~a~%" (result->string (result 'result-kind))) (newline)) (format trs-port ":test-result: ~A ~A [~,3fs]~%" (result->string (test-result-kind runner)) (test-runner-test-name runner) time-elapsed-seconds))) (define (test-on-group-end-gnu runner) ;; Procedure called by a 'test-end', including at the end of a test-group. (let ((fail (or (positive? (test-runner-fail-count runner)) (positive? (test-runner-xpass-count runner)))) (skip (or (positive? (test-runner-skip-count runner)) (positive? (test-runner-xfail-count runner))))) ;; XXX: The global results need some refinements for XPASS. (format trs-port ":global-test-result: ~A~%" (if fail "FAIL" (if skip "SKIP" "PASS"))) (format trs-port ":recheck: ~A~%" (if fail "yes" "no")) (format trs-port ":copy-in-global-log: ~A~%" (if (or fail skip) "yes" "no")) (when brief? ;; Display the global test group result on the console. (format out-port "~A: ~A~%" (result->string (if fail 'fail (if skip 'skip 'pass)) #:colorize? color?) test-name)) #f)) (let ((runner (test-runner-null))) (test-runner-on-test-begin! runner test-on-test-begin-gnu) (test-runner-on-test-end! runner test-on-test-end-gnu) (test-runner-on-group-end! runner test-on-group-end-gnu) (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple) runner)) ;;; ;;; SRFI 64 test specifiers. ;;; (define (test-match-name* regexp) "Return a test specifier that matches a test name against REGEXP." (lambda (runner) (string-match regexp (test-runner-test-name runner)))) (define (test-match-name*/negated regexp) "Return a negated test specifier version of test-match-name*." (lambda (runner) (not (string-match regexp (test-runner-test-name runner))))) ;;; XXX: test-match-all is a syntax, which isn't convenient to use with a list ;;; of test specifiers computed at run time. Copy this SRFI 64 internal ;;; definition here, which is the procedural equivalent of 'test-match-all'. (define (%test-match-all . pred-list) (lambda (runner) (let ((result #t)) (let loop ((l pred-list)) (if (null? l) result (begin (if (not ((car l) runner)) (set! result #f)) (loop (cdr l)))))))) ;;; ;;; Entry point. ;;; (define (main . args) (let* ((opts (getopt-long (command-line) %options)) (option (cut option-ref opts <> <>))) (cond ((option 'help #f) (show-help)) ((option 'version #f) (format #t "test-driver.scm ~A~%" script-version)) (else (let* ((log (and=> (option 'log-file #f) (cut open-file <> "w0"))) (trs (and=> (option 'trs-file #f) (cut open-file <> "wl"))) (out (duplicate-port (current-output-port) "wl")) (test-name (option 'test-name #f)) (select (option 'select #f)) (exclude (option 'exclude #f)) (test-specifiers (filter-map identity (list (and=> select test-match-name*) (and=> exclude test-match-name*/negated)))) (test-specifier (apply %test-match-all test-specifiers)) (color-tests (if (assoc 'color-tests opts) (option->boolean opts 'color-tests) #t))) (when log (redirect-port log (current-output-port)) (redirect-port log (current-warning-port)) (redirect-port log (current-error-port))) (test-with-runner (test-runner-gnu test-name #:color? color-tests #:brief? (option->boolean opts 'brief) #:errors-only? (option->boolean opts 'errors-only) #:show-duration? (option->boolean opts 'show-duration) #:out-port out #:trs-port trs) (test-apply test-specifier (lambda _ (load-from-path test-name)))) (and=> log close-port) (and=> trs close-port) (close-port out)))) (exit 0))) ;;; Local Variables: ;;; eval: (add-hook 'write-file-functions 'time-stamp) ;;; time-stamp-start: "(define script-version \"" ;;; time-stamp-format: "%:y-%02m-%02d.%02H" ;;; time-stamp-time-zone: "UTC" ;;; time-stamp-end: "\") ;UTC" ;;; End: ;;;; test-driver.scm ends here.