github.com/leanovate/gopter@v0.2.9/formated_reporter.go (about)

     1  package gopter
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"strings"
     8  	"unicode"
     9  )
    10  
    11  const newLine = "\n"
    12  
    13  // FormatedReporter reports test results in a human readable manager.
    14  type FormatedReporter struct {
    15  	verbose bool
    16  	width   int
    17  	output  io.Writer
    18  }
    19  
    20  // NewFormatedReporter create a new formated reporter
    21  // verbose toggles verbose output of the property results
    22  // width is the maximal width per line
    23  // output is the writer were the report will be written to
    24  func NewFormatedReporter(verbose bool, width int, output io.Writer) Reporter {
    25  	return &FormatedReporter{
    26  		verbose: verbose,
    27  		width:   width,
    28  		output:  output,
    29  	}
    30  }
    31  
    32  // ConsoleReporter creates a FormatedReporter writing to the console (i.e. stdout)
    33  func ConsoleReporter(verbose bool) Reporter {
    34  	return NewFormatedReporter(verbose, 75, os.Stdout)
    35  }
    36  
    37  // ReportTestResult reports a single property result
    38  func (r *FormatedReporter) ReportTestResult(propName string, result *TestResult) {
    39  	if result.Passed() {
    40  		fmt.Fprintln(r.output, r.formatLines(fmt.Sprintf("+ %s: %s", propName, r.reportResult(result)), "", ""))
    41  	} else {
    42  		fmt.Fprintln(r.output, r.formatLines(fmt.Sprintf("! %s: %s", propName, r.reportResult(result)), "", ""))
    43  	}
    44  }
    45  
    46  func (r *FormatedReporter) reportResult(result *TestResult) string {
    47  	status := ""
    48  	switch result.Status {
    49  	case TestProved:
    50  		status = "OK, proved property.\n" + r.reportPropArgs(result.Args)
    51  	case TestPassed:
    52  		status = fmt.Sprintf("OK, passed %d tests.", result.Succeeded)
    53  	case TestFailed:
    54  		status = fmt.Sprintf("Falsified after %d passed tests.\n%s%s", result.Succeeded, r.reportLabels(result.Labels), r.reportPropArgs(result.Args))
    55  	case TestExhausted:
    56  		status = fmt.Sprintf("Gave up after only %d passed tests. %d tests were discarded.", result.Succeeded, result.Discarded)
    57  	case TestError:
    58  		if r.verbose {
    59  			status = fmt.Sprintf("Error on property evaluation after %d passed tests: %s\n%s\n%s", result.Succeeded, result.Error.Error(), result.ErrorStack, r.reportPropArgs(result.Args))
    60  		} else {
    61  			status = fmt.Sprintf("Error on property evaluation after %d passed tests: %s\n%s", result.Succeeded, result.Error.Error(), r.reportPropArgs(result.Args))
    62  		}
    63  	}
    64  
    65  	if r.verbose {
    66  		return concatLines(status, fmt.Sprintf("Elapsed time: %s", result.Time.String()))
    67  	}
    68  	return status
    69  }
    70  
    71  func (r *FormatedReporter) reportLabels(labels []string) string {
    72  	if labels != nil && len(labels) > 0 {
    73  		return fmt.Sprintf("> Labels of failing property: %s\n", strings.Join(labels, newLine))
    74  	}
    75  	return ""
    76  }
    77  
    78  func (r *FormatedReporter) reportPropArgs(p PropArgs) string {
    79  	result := ""
    80  	for i, arg := range p {
    81  		if result != "" {
    82  			result += newLine
    83  		}
    84  		result += r.reportPropArg(i, arg)
    85  	}
    86  	return result
    87  }
    88  
    89  func (r *FormatedReporter) reportPropArg(idx int, propArg *PropArg) string {
    90  	label := propArg.Label
    91  	if label == "" {
    92  		label = fmt.Sprintf("ARG_%d", idx)
    93  	}
    94  	result := fmt.Sprintf("%s: %s", label, propArg.ArgFormatted)
    95  	if propArg.Shrinks > 0 {
    96  		result += fmt.Sprintf("\n%s_ORIGINAL (%d shrinks): %s", label, propArg.Shrinks, propArg.OrigArgFormatted)
    97  	}
    98  
    99  	return result
   100  }
   101  
   102  func (r *FormatedReporter) formatLines(str, lead, trail string) string {
   103  	result := ""
   104  	for _, line := range strings.Split(str, "\n") {
   105  		if result != "" {
   106  			result += newLine
   107  		}
   108  		result += r.breakLine(lead+line+trail, "  ")
   109  	}
   110  	return result
   111  }
   112  
   113  func (r *FormatedReporter) breakLine(str, lead string) string {
   114  	if len(str) <= r.width {
   115  		return str
   116  	}
   117  
   118  	result := ""
   119  	for len(str) > r.width {
   120  		idx := strings.LastIndexFunc(str[0:r.width], func(ch rune) bool {
   121  			return unicode.IsSpace(ch)
   122  		})
   123  		if idx <= 0 {
   124  			idx = r.width
   125  		}
   126  		result += str[0:idx] + "\n" + lead
   127  		str = str[idx:]
   128  	}
   129  	result += str
   130  	return result
   131  }
   132  
   133  func concatLines(strs ...string) string {
   134  	result := ""
   135  	for _, str := range strs {
   136  		if str != "" {
   137  			if result != "" {
   138  				result += "\n"
   139  			}
   140  			result += str
   141  		}
   142  	}
   143  	return result
   144  }