123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- ;;;; coverage.test --- Code coverage. -*- mode: scheme; coding: utf-8; -*-
- ;;;;
- ;;;; Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2017 Free Software Foundation, Inc.
- ;;;;
- ;;;; This library is free software; you can redistribute it and/or
- ;;;; modify it under the terms of the GNU Lesser General Public
- ;;;; License as published by the Free Software Foundation; either
- ;;;; version 3 of the License, or (at your option) any later version.
- ;;;;
- ;;;; This library 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
- ;;;; Lesser General Public License for more details.
- ;;;;
- ;;;; You should have received a copy of the GNU Lesser General Public
- ;;;; License along with this library; if not, write to the Free Software
- ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- (define-module (test-coverage)
- #:use-module (test-suite lib)
- #:use-module (system vm coverage)
- #:use-module (system vm vm)
- #:use-module (system base compile)
- #:use-module (system foreign)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11))
- (define-syntax code
- (syntax-rules ()
- ((_ filename snippet)
- (let ((input (open-input-string snippet)))
- (set-port-filename! input filename)
- (read-enable 'positions)
- (compile (read input))))))
- (define test-procedure
- (compile '(lambda (x)
- (if (> x 2)
- (- x 2)
- (+ x 2)))))
- (with-test-prefix "instrumented/executed-lines"
- (pass-if "instr = exec"
- (let ((proc (code "foo.scm" "(lambda (x y) ;; 0
- (+ x y)) ;; 1")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 1 2)))))
- (and (coverage-data? data)
- (= 3 result)
- (let-values (((instr exec)
- (instrumented/executed-lines data "foo.scm")))
- (and (= 2 instr) (= 2 exec)))))))
- (pass-if "instr >= exec"
- (let ((proc (code "foo.scm" "(lambda (x y) ;; 0
- (if (> x y) ;; 1
- (begin ;; 2
- (display x) ;; 3
- (+ x y)))) ;; 4")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 1 2)))))
- (and (coverage-data? data)
- (let-values (((instr exec)
- (instrumented/executed-lines data "foo.scm")))
- (and (> instr 0) (>= instr exec))))))))
- (with-test-prefix "line-execution-counts"
- (pass-if "once"
- (let ((proc (code "bar.scm" "(lambda (x y) ;; 0
- (+ (/ x y) ;; 1
- (* x y))) ;; 2")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 1 2)))))
- (let ((counts (line-execution-counts data "bar.scm")))
- (and (pair? counts)
- (every (lambda (line+count)
- (let ((line (car line+count))
- (count (cdr line+count)))
- (and (>= line 0)
- (<= line 2)
- (= count 1))))
- counts))))))
- ;; Unhappily, lack of source location on identifiers combined with a
- ;; block reordering change makes this test fail. The right solution
- ;; is to fix the compiler, but really it should happen by fixing
- ;; psyntax to have source location info for identifiers and immediate
- ;; values.
- (expect-fail "several times"
- (let ((proc (code "fooz.scm" "(lambda (x) ;; 0
- (format #f \"hello\") ;; 1
- (let loop ((x x)) ;; 2
- (cond ((> x 0) ;; 3
- (begin ;; 4
- (format #f \"~a\" x)
- (loop (1- x)))) ;; 6
- ((= x 0) #t) ;; 7
- ((< x 0) 'never))))")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 77)))))
- (let ((counts (line-execution-counts data "fooz.scm")))
- (and (pair? counts)
- (every (lambda (line+count)
- (let ((line (car line+count))
- (count (cdr line+count)))
- ;; The actual line counts for aliasing
- ;; operations, like the loop header that
- ;; initializes "x" to "x", are sensitive to
- ;; whether there is an associated "mov"
- ;; instruction, or whether the value is left
- ;; in place. Currently there are no
- ;; instructions for line 2, but we allow 1 as
- ;; well.
- (case line
- ((0 1) (= count 1))
- ((2) (<= 0 count 1))
- ((3) (= count 78))
- ((4 5 6) (= count 77))
- ((7) (= count 1))
- ((8) (= count 0))
- (else #f))))
- counts))))))
- (pass-if "some"
- (let ((proc (code "baz.scm" "(lambda (x y) ;; 0
- (if (> x y) ;; 1
- (begin ;; 2
- (display x) ;; 3
- (+ x y)) ;; 4
- (+ x y))) ;; 5")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 1 2)))))
- (let ((counts (line-execution-counts data "baz.scm")))
- (and (pair? counts)
- (every (lambda (line+count)
- (let ((line (car line+count))
- (count (cdr line+count)))
- (case line
- ((0 1 5) (= count 1))
- ((2 3) (= count 0))
- ((4) #t) ;; the start of the `else' branch is
- ;; attributed to line 4
- (else #f))))
- counts))))))
- ;; Same unfortunate caveat as above: block ordering and source
- ;; locations :(
- (expect-fail "one proc hit, one proc unused"
- (let ((proc (code "baz.scm" "(letrec ((even? (lambda (x) ;; 0
- (or (= x 0) ;; 1
- (not (odd? (1- x))))))
- (odd? (lambda (x) ;; 3
- (not (even? (1- x)))))) ;; 4
- even?)")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 0)))))
- (let ((counts (line-execution-counts data "baz.scm")))
- (and (pair? counts)
- (every (lambda (line+count)
- (let ((line (car line+count))
- (count (cdr line+count)))
- (case line
- ((0 1) (= count 1))
- ((2 3 4 5) (= count 0))
- (else #f))))
- counts))))))
- (pass-if "case-lambda"
- (let ((proc (code "cl.scm" "(case-lambda ;; 0
- ((x) (+ x 3)) ;; 1
- ((x y) (+ x y))) ;; 2")))
- (let-values (((data result)
- (with-code-coverage
- (lambda ()
- (+ (proc 1) (proc 2 3))))))
- (let ((counts (line-execution-counts data "cl.scm")))
- (and (pair? counts)
- (lset= equal? '((0 . 2) (1 . 1) (2 . 1)) counts))))))
- (pass-if "all code on one line"
- ;; There are several proc/IP pairs pointing to this source line, yet the hit
- ;; count for the line should be 1.
- (let ((proc (code "one-liner.scm"
- "(lambda (x y) (+ x y (* x y) (if (> x y) 1 2) (quotient y x)))")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 451 1884)))))
- (let ((counts (line-execution-counts data "one-liner.scm")))
- (equal? counts '((0 . 1)))))))
- (pass-if "tail calls"
- (let ((proc (code "tail-calls.scm"
- "(begin
- (define (tail-call-test)
- (display \"foo\\n\")
- (tail-call-target))
- (define (tail-call-target)
- (display \"bar\\n\"))
- tail-call-test)")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (with-output-to-string proc)))))
- (let ((counts (line-execution-counts data "tail-calls.scm")))
- (define (lset-contains? eq? a b)
- (lset= eq? b (lset-intersection eq? a b)))
- ;; Due to top-level binding optimization, the target may be
- ;; inlined or into the caller. All we can say is that the
- ;; entry was seen, and the two displays were called.
- (lset-contains? equal? counts '((1 . 1) (2 . 1) (6 . 1))))))))
- (with-test-prefix "procedure-execution-count"
- (pass-if "several times"
- (let ((proc (code "foo.scm" "(lambda (x y) x)")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (+ (proc 1 2) (proc 2 3))))))
- (and (coverage-data? data)
- (= 3 result)
- (= (procedure-execution-count data proc) 2)))))
- (pass-if "case-lambda"
- (let ((proc (code "foo.scm" "(case-lambda ((x) x) ((x y) (+ x y)))")))
- (let-values (((data result)
- (with-code-coverage
- (lambda ()
- (+ (proc 1) (proc 2 3))))))
- (and (coverage-data? data)
- (= 6 result)
- (= (procedure-execution-count data proc) 2)))))
- (pass-if "never"
- (let ((proc (code "foo.scm" "(lambda (x y) x)")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (+ 1 2)))))
- (and (coverage-data? data)
- (= 3 result)
- (zero? (procedure-execution-count data proc))))))
- (pass-if "applicable struct"
- (let* ((<box> (make-struct/no-tail <applicable-struct-vtable> 'pw))
- (proc (lambda args (length args)))
- (b (make-struct/no-tail <box> proc)))
- (let-values (((data result)
- (with-code-coverage b)))
- (and (coverage-data? data)
- (= 0 result)
- (= (procedure-execution-count data proc) 1)))))
- (pass-if "called from C"
- ;; The `scm_call_N' functions use the VM returned by `the-vm'. This
- ;; test makes sure that their calls are traced.
- (let ((proc (code "foo.scm" "(lambda (x y) (+ x y))"))
- (call (false-if-exception ; can we resolve `scm_call_2'?
- (pointer->procedure '*
- (dynamic-func "scm_call_2"
- (dynamic-link))
- '(* * *)))))
- (if call
- (let-values (((data result)
- (with-code-coverage
- (lambda ()
- (call (make-pointer (object-address proc))
- (make-pointer (object-address 1))
- (make-pointer (object-address 2)))))))
- (and (coverage-data? data)
- (= (object-address 3) (pointer-address result))
- (= (procedure-execution-count data proc) 1)))
- (throw 'unresolved))))
- (pass-if "called from eval"
- (let-values (((data result)
- (with-code-coverage
- (lambda ()
- (eval '(test-procedure 123) (current-module))))))
- (and (coverage-data? data)
- (= (test-procedure 123) result)
- (= (procedure-execution-count data test-procedure) 1)))))
- (with-test-prefix "instrumented-source-files"
- (pass-if "source files are listed as expected"
- (let ((proc (code "chbouib.scm" "(lambda (x y) x)")))
- (let-values (((data result)
- (with-code-coverage
- (lambda () (proc 1 2)))))
- (let ((files (map basename (instrumented-source-files data))))
- (and (member "boot-9.scm" files)
- (member "chbouib.scm" files)
- #t))))))
|