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 }