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 }