coverage.test 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. ;;;; coverage.test --- Code coverage. -*- mode: scheme; coding: utf-8; -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2017 Free Software Foundation, Inc.
  4. ;;;;
  5. ;;;; This library is free software; you can redistribute it and/or
  6. ;;;; modify it under the terms of the GNU Lesser General Public
  7. ;;;; License as published by the Free Software Foundation; either
  8. ;;;; version 3 of the License, or (at your option) any later version.
  9. ;;;;
  10. ;;;; This library is distributed in the hope that it will be useful,
  11. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;;; Lesser General Public License for more details.
  14. ;;;;
  15. ;;;; You should have received a copy of the GNU Lesser General Public
  16. ;;;; License along with this library; if not, write to the Free Software
  17. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. (define-module (test-coverage)
  19. #:use-module (test-suite lib)
  20. #:use-module (system vm coverage)
  21. #:use-module (system vm vm)
  22. #:use-module (system base compile)
  23. #:use-module (system foreign)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (srfi srfi-11))
  26. (define-syntax code
  27. (syntax-rules ()
  28. ((_ filename snippet)
  29. (let ((input (open-input-string snippet)))
  30. (set-port-filename! input filename)
  31. (read-enable 'positions)
  32. (compile (read input))))))
  33. (define test-procedure
  34. (compile '(lambda (x)
  35. (if (> x 2)
  36. (- x 2)
  37. (+ x 2)))))
  38. (with-test-prefix "instrumented/executed-lines"
  39. (pass-if "instr = exec"
  40. (let ((proc (code "foo.scm" "(lambda (x y) ;; 0
  41. (+ x y)) ;; 1")))
  42. (let-values (((data result)
  43. (with-code-coverage
  44. (lambda () (proc 1 2)))))
  45. (and (coverage-data? data)
  46. (= 3 result)
  47. (let-values (((instr exec)
  48. (instrumented/executed-lines data "foo.scm")))
  49. (and (= 2 instr) (= 2 exec)))))))
  50. (pass-if "instr >= exec"
  51. (let ((proc (code "foo.scm" "(lambda (x y) ;; 0
  52. (if (> x y) ;; 1
  53. (begin ;; 2
  54. (display x) ;; 3
  55. (+ x y)))) ;; 4")))
  56. (let-values (((data result)
  57. (with-code-coverage
  58. (lambda () (proc 1 2)))))
  59. (and (coverage-data? data)
  60. (let-values (((instr exec)
  61. (instrumented/executed-lines data "foo.scm")))
  62. (and (> instr 0) (>= instr exec))))))))
  63. (with-test-prefix "line-execution-counts"
  64. (pass-if "once"
  65. (let ((proc (code "bar.scm" "(lambda (x y) ;; 0
  66. (+ (/ x y) ;; 1
  67. (* x y))) ;; 2")))
  68. (let-values (((data result)
  69. (with-code-coverage
  70. (lambda () (proc 1 2)))))
  71. (let ((counts (line-execution-counts data "bar.scm")))
  72. (and (pair? counts)
  73. (every (lambda (line+count)
  74. (let ((line (car line+count))
  75. (count (cdr line+count)))
  76. (and (>= line 0)
  77. (<= line 2)
  78. (= count 1))))
  79. counts))))))
  80. ;; Unhappily, lack of source location on identifiers combined with a
  81. ;; block reordering change makes this test fail. The right solution
  82. ;; is to fix the compiler, but really it should happen by fixing
  83. ;; psyntax to have source location info for identifiers and immediate
  84. ;; values.
  85. (expect-fail "several times"
  86. (let ((proc (code "fooz.scm" "(lambda (x) ;; 0
  87. (format #f \"hello\") ;; 1
  88. (let loop ((x x)) ;; 2
  89. (cond ((> x 0) ;; 3
  90. (begin ;; 4
  91. (format #f \"~a\" x)
  92. (loop (1- x)))) ;; 6
  93. ((= x 0) #t) ;; 7
  94. ((< x 0) 'never))))")))
  95. (let-values (((data result)
  96. (with-code-coverage
  97. (lambda () (proc 77)))))
  98. (let ((counts (line-execution-counts data "fooz.scm")))
  99. (and (pair? counts)
  100. (every (lambda (line+count)
  101. (let ((line (car line+count))
  102. (count (cdr line+count)))
  103. ;; The actual line counts for aliasing
  104. ;; operations, like the loop header that
  105. ;; initializes "x" to "x", are sensitive to
  106. ;; whether there is an associated "mov"
  107. ;; instruction, or whether the value is left
  108. ;; in place. Currently there are no
  109. ;; instructions for line 2, but we allow 1 as
  110. ;; well.
  111. (case line
  112. ((0 1) (= count 1))
  113. ((2) (<= 0 count 1))
  114. ((3) (= count 78))
  115. ((4 5 6) (= count 77))
  116. ((7) (= count 1))
  117. ((8) (= count 0))
  118. (else #f))))
  119. counts))))))
  120. (pass-if "some"
  121. (let ((proc (code "baz.scm" "(lambda (x y) ;; 0
  122. (if (> x y) ;; 1
  123. (begin ;; 2
  124. (display x) ;; 3
  125. (+ x y)) ;; 4
  126. (+ x y))) ;; 5")))
  127. (let-values (((data result)
  128. (with-code-coverage
  129. (lambda () (proc 1 2)))))
  130. (let ((counts (line-execution-counts data "baz.scm")))
  131. (and (pair? counts)
  132. (every (lambda (line+count)
  133. (let ((line (car line+count))
  134. (count (cdr line+count)))
  135. (case line
  136. ((0 1 5) (= count 1))
  137. ((2 3) (= count 0))
  138. ((4) #t) ;; the start of the `else' branch is
  139. ;; attributed to line 4
  140. (else #f))))
  141. counts))))))
  142. ;; Same unfortunate caveat as above: block ordering and source
  143. ;; locations :(
  144. (expect-fail "one proc hit, one proc unused"
  145. (let ((proc (code "baz.scm" "(letrec ((even? (lambda (x) ;; 0
  146. (or (= x 0) ;; 1
  147. (not (odd? (1- x))))))
  148. (odd? (lambda (x) ;; 3
  149. (not (even? (1- x)))))) ;; 4
  150. even?)")))
  151. (let-values (((data result)
  152. (with-code-coverage
  153. (lambda () (proc 0)))))
  154. (let ((counts (line-execution-counts data "baz.scm")))
  155. (and (pair? counts)
  156. (every (lambda (line+count)
  157. (let ((line (car line+count))
  158. (count (cdr line+count)))
  159. (case line
  160. ((0 1) (= count 1))
  161. ((2 3 4 5) (= count 0))
  162. (else #f))))
  163. counts))))))
  164. (pass-if "case-lambda"
  165. (let ((proc (code "cl.scm" "(case-lambda ;; 0
  166. ((x) (+ x 3)) ;; 1
  167. ((x y) (+ x y))) ;; 2")))
  168. (let-values (((data result)
  169. (with-code-coverage
  170. (lambda ()
  171. (+ (proc 1) (proc 2 3))))))
  172. (let ((counts (line-execution-counts data "cl.scm")))
  173. (and (pair? counts)
  174. (lset= equal? '((0 . 2) (1 . 1) (2 . 1)) counts))))))
  175. (pass-if "all code on one line"
  176. ;; There are several proc/IP pairs pointing to this source line, yet the hit
  177. ;; count for the line should be 1.
  178. (let ((proc (code "one-liner.scm"
  179. "(lambda (x y) (+ x y (* x y) (if (> x y) 1 2) (quotient y x)))")))
  180. (let-values (((data result)
  181. (with-code-coverage
  182. (lambda () (proc 451 1884)))))
  183. (let ((counts (line-execution-counts data "one-liner.scm")))
  184. (equal? counts '((0 . 1)))))))
  185. (pass-if "tail calls"
  186. (let ((proc (code "tail-calls.scm"
  187. "(begin
  188. (define (tail-call-test)
  189. (display \"foo\\n\")
  190. (tail-call-target))
  191. (define (tail-call-target)
  192. (display \"bar\\n\"))
  193. tail-call-test)")))
  194. (let-values (((data result)
  195. (with-code-coverage
  196. (lambda () (with-output-to-string proc)))))
  197. (let ((counts (line-execution-counts data "tail-calls.scm")))
  198. (define (lset-contains? eq? a b)
  199. (lset= eq? b (lset-intersection eq? a b)))
  200. ;; Due to top-level binding optimization, the target may be
  201. ;; inlined or into the caller. All we can say is that the
  202. ;; entry was seen, and the two displays were called.
  203. (lset-contains? equal? counts '((1 . 1) (2 . 1) (6 . 1))))))))
  204. (with-test-prefix "procedure-execution-count"
  205. (pass-if "several times"
  206. (let ((proc (code "foo.scm" "(lambda (x y) x)")))
  207. (let-values (((data result)
  208. (with-code-coverage
  209. (lambda () (+ (proc 1 2) (proc 2 3))))))
  210. (and (coverage-data? data)
  211. (= 3 result)
  212. (= (procedure-execution-count data proc) 2)))))
  213. (pass-if "case-lambda"
  214. (let ((proc (code "foo.scm" "(case-lambda ((x) x) ((x y) (+ x y)))")))
  215. (let-values (((data result)
  216. (with-code-coverage
  217. (lambda ()
  218. (+ (proc 1) (proc 2 3))))))
  219. (and (coverage-data? data)
  220. (= 6 result)
  221. (= (procedure-execution-count data proc) 2)))))
  222. (pass-if "never"
  223. (let ((proc (code "foo.scm" "(lambda (x y) x)")))
  224. (let-values (((data result)
  225. (with-code-coverage
  226. (lambda () (+ 1 2)))))
  227. (and (coverage-data? data)
  228. (= 3 result)
  229. (zero? (procedure-execution-count data proc))))))
  230. (pass-if "applicable struct"
  231. (let* ((<box> (make-struct/no-tail <applicable-struct-vtable> 'pw))
  232. (proc (lambda args (length args)))
  233. (b (make-struct/no-tail <box> proc)))
  234. (let-values (((data result)
  235. (with-code-coverage b)))
  236. (and (coverage-data? data)
  237. (= 0 result)
  238. (= (procedure-execution-count data proc) 1)))))
  239. (pass-if "called from C"
  240. ;; The `scm_call_N' functions use the VM returned by `the-vm'. This
  241. ;; test makes sure that their calls are traced.
  242. (let ((proc (code "foo.scm" "(lambda (x y) (+ x y))"))
  243. (call (false-if-exception ; can we resolve `scm_call_2'?
  244. (pointer->procedure '*
  245. (dynamic-func "scm_call_2"
  246. (dynamic-link))
  247. '(* * *)))))
  248. (if call
  249. (let-values (((data result)
  250. (with-code-coverage
  251. (lambda ()
  252. (call (make-pointer (object-address proc))
  253. (make-pointer (object-address 1))
  254. (make-pointer (object-address 2)))))))
  255. (and (coverage-data? data)
  256. (= (object-address 3) (pointer-address result))
  257. (= (procedure-execution-count data proc) 1)))
  258. (throw 'unresolved))))
  259. (pass-if "called from eval"
  260. (let-values (((data result)
  261. (with-code-coverage
  262. (lambda ()
  263. (eval '(test-procedure 123) (current-module))))))
  264. (and (coverage-data? data)
  265. (= (test-procedure 123) result)
  266. (= (procedure-execution-count data test-procedure) 1)))))
  267. (with-test-prefix "instrumented-source-files"
  268. (pass-if "source files are listed as expected"
  269. (let ((proc (code "chbouib.scm" "(lambda (x y) x)")))
  270. (let-values (((data result)
  271. (with-code-coverage
  272. (lambda () (proc 1 2)))))
  273. (let ((files (map basename (instrumented-source-files data))))
  274. (and (member "boot-9.scm" files)
  275. (member "chbouib.scm" files)
  276. #t))))))