1; QuickCheck clone 2 3(define-record-type :generator 4 (make-generator proc) 5 generator? 6 ;; int(size) random-generator -> val 7 (proc generator-proc)) 8 9; for transliteration from Haskell 10(define (return val) 11 (make-generator 12 (lambda (size rgen) 13 val))) 14 15(define (>>= m1 k) 16 (let ((proc1 (generator-proc m1))) 17 (make-generator 18 (lambda (size rgen) 19 (call-with-values 20 (lambda () 21 (random-generator-split rgen)) 22 (lambda (rgen1 rgen2) 23 (let ((gen (k (proc1 size rgen1)))) 24 ((generator-proc gen) size rgen2)))))))) 25 26(define (sequence gens) 27 (if (null? gens) 28 (return '()) 29 (>>= (car gens) 30 (lambda (val) 31 (>>= (sequence (cdr gens)) 32 (lambda (rest) 33 (return (cons val rest)))))))) 34 35; for export 36(define generator-unit return) 37(define generator-bind >>=) 38(define generator-sequence sequence) 39 40(define (lift->generator proc . gens) 41 (>>= (sequence gens) 42 (lambda (vals) 43 (return (apply proc vals))))) 44 45; [lower, upper] 46(define (choose-integer lower upper) 47 (make-generator 48 (lambda (size rgen) 49 (call-with-values 50 (lambda () 51 (random-integer rgen lower upper)) 52 (lambda (n _) 53 n))))) 54 55(define (choose-real lower upper) 56 (make-generator 57 (lambda (size rgen) 58 (call-with-values 59 (lambda () 60 (random-real rgen lower upper)) 61 (lambda (n _) 62 n))))) 63 64(define choose-ascii-char 65 (lift->generator integer->char (choose-integer 0 127))) 66 67(define choose-ascii-letter 68 (lift->generator (lambda (i) 69 (string-ref 70 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" i)) 71 (choose-integer 0 51))) 72 73(define choose-printable-ascii-char 74 (lift->generator integer->char (choose-integer 32 127))) 75 76(define max-scalar-value #x10FFFF) 77(define gap-start #xD800) 78(define gap-end #xE000) 79(define gap-size (- gap-end gap-start)) 80 81(define (choose-char lower upper) 82 (make-generator 83 (lambda (size rgen) 84 (call-with-values 85 (lambda () 86 (random-integer rgen (char->integer lower) 87 (min (char->integer upper) 88 (- max-scalar-value gap-size)))) 89 (lambda (n _) 90 (integer->char 91 (if (< n gap-start) 92 n 93 (+ n gap-size)))))))) 94 95; int (generator a) -> (generator a) 96(define (variant v gen) 97 (let ((proc (generator-proc gen))) 98 (make-generator 99 (lambda (size rgen) 100 (let loop ((v (+ 1 v)) 101 (rgen rgen)) 102 (if (zero? v) 103 (proc size rgen) 104 (call-with-values 105 (lambda () 106 (random-generator-split rgen)) 107 (lambda (rgen1 rgen2) 108 (loop (- v 1) rgen2))))))))) 109 110; int random-gen (generator a) -> a 111(define (generate n rgen gen) 112 (call-with-values 113 (lambda () 114 (random-integer rgen 0 n)) 115 (lambda (size nrgen) 116 ((generator-proc gen) size nrgen)))) 117 118; (vals -> (generator b)) -> (generator (vals -> b)) 119(define (promote proc) 120 (make-generator 121 (lambda (size rgen) 122 (lambda vals 123 (let ((g (apply proc vals))) 124 ((generator-proc g) size rgen)))))) 125 126; (int -> (generator a)) -> (generator a) 127(define (sized proc) 128 (make-generator 129 (lambda (size rgen) 130 (let ((g (proc size))) 131 ((generator-proc g) size rgen))))) 132 133; (list a) -> (generator a) 134(define (choose-one-of lis) 135 (lift->generator (lambda (n) 136 (list-ref lis n)) 137 (choose-integer 0 (- (length lis) 1)))) 138 139; vector from the paper 140; (generator a) int -> (generator (list a)) 141(define (choose-list el-gen n) 142 (let recur ((n n)) 143 (if (zero? n) 144 (return '()) 145 (>>= el-gen 146 (lambda (val) 147 (>>= (recur (- n 1)) 148 (lambda (rest) 149 (return (cons val rest))))))))) 150 151; (generator char) int -> (generator string) 152(define (choose-string char-gen n) 153 (lift->generator list->string (choose-list char-gen n))) 154 155(define (choose-symbol char-gen n) 156 (>>= (choose-string char-gen n) 157 (lambda (s) 158 (return (string->symbol s))))) 159 160(define (choose-vector el-gen n) 161 (lift->generator list->vector (choose-list el-gen n))) 162 163; (list (promise (generator a))) -> (generator a) 164(define (choose-mixed gens) 165 (>>= (choose-one-of gens) 166 force)) 167 168; (list (pair int (generator a))) -> (generator a) 169(define (choose-with-frequencies lis) 170 (>>= (choose-integer 1 (apply + (map car lis))) 171 (lambda (n) 172 (pick n lis)))) 173 174(define (pick n lis) 175 (let ((k (caar lis))) 176 (if (<= n k) 177 (cdar lis) 178 (pick (- n k) (cdr lis))))) 179 180(define-record-type :arbitrary 181 (make-arbitrary generator transformer) 182 arbitrary? 183 ;; (generator a) 184 (generator arbitrary-generator) 185 ;; a (generator b) -> (generator b) 186 (transformer arbitrary-transformer)) 187 188; class Arbitrary a where 189; arbitrary :: Gen a 190; coarbitrary :: a -> Gen b -> Gen b 191 192(define (coarbitrary arb val gen) 193 ((arbitrary-transformer arb) val gen)) 194 195(define arbitrary-boolean 196 (make-arbitrary (choose-one-of '(#t #f)) 197 (lambda (a gen) 198 (variant (if a 0 1) gen)))) 199 200(define arbitrary-integer 201 (make-arbitrary (sized 202 (lambda (n) 203 (choose-integer (- n) n))) 204 (lambda (n gen) 205 (variant (if (>= n 0) 206 (* 2 n) 207 (+ (* 2 (- n)) 1)) 208 gen)))) 209 210(define (arbitrary-integer-from-to lower upper) 211 (make-arbitrary (choose-integer lower upper) 212 (lambda (n gen) 213 (variant (if (>= n 0) 214 (* 2 n) 215 (+ (* 2 (- n)) 1)) 216 gen)))) 217 218(define arbitrary-natural 219 (make-arbitrary (sized 220 (lambda (n) 221 (choose-integer 0 n))) 222 (lambda (n gen) 223 (variant n gen)))) 224 225(define arbitrary-ascii-char 226 (make-arbitrary choose-ascii-char 227 (lambda (ch gen) 228 (variant (char->integer ch) gen)))) 229 230(define arbitrary-ascii-letter 231 (make-arbitrary choose-ascii-letter 232 (lambda (ch gen) 233 (variant (char->integer ch) gen)))) 234 235(define arbitrary-printable-ascii-char 236 (make-arbitrary choose-printable-ascii-char 237 (lambda (ch gen) 238 (variant (char->integer ch) gen)))) 239 240(define arbitrary-char 241 (make-arbitrary (sized 242 (lambda (n) 243 (choose-char (integer->char 0) 244 (integer->char n)))) 245 (lambda (ch gen) 246 (variant (char->integer ch) gen)))) 247 248(define (make-rational a b c) 249 (+ a 250 (/ a 251 (+ (abs c) 1)))) 252 253(define arbitrary-rational 254 (make-arbitrary (lift->generator make-rational 255 (arbitrary-generator arbitrary-integer) 256 (arbitrary-generator arbitrary-integer) 257 (arbitrary-generator arbitrary-integer)) 258 (lambda (r gen) 259 (coarbitrary arbitrary-integer 260 (numerator r) 261 (coarbitrary arbitrary-integer 262 (denominator r) gen))))) 263(define (fraction a b c) 264 (+ a 265 (exact->inexact (/ b 266 (+ (abs c) 1))))) 267 268(define arbitrary-real 269 (make-arbitrary (choose-with-frequencies 270 (list 271 (cons 5 (sized 272 (lambda (n) 273 (choose-integer (- n) n)))) 274 (cons 4 (lift->generator make-rational 275 (arbitrary-generator arbitrary-integer) 276 (arbitrary-generator arbitrary-integer) 277 (arbitrary-generator arbitrary-integer))) 278 (cons 1 (lift->generator fraction 279 (arbitrary-generator arbitrary-integer) 280 (arbitrary-generator arbitrary-integer) 281 (arbitrary-generator arbitrary-integer))))) 282 (lambda (r gen) 283 (let ((fr (rationalize (inexact->exact r) 1/1000))) 284 (coarbitrary arbitrary-integer 285 (numerator fr) 286 (coarbitrary arbitrary-integer 287 (denominator fr) gen)))))) 288 289 290(define (arbitrary-mixed pred+arbitrary-promise-list) 291 (make-arbitrary (choose-mixed (map (lambda (p) 292 (delay (arbitrary-generator (force (cdr p))))) 293 pred+arbitrary-promise-list)) 294 (lambda (val gen) 295 (let loop ((lis pred+arbitrary-promise-list) (n 0)) 296 (cond 297 ((null? lis) 298 (assertion-violation 'arbitrary-mixed 299 "value matches none of the predicates" 300 val pred+arbitrary-promise-list)) 301 (((caar lis) val) 302 (variant n gen)) 303 (else 304 (loop (cdr lis) (+ 1 n)))))))) 305 306(define (arbitrary-one-of eql? . vals) 307 (make-arbitrary (choose-one-of vals) 308 (lambda (val gen) 309 (let loop ((lis vals) (n 0)) 310 (cond 311 ((null? lis) 312 (assertion-violation 'arbitrary-one-of 313 "value is not in the list" 314 val vals)) 315 ((eql? (car lis) val) 316 (variant n gen)) 317 (else 318 (loop (cdr lis) (+ 1 n)))))))) 319 320(define (arbitrary-pair arbitrary-car arbitrary-cdr) 321 (make-arbitrary (lift->generator cons 322 (arbitrary-generator arbitrary-car) 323 (arbitrary-generator arbitrary-cdr)) 324 (lambda (p gen) 325 (coarbitrary arbitrary-car 326 (car p) 327 (coarbitrary arbitrary-cdr 328 (cdr p) gen))))) 329 330; a tuple is just a non-uniform list 331(define (arbitrary-tuple . arbitrary-els) 332 (make-arbitrary (apply lift->generator 333 list 334 (map arbitrary-generator arbitrary-els)) 335 (lambda (lis gen) 336 (let recur ((arbitrary-els arbitrary-els) 337 (lis lis)) 338 (if (null? arbitrary-els) 339 gen 340 ((arbitrary-transformer (car arbitrary-els)) 341 (car lis) 342 (recur (cdr arbitrary-els) 343 (cdr lis)))))))) 344 345(define (arbitrary-record construct accessors . arbitrary-els) 346 (make-arbitrary (apply lift->generator 347 construct 348 (map arbitrary-generator arbitrary-els)) 349 (lambda (rec gen) 350 (let recur ((arbitrary-els arbitrary-els) 351 (lis (map (lambda (accessor) (accessor rec)) accessors))) 352 (if (null? arbitrary-els) 353 gen 354 ((arbitrary-transformer (car arbitrary-els)) 355 (car lis) 356 (recur (cdr arbitrary-els) 357 (cdr lis)))))))) 358 359(define (arbitrary-sequence min-length choose-sequence sequence->list arbitrary-el) 360 (make-arbitrary (sized 361 (lambda (n) 362 (>>= (choose-integer min-length (+ n min-length)) 363 (lambda (length) 364 (choose-sequence (arbitrary-generator arbitrary-el) length))))) 365 (lambda (seq gen) 366 (let recur ((lis (sequence->list seq))) 367 (if (null? lis) 368 (variant 0 gen) 369 ((arbitrary-transformer arbitrary-el) 370 (car lis) 371 (variant 1 (recur (cdr lis))))))))) 372 373(define (arbitrary-list arbitrary-el) 374 (arbitrary-sequence 0 choose-list values arbitrary-el)) 375 376(define (arbitrary-nonempty-list arbitrary-el) 377 (arbitrary-sequence 1 choose-list values arbitrary-el)) 378 379(define (arbitrary-vector arbitrary-el) 380 (arbitrary-sequence 0 choose-vector vector->list arbitrary-el)) 381 382(define arbitrary-ascii-string 383 (arbitrary-sequence 0 choose-string string->list arbitrary-ascii-char)) 384 385(define arbitrary-printable-ascii-string 386 (arbitrary-sequence 0 choose-string string->list arbitrary-printable-ascii-char)) 387 388(define arbitrary-string 389 (arbitrary-sequence 0 choose-string string->list arbitrary-char)) 390 391(define arbitrary-symbol 392 (arbitrary-sequence 0 choose-symbol 393 (lambda (symbol) 394 (string->list (symbol->string symbol))) 395 arbitrary-ascii-letter)) 396 397(define (arbitrary-procedure arbitrary-result . arbitrary-args) 398 (let ((arbitrary-arg-tuple (apply arbitrary-tuple arbitrary-args))) 399 (make-arbitrary (promote 400 (lambda args 401 ((arbitrary-transformer arbitrary-arg-tuple) 402 args 403 (arbitrary-generator arbitrary-result)))) 404 (lambda (proc gen) 405 (>>= (arbitrary-generator arbitrary-arg-tuple) 406 (lambda (args) 407 ((arbitrary-transformer arbitrary-result) 408 (apply proc args) 409 gen))))))) 410 411 412(define-record-type :property 413 (make-property proc arg-names args) 414 property? 415 (proc property-proc) 416 (arg-names property-arg-names) 417 ;; (list (union arbitrary generator)) 418 (args property-args)) 419 420(define-syntax property 421 (syntax-rules () 422 ((property ((?id ?gen) ...) ?body0 ?body1 ...) 423 (make-property (lambda (?id ...) 424 ?body0 ?body1 ...) 425 '(?id ...) 426 (list ?gen ...))))) 427 428(define-record-type :result 429 (make-result ok stamp arguments-list) 430 check-result? 431 ;; () = unknown, #t, #f 432 (ok result-ok) 433 (stamp result-stamp) 434 ;; (list (list (pair (union #f symbol) value))) 435 (arguments-list result-arguments-list)) 436 437(define (result-with-ok res ok) 438 (make-result ok 439 (result-stamp res) 440 (result-arguments-list res))) 441 442(define (result-add-stamp res stamp) 443 (make-result (result-ok res) 444 (cons stamp (result-stamp res)) 445 (result-arguments-list res))) 446 447; result (list (pair (union #f symbol) value)) -> result 448(define (result-add-arguments res args) 449 (make-result (result-ok res) 450 (result-stamp res) 451 (cons args (result-arguments-list res)))) 452 453(define nothing 454 (make-result '() '() '())) 455 456(define exception-result 457 (make-result #f '() '())) 458 459; A testable value is one of the following: 460; - a :property object 461; - a boolean 462; - a result record 463; - a generator of a result record 464 465(define (coerce->result-generator thing) 466 (cond 467 ((property? thing) 468 (for-all/names (property-proc thing) 469 (property-arg-names thing) 470 (property-args thing))) 471 ((boolean? thing) (return (result-with-ok nothing thing))) 472 ((check-result? thing) (return thing)) 473 ((generator? thing) thing) 474 (else 475 (assertion-violation 'coerce->result-generator 476 "cannot be coerced to a result generator" 477 thing)))) 478 479(define (coerce->generator thing) 480 (cond 481 ((generator? thing) thing) 482 ((arbitrary? thing) (arbitrary-generator thing)) 483 (else 484 (assertion-violation 'coerce->generator 485 "cannot be coerced to a generator" thing)))) 486 487(define (for-all proc . args) 488 (>>= (sequence (map coerce->generator args)) 489 (lambda (args) 490 (>>= (with-handlers ((exn:fail? 491 (lambda (_) 492 (return exception-result)))) 493 (coerce->result-generator (apply proc args))) 494 (lambda (res) 495 (return (result-add-arguments res 496 (map (lambda (arg) (cons #f arg)) args)))))))) 497 498(define (for-all/names proc arg-names args) 499 (>>= (sequence (map coerce->generator args)) 500 (lambda (args) 501 (>>= (with-handlers ((exn:fail? 502 (lambda (_) 503 (return exception-result)))) 504 (coerce->result-generator (apply proc args))) 505 (lambda (res) 506 (return (result-add-arguments res (map cons arg-names args)))))))) 507 508(define-syntax ==> 509 (syntax-rules () 510 ((==> ?bool ?prop) 511 (if ?bool 512 ?prop 513 (return nothing))))) 514 515(define (label str testable) 516 (>>= (coerce->result-generator testable) 517 (lambda (res) 518 (return (result-add-stamp res str))))) 519 520(define-syntax classify 521 (syntax-rules () 522 ((classify ?really? ?str ?testable) 523 (let ((testable ?testable)) 524 (if ?really? 525 (label ?str testable) 526 testable))))) 527 528(define-syntax trivial 529 (syntax-rules () 530 ((trivial ?really? ?testable) 531 (classify ?really? "trivial" ?testable)))) 532 533(define (collect lbl testable) 534 (label (external-representation lbl) testable)) 535 536(define (external-representation obj) 537 (let ((port (make-string-output-port))) 538 (write obj port) 539 (string-output-port-output port))) 540 541; Running the whole shebang 542 543(define-record-type :config 544 (make-config max-test max-fail size print-every) 545 config? 546 (max-test config-max-test) 547 (max-fail config-max-fail) 548 (size config-size) 549 (print-every config-print-every)) 550 551(define quick 552 (make-config 100 553 1000 554 (lambda (n) 555 (+ 3 (quotient n 2))) 556 values)) 557 558(define verbose 559 (make-config 100 560 1000 561 (lambda (n) 562 (+ 3 (quotient n 2))) 563 (lambda (n args) 564 (display n) 565 (display ":") 566 (newline) 567 (for-each (lambda (arg) 568 (display arg) 569 (newline)) 570 args)))) 571 572(define (check-results config prop) 573 (let ((rgen (make-random-generator 0))) 574 (tests config (coerce->result-generator prop) rgen 0 0 '()))) 575 576(define (check config prop) 577 (call-with-values 578 (lambda () 579 (check-results config prop)) 580 report-result)) 581 582(define (quickcheck-results prop) 583 (check-results quick prop)) 584 585(define (quickcheck prop) 586 (check quick prop)) 587 588; returns three values: 589; - ntest 590; - stamps 591; - #t for success, #f for exhausted, result for failure 592 593(define (tests config gen rgen ntest nfail stamps) 594 (let loop ((rgen rgen) 595 (ntest ntest) 596 (nfail nfail) 597 (stamps stamps)) 598 (cond 599 ((= ntest (config-max-test config)) 600 (values ntest stamps #t)) 601 ((= ntest (config-max-fail config)) 602 (values ntest stamps #f)) 603 (else 604 (call-with-values 605 (lambda () 606 (random-generator-split rgen)) 607 (lambda (rgen1 rgen2) 608 (let ((result (generate ((config-size config) ntest) rgen2 gen))) 609 ((config-print-every config) ntest (result-arguments-list result)) 610 (case (result-ok result) 611 ((()) (loop rgen1 ntest (+ 1 nfail) stamps)) 612 ((#t) (loop rgen1 (+ 1 ntest) nfail (cons (result-stamp result) stamps))) 613 ((#f) 614 (values ntest stamps result)))))))))) 615 616(define (report-result ntest stamps maybe-result) 617 (case maybe-result 618 ((#t) 619 (done "OK, passed" ntest stamps)) 620 ((#f) 621 (done "Arguments exhausted after" ntest stamps)) 622 (else 623 (display "Falsifiable, after ") 624 (display ntest) 625 (display " tests:") 626 (newline) 627 (for-each write-arguments 628 (result-arguments-list maybe-result))))) 629 630; (pair (union #f symbol) value) 631(define (write-argument arg) 632 (if (car arg) 633 (begin 634 (display (car arg)) 635 (display " = ")) 636 (values)) 637 (write (cdr arg))) 638 639; (list (pair (union #f symbol) value)) 640(define (write-arguments args) 641 (if (pair? args) 642 (begin 643 (write-argument (car args)) 644 (for-each (lambda (arg) 645 (display " ") 646 (write-argument arg)) 647 (cdr args)) 648 (newline)) 649 (values))) 650 651(define (done mesg ntest stamps) 652 (display mesg) 653 (display " ") 654 (display ntest) 655 (display " tests") 656 (let* ((sorted (list-sort stamp<? (filter pair? stamps))) 657 (grouped (group-sizes sorted)) 658 (sorted (list-sort (lambda (p1 p2) 659 (< (car p1) (car p2))) 660 grouped)) 661 (entries (map (lambda (p) 662 (let ((n (car p)) 663 (lis (cdr p))) 664 (string-append (number->string (quotient (* 100 n) ntest)) 665 "% " 666 (intersperse ", " lis)))) 667 (reverse sorted)))) 668 (cond 669 ((null? entries) 670 (display ".") 671 (newline)) 672 ((null? (cdr entries)) 673 (display " (") 674 (display (car entries)) 675 (display ").") 676 (newline)) 677 (else 678 (display ".") (newline) 679 (for-each (lambda (entry) 680 (display entry) 681 (display ".") 682 (newline)) 683 entries))))) 684 685(define (group-sizes lis) 686 (if (null? lis) 687 '() 688 (let loop ((current (car lis)) 689 (size 1) 690 (lis (cdr lis)) 691 (rev '())) 692 (cond 693 ((null? lis) 694 (reverse (cons (cons size current) rev))) 695 ((equal? current (car lis)) 696 (loop current (+ 1 size) (cdr lis) rev)) 697 (else 698 (loop (car lis) 1 (cdr lis) (cons (cons size current) rev))))))) 699 700(define (stamp<? s1 s2) 701 (cond 702 ((null? s1) 703 (pair? s1)) 704 ((null? s2) 705 #t) 706 ((string<? (car s1) (car s2)) 707 #t) 708 ((string=? (car s1) (car s2)) 709 (stamp<? (cdr s1) (cdr s2))) 710 (else #f))) 711 712 713(define (intersperse del lis) 714 (if (null? lis) 715 "" 716 (string-append (car lis) 717 (let recur ((lis (cdr lis))) 718 (if (null? lis) 719 "" 720 (string-append del 721 (recur (cdr lis)))))))) 722