github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/internal/cmdtest/test_cmd.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2017 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package cmdtest
    26  
    27  import (
    28  	"bufio"
    29  	"bytes"
    30  	"fmt"
    31  	"io"
    32  	"io/ioutil"
    33  	"os"
    34  	"os/exec"
    35  	"regexp"
    36  	"strings"
    37  	"sync"
    38  	"testing"
    39  	"text/template"
    40  	"time"
    41  
    42  	"github.com/docker/docker/pkg/reexec"
    43  )
    44  
    45  func NewTestCmd(t *testing.T, data interface{}) *TestCmd {
    46  	return &TestCmd{T: t, Data: data}
    47  }
    48  
    49  type TestCmd struct {
    50  //为方便起见,所有测试方法都可用。
    51  	*testing.T
    52  
    53  	Func    template.FuncMap
    54  	Data    interface{}
    55  	Cleanup func()
    56  
    57  	cmd    *exec.Cmd
    58  	stdout *bufio.Reader
    59  	stdin  io.WriteCloser
    60  	stderr *testlogger
    61  }
    62  
    63  //运行exec的当前二进制文件,使用argv[0]作为名称,它将触发
    64  //该名称的reexec init函数(例如,在cmd/geth/run\test.go中的“geth test”)。
    65  func (tt *TestCmd) Run(name string, args ...string) {
    66  	tt.stderr = &testlogger{t: tt.T}
    67  	tt.cmd = &exec.Cmd{
    68  		Path:   reexec.Self(),
    69  		Args:   append([]string{name}, args...),
    70  		Stderr: tt.stderr,
    71  	}
    72  	stdout, err := tt.cmd.StdoutPipe()
    73  	if err != nil {
    74  		tt.Fatal(err)
    75  	}
    76  	tt.stdout = bufio.NewReader(stdout)
    77  	if tt.stdin, err = tt.cmd.StdinPipe(); err != nil {
    78  		tt.Fatal(err)
    79  	}
    80  	if err := tt.cmd.Start(); err != nil {
    81  		tt.Fatal(err)
    82  	}
    83  }
    84  
    85  //inputline将给定文本写入childs stdin。
    86  //这种方法也可以从期望模板调用,例如:
    87  //
    88  //geth.expect(`passphrase:.inputline“password”`)
    89  func (tt *TestCmd) InputLine(s string) string {
    90  	io.WriteString(tt.stdin, s+"\n")
    91  	return ""
    92  }
    93  
    94  func (tt *TestCmd) SetTemplateFunc(name string, fn interface{}) {
    95  	if tt.Func == nil {
    96  		tt.Func = make(map[string]interface{})
    97  	}
    98  	tt.Func[name] = fn
    99  }
   100  
   101  //Expect将其参数作为模板运行,然后期望
   102  //子进程在5s内输出模板的结果。
   103  //
   104  //如果模板以换行符开头,则将删除换行符
   105  //匹配前。
   106  func (tt *TestCmd) Expect(tplsource string) {
   107  //通过运行模板生成预期的输出。
   108  	tpl := template.Must(template.New("").Funcs(tt.Func).Parse(tplsource))
   109  	wantbuf := new(bytes.Buffer)
   110  	if err := tpl.Execute(wantbuf, tt.Data); err != nil {
   111  		panic(err)
   112  	}
   113  //在开始处只修剪一条新行。这使得测试看起来
   114  //更好,因为所有期望的字符串都在第0列。
   115  	want := bytes.TrimPrefix(wantbuf.Bytes(), []byte("\n"))
   116  	if err := tt.matchExactOutput(want); err != nil {
   117  		tt.Fatal(err)
   118  	}
   119  	tt.Logf("Matched stdout text:\n%s", want)
   120  }
   121  
   122  func (tt *TestCmd) matchExactOutput(want []byte) error {
   123  	buf := make([]byte, len(want))
   124  	n := 0
   125  	tt.withKillTimeout(func() { n, _ = io.ReadFull(tt.stdout, buf) })
   126  	buf = buf[:n]
   127  	if n < len(want) || !bytes.Equal(buf, want) {
   128  //在不匹配的情况下获取任何额外的缓冲输出
   129  //因为它可能有助于调试。
   130  		buf = append(buf, make([]byte, tt.stdout.Buffered())...)
   131  		tt.stdout.Read(buf[n:])
   132  //找到不匹配的位置。
   133  		for i := 0; i < n; i++ {
   134  			if want[i] != buf[i] {
   135  				return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s",
   136  					buf[:i], buf[i:n], want)
   137  			}
   138  		}
   139  		if n < len(want) {
   140  			return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
   141  				buf, want[:n], want[n:])
   142  		}
   143  	}
   144  	return nil
   145  }
   146  
   147  //expectregexp要求子进程输出与
   148  //在5s内给出正则表达式。
   149  //
   150  //注意,任意数量的输出可能被
   151  //正则表达式。这通常意味着不能使用expect
   152  //在expectregexp之后。
   153  func (tt *TestCmd) ExpectRegexp(regex string) (*regexp.Regexp, []string) {
   154  	regex = strings.TrimPrefix(regex, "\n")
   155  	var (
   156  		re      = regexp.MustCompile(regex)
   157  		rtee    = &runeTee{in: tt.stdout}
   158  		matches []int
   159  	)
   160  	tt.withKillTimeout(func() { matches = re.FindReaderSubmatchIndex(rtee) })
   161  	output := rtee.buf.Bytes()
   162  	if matches == nil {
   163  		tt.Fatalf("Output did not match:\n---------------- (stdout text)\n%s\n---------------- (regular expression)\n%s",
   164  			output, regex)
   165  		return re, nil
   166  	}
   167  	tt.Logf("Matched stdout text:\n%s", output)
   168  	var submatches []string
   169  	for i := 0; i < len(matches); i += 2 {
   170  		submatch := string(output[matches[i]:matches[i+1]])
   171  		submatches = append(submatches, submatch)
   172  	}
   173  	return re, submatches
   174  }
   175  
   176  //expectexit期望子进程在5秒内退出
   177  //在stdout上打印任何附加文本。
   178  func (tt *TestCmd) ExpectExit() {
   179  	var output []byte
   180  	tt.withKillTimeout(func() {
   181  		output, _ = ioutil.ReadAll(tt.stdout)
   182  	})
   183  	tt.WaitExit()
   184  	if tt.Cleanup != nil {
   185  		tt.Cleanup()
   186  	}
   187  	if len(output) > 0 {
   188  		tt.Errorf("Unmatched stdout text:\n%s", output)
   189  	}
   190  }
   191  
   192  func (tt *TestCmd) WaitExit() {
   193  	tt.cmd.Wait()
   194  }
   195  
   196  func (tt *TestCmd) Interrupt() {
   197  	tt.cmd.Process.Signal(os.Interrupt)
   198  }
   199  
   200  //stderrtext返回到目前为止写入的任何stderr输出。
   201  //返回的文本保留Expectexit之后的所有日志行
   202  //返回。
   203  func (tt *TestCmd) StderrText() string {
   204  	tt.stderr.mu.Lock()
   205  	defer tt.stderr.mu.Unlock()
   206  	return tt.stderr.buf.String()
   207  }
   208  
   209  func (tt *TestCmd) CloseStdin() {
   210  	tt.stdin.Close()
   211  }
   212  
   213  func (tt *TestCmd) Kill() {
   214  	tt.cmd.Process.Kill()
   215  	if tt.Cleanup != nil {
   216  		tt.Cleanup()
   217  	}
   218  }
   219  
   220  func (tt *TestCmd) withKillTimeout(fn func()) {
   221  	timeout := time.AfterFunc(5*time.Second, func() {
   222  		tt.Log("killing the child process (timeout)")
   223  		tt.Kill()
   224  	})
   225  	defer timeout.Stop()
   226  	fn()
   227  }
   228  
   229  //测试记录器通过t.log记录所有写入的行,并且
   230  //收集以备日后检查。
   231  type testlogger struct {
   232  	t   *testing.T
   233  	mu  sync.Mutex
   234  	buf bytes.Buffer
   235  }
   236  
   237  func (tl *testlogger) Write(b []byte) (n int, err error) {
   238  	lines := bytes.Split(b, []byte("\n"))
   239  	for _, line := range lines {
   240  		if len(line) > 0 {
   241  			tl.t.Logf("(stderr) %s", line)
   242  		}
   243  	}
   244  	tl.mu.Lock()
   245  	tl.buf.Write(b)
   246  	tl.mu.Unlock()
   247  	return len(b), err
   248  }
   249  
   250  //runetee将通过它读取的文本收集到buf中。
   251  type runeTee struct {
   252  	in interface {
   253  		io.Reader
   254  		io.ByteReader
   255  		io.RuneReader
   256  	}
   257  	buf bytes.Buffer
   258  }
   259  
   260  func (rtee *runeTee) Read(b []byte) (n int, err error) {
   261  	n, err = rtee.in.Read(b)
   262  	rtee.buf.Write(b[:n])
   263  	return n, err
   264  }
   265  
   266  func (rtee *runeTee) ReadRune() (r rune, size int, err error) {
   267  	r, size, err = rtee.in.ReadRune()
   268  	if err == nil {
   269  		rtee.buf.WriteRune(r)
   270  	}
   271  	return r, size, err
   272  }
   273  
   274  func (rtee *runeTee) ReadByte() (b byte, err error) {
   275  	b, err = rtee.in.ReadByte()
   276  	if err == nil {
   277  		rtee.buf.WriteByte(b)
   278  	}
   279  	return b, err
   280  }