racket / rackunit

Other
18 stars 34 forks source link

expected / actual interaction with pretty-printing #91

Closed rfindler closed 6 years ago

rfindler commented 6 years ago

Sometimes, rackunit inserts newlines into expected and actual results when using check-equal? (which is great!) but the indentation has issues. For example, this program:

#lang racket
(require rackunit)
(check-equal?
 (make-list 10 'xfdjkalf)
 (make-list 11 'xfdjkalf))

produces this output:

--------------------
. FAILURE
name:       check-equal?
location:   unsaved-editor:3:0
actual:     '(xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf)
expected:   '(xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf
  xfdjkalf)
--------------------
> 

I think that the printer should either detect that there are newlines in the printed result and, in tht case, lead with a newline, or it should set various printing parameters so that everything is indented past the "expected". Probably the former is best! But maybe a hybrid, where you put that first newline in, but then indent everything by one space or something so that the words "expected" and "actual" stand out.

AlexKnauth commented 6 years ago

There are two ways we could do this:

  1. Make everything pretty-printed, and use a pretty version of the format function instead of format in check-info->string.
  2. Try set the pretty-print-print-line parameter to print the extra padding after the newline.

Strategy (1) can be accomplished with a function like this:

(require (rename-in racket/pretty [pretty-format pretty-string]))

;; exactly like format, but pretty (*not* the same as pretty-format from racket/pretty)
(define (pretty-format fmt . vs)
  (pretty-string (formatted fmt vs)))

(struct formatted (fmt vs)
  #:methods gen:custom-write
  [(define (write-proc this out mode)
     (apply fprintf out (formatted-fmt this) (formatted-vs this)))])

;; using it produces:
actual:     '(xfdjkalf
              xfdjkalf
              xfdjkalf
              xfdjkalf
              xfdjkalf
              xfdjkalf
              xfdjkalf
              xfdjkalf
              xfdjkalf
              xfdjkalf)

I also attempted strategy (2) with the function below, but the indentation wasn't right for any of the sub-expressions:

(define (call-with-pretty-padding pad thunk)
  ;; the old and new value of the parameter
  (define old-newline (pretty-print-print-line))
  (define (new-newline line-num out prev-len dest-cols)
    (+ (old-newline line-num out prev-len dest-cols)
       (if (zero? line-num)
           0
           (write-string pad out))))
  (parameterize ([pretty-print-print-line new-newline])
    (thunk)))

;; using it produces:
actual:     '(xfdjkalf
            xfdjkalf
            xfdjkalf
            xfdjkalf
            xfdjkalf
            xfdjkalf
            xfdjkalf
            xfdjkalf
            xfdjkalf
            xfdjkalf)
rfindler commented 6 years ago

I think that you could monitor the output at the port level to see if you see a newline and, if you do, restart the printing (but this time putting a newline after the word "expected:"), perhaps with a modest indentation (one or two characters) on each line.

As for the indenting-not-quite-right issue, I can't recall exactly how that goes either, but I think that I figured it out once before and used it in redex here:

https://github.com/racket/redex/blob/master/redex-lib/redex/private/reduction-semantics.rkt#L3140

maybe that helps?

AlexKnauth commented 6 years ago

Or, there's the string-indent function, already there for nested-info values. That seems simpler than either strategy.

rfindler commented 6 years ago

I don't know that function, but shame on me for going directly to the efficient solution before a need for it exists. :)

rfindler commented 6 years ago

Thanks!