From 45ed7de654751f962e5c6d493a18933dcea604ec Mon Sep 17 00:00:00 2001 From: Ivan Kirichenko Date: Mon, 29 Dec 2014 14:26:07 +0700 Subject: [PATCH] - Moved outputWriter to reporter.go - outputWriter is now interface - Old outputWriter implementation is now plainWriter - Added some basic functionality to xunitWriter --- check.go | 87 +------------------------ reporter.go | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 84 deletions(-) create mode 100644 reporter.go diff --git a/check.go b/check.go index ca8c0f9..9465cd6 100644 --- a/check.go +++ b/check.go @@ -509,7 +509,7 @@ type suiteRunner struct { tracker *resultTracker tempDir *tempDir keepDir bool - output *outputWriter + output outputWriter reportedProblemLast bool benchTime time.Duration benchMem bool @@ -545,7 +545,7 @@ func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner { runner := &suiteRunner{ suite: suite, - output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose), + output: newPlainWriter(conf.Output, conf.Stream, conf.Verbose), tracker: newResultTracker(), benchTime: conf.BenchmarkTime, benchMem: conf.BenchmarkMem, @@ -632,7 +632,7 @@ func (runner *suiteRunner) run() *Result { // goroutine with the provided dispatcher for running it. func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { var logw io.Writer - if runner.output.Stream { + if runner.output.StreamEnabled() { logw = runner.output } if logb == nil { @@ -862,84 +862,3 @@ func (runner *suiteRunner) reportCallDone(c *C) { runner.output.WriteCallSuccess("MISS", c) } } - -// ----------------------------------------------------------------------- -// Output writer manages atomic output writing according to settings. - -type outputWriter struct { - m sync.Mutex - writer io.Writer - wroteCallProblemLast bool - Stream bool - Verbose bool -} - -func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter { - return &outputWriter{writer: writer, Stream: stream, Verbose: verbose} -} - -func (ow *outputWriter) Write(content []byte) (n int, err error) { - ow.m.Lock() - n, err = ow.writer.Write(content) - ow.m.Unlock() - return -} - -func (ow *outputWriter) WriteCallStarted(label string, c *C) { - if ow.Stream { - header := renderCallHeader(label, c, "", "\n") - ow.m.Lock() - ow.writer.Write([]byte(header)) - ow.m.Unlock() - } -} - -func (ow *outputWriter) WriteCallProblem(label string, c *C) { - var prefix string - if !ow.Stream { - prefix = "\n-----------------------------------" + - "-----------------------------------\n" - } - header := renderCallHeader(label, c, prefix, "\n\n") - ow.m.Lock() - ow.wroteCallProblemLast = true - ow.writer.Write([]byte(header)) - if !ow.Stream { - c.logb.WriteTo(ow.writer) - } - ow.m.Unlock() -} - -func (ow *outputWriter) WriteCallSuccess(label string, c *C) { - if ow.Stream || (ow.Verbose && c.kind == testKd) { - // TODO Use a buffer here. - var suffix string - if c.reason != "" { - suffix = " (" + c.reason + ")" - } - if c.status == succeededSt { - suffix += "\t" + c.timerString() - } - suffix += "\n" - if ow.Stream { - suffix += "\n" - } - header := renderCallHeader(label, c, "", suffix) - ow.m.Lock() - // Resist temptation of using line as prefix above due to race. - if !ow.Stream && ow.wroteCallProblemLast { - header = "\n-----------------------------------" + - "-----------------------------------\n" + - header - } - ow.wroteCallProblemLast = false - ow.writer.Write([]byte(header)) - ow.m.Unlock() - } -} - -func renderCallHeader(label string, c *C, prefix, suffix string) string { - pc := c.method.PC() - return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc), - niceFuncName(pc), suffix) -} diff --git a/reporter.go b/reporter.go new file mode 100644 index 0000000..f42b4cb --- /dev/null +++ b/reporter.go @@ -0,0 +1,178 @@ +package check + +import ( + "bytes" + "fmt" + "io" + "sync" + "time" +) + +// TODO: start test suite +type outputWriter interface { + Write(content []byte) (n int, err error) + WriteCallStarted(label string, c *C) + WriteCallProblem(label string, c *C) + WriteCallSuccess(label string, c *C) + StreamEnabled() bool +} + +/*************** Plain writer *****************/ + +type plainWriter struct { + outputWriter + m sync.Mutex + writer io.Writer + wroteCallProblemLast bool + stream bool + verbose bool +} + +func newPlainWriter(writer io.Writer, stream, verbose bool) *plainWriter { + return &plainWriter{writer: writer, stream: stream, verbose: verbose} +} + +func (w *plainWriter) StreamEnabled() bool { return w.stream } + +func (w *plainWriter) Write(content []byte) (n int, err error) { + w.m.Lock() + n, err = w.writer.Write(content) + w.m.Unlock() + return +} + +func (w *plainWriter) WriteCallStarted(label string, c *C) { + if w.stream { + header := renderCallHeader(label, c, "", "\n") + w.m.Lock() + w.writer.Write([]byte(header)) + w.m.Unlock() + } +} + +func (w *plainWriter) WriteCallProblem(label string, c *C) { + var prefix string + if !w.stream { + prefix = "\n-----------------------------------" + + "-----------------------------------\n" + } + header := renderCallHeader(label, c, prefix, "\n\n") + w.m.Lock() + w.wroteCallProblemLast = true + w.writer.Write([]byte(header)) + if !w.stream { + c.logb.WriteTo(w.writer) + } + w.m.Unlock() +} + +func (w *plainWriter) WriteCallSuccess(label string, c *C) { + if w.stream || (w.verbose && c.kind == testKd) { + // TODO Use a buffer here. + var suffix string + if c.reason != "" { + suffix = " (" + c.reason + ")" + } + if c.status == succeededSt { + suffix += "\t" + c.timerString() + } + suffix += "\n" + if w.stream { + suffix += "\n" + } + header := renderCallHeader(label, c, "", suffix) + w.m.Lock() + // Resist temptation of using line as prefix above due to race. + if !w.stream && w.wroteCallProblemLast { + header = "\n-----------------------------------" + + "-----------------------------------\n" + + header + } + w.wroteCallProblemLast = false + w.writer.Write([]byte(header)) + w.m.Unlock() + } +} + +func renderCallHeader(label string, c *C, prefix, suffix string) string { + pc := c.method.PC() + return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc), + niceFuncName(pc), suffix) +} + +/*************** xUnit writer *****************/ +// TODO: Write mrthod can collect data for std-out +type xunitReport struct { + suites []xunitSuite `xml:"testsuites>testsuite,omitempty"` +} + +type xunitSuite struct { + Package string `xml:"package,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + Classname string `xml:"classname,attr,omitempty"` + Time float64 `xml:"time,attr"` + Timestamp time.Time `xml:"timestamp,attr"` + + Tests uint64 `xml:"tests,attr"` + Failures uint64 `xml:"failures,attr"` + Errors uint64 `xml:"errors,attr"` + Skipped uint64 `xml:"skipped,attr"` + + Properties []xunitSuiteProperty `xml:"properties>property"` //TODO: test + Testcases []xunitTestcase `xml:"testcase"` + + SystemOut string `xml:"system-out,omitempty"` + SystemErr string `xml:"system-err,omitempty"` +} + +type xunitSuiteProperty struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} + +type xunitTestcase struct { + Name string `xml:"name,attr,omitempty"` + Classname string `xml:"classname,attr,omitempty"` + Time float64 `xml:"time,attr"` + Failure *xunitTestcaseResult `xml:"failure,omitempty"` + Error *xunitTestcaseResult `xml:"error,omitempty"` +} + +type xunitTestcaseResult struct { + Message string `xml:"message,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Value string `xml:",chardata"` +} + +type xunitWriter struct { + outputWriter + m sync.Mutex + writer io.Writer + stream bool + verbose bool + + systemOut io.Writer +} + +func newXunitWriter(writer io.Writer, stream, verbose bool) *plainWriter { + return &xunitWriter{ + writer: writer, + systemOut: bytes.Buffer{}, + stream: stream, + verbose: verbose, + } +} + +func (w *xunitWriter) GetReport() string { +} + +func (w *xunitWriter) Write(content []byte) (n int, err error) { + w.m.Lock() + n, err = w.systemOut.Write(content) + w.m.Unlock() + return +} +func (w *xunitWriter) WriteCallStarted(label string, c *C) {} +func (w *xunitWriter) WriteCallProblem(label string, c *C) {} +func (w *xunitWriter) WriteCallSuccess(label string, c *C) {} +func (w *xunitWriter) StreamEnabled() bool {}