Home | History | Annotate | Download | only in testing
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package testing
      6 
      7 import (
      8 	"bytes"
      9 	"fmt"
     10 	"io"
     11 	"os"
     12 	"sort"
     13 	"strings"
     14 	"time"
     15 )
     16 
     17 type InternalExample struct {
     18 	Name      string
     19 	F         func()
     20 	Output    string
     21 	Unordered bool
     22 }
     23 
     24 // An internal function but exported because it is cross-package; part of the implementation
     25 // of the "go test" command.
     26 func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
     27 	_, ok = runExamples(matchString, examples)
     28 	return ok
     29 }
     30 
     31 func runExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ran, ok bool) {
     32 	ok = true
     33 
     34 	var eg InternalExample
     35 
     36 	for _, eg = range examples {
     37 		matched, err := matchString(*match, eg.Name)
     38 		if err != nil {
     39 			fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
     40 			os.Exit(1)
     41 		}
     42 		if !matched {
     43 			continue
     44 		}
     45 		ran = true
     46 		if !runExample(eg) {
     47 			ok = false
     48 		}
     49 	}
     50 
     51 	return ran, ok
     52 }
     53 
     54 func sortLines(output string) string {
     55 	lines := strings.Split(output, "\n")
     56 	sort.Strings(lines)
     57 	return strings.Join(lines, "\n")
     58 }
     59 
     60 func runExample(eg InternalExample) (ok bool) {
     61 	if *chatty {
     62 		fmt.Printf("=== RUN   %s\n", eg.Name)
     63 	}
     64 
     65 	// Capture stdout.
     66 	stdout := os.Stdout
     67 	r, w, err := os.Pipe()
     68 	if err != nil {
     69 		fmt.Fprintln(os.Stderr, err)
     70 		os.Exit(1)
     71 	}
     72 	os.Stdout = w
     73 	outC := make(chan string)
     74 	go func() {
     75 		var buf bytes.Buffer
     76 		_, err := io.Copy(&buf, r)
     77 		r.Close()
     78 		if err != nil {
     79 			fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
     80 			os.Exit(1)
     81 		}
     82 		outC <- buf.String()
     83 	}()
     84 
     85 	start := time.Now()
     86 	ok = true
     87 
     88 	// Clean up in a deferred call so we can recover if the example panics.
     89 	defer func() {
     90 		dstr := fmtDuration(time.Now().Sub(start))
     91 
     92 		// Close pipe, restore stdout, get output.
     93 		w.Close()
     94 		os.Stdout = stdout
     95 		out := <-outC
     96 
     97 		var fail string
     98 		err := recover()
     99 		got := strings.TrimSpace(out)
    100 		want := strings.TrimSpace(eg.Output)
    101 		if eg.Unordered {
    102 			if sortLines(got) != sortLines(want) && err == nil {
    103 				fail = fmt.Sprintf("got:\n%s\nwant (unordered):\n%s\n", out, eg.Output)
    104 			}
    105 		} else {
    106 			if got != want && err == nil {
    107 				fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", got, want)
    108 			}
    109 		}
    110 		if fail != "" || err != nil {
    111 			fmt.Printf("--- FAIL: %s (%s)\n%s", eg.Name, dstr, fail)
    112 			ok = false
    113 		} else if *chatty {
    114 			fmt.Printf("--- PASS: %s (%s)\n", eg.Name, dstr)
    115 		}
    116 		if err != nil {
    117 			panic(err)
    118 		}
    119 	}()
    120 
    121 	// Run example.
    122 	eg.F()
    123 	return
    124 }
    125