codekingpro/portable-devtools
114k
1// Copyright 2010 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5package io_test6 7import (8 "bytes"9 "crypto/sha1"10 "errors"11 "fmt"12 . "io"13 "runtime"14 "strings"15 "testing"16 "time"17)18 19func TestMultiReader(t *testing.T) {20 var mr Reader21 var buf []byte22 nread := 023 withFooBar := func(tests func()) {24 r1 := strings.NewReader("foo ")25 r2 := strings.NewReader("")26 r3 := strings.NewReader("bar")27 mr = MultiReader(r1, r2, r3)28 buf = make([]byte, 20)29 tests()30 }31 expectRead := func(size int, expected string, eerr error) {32 nread++33 n, gerr := mr.Read(buf[0:size])34 if n != len(expected) {35 t.Errorf("#%d, expected %d bytes; got %d",36 nread, len(expected), n)37 }38 got := string(buf[0:n])39 if got != expected {40 t.Errorf("#%d, expected %q; got %q",41 nread, expected, got)42 }43 if gerr != eerr {44 t.Errorf("#%d, expected error %v; got %v",45 nread, eerr, gerr)46 }47 buf = buf[n:]48 }49 withFooBar(func() {50 expectRead(2, "fo", nil)51 expectRead(5, "o ", nil)52 expectRead(5, "bar", nil)53 expectRead(5, "", EOF)54 })55 withFooBar(func() {56 expectRead(4, "foo ", nil)57 expectRead(1, "b", nil)58 expectRead(3, "ar", nil)59 expectRead(1, "", EOF)60 })61 withFooBar(func() {62 expectRead(5, "foo ", nil)63 })64}65 66func TestMultiReaderAsWriterTo(t *testing.T) {67 mr := MultiReader(68 strings.NewReader("foo "),69 MultiReader( // Tickle the buffer reusing codepath70 strings.NewReader(""),71 strings.NewReader("bar"),72 ),73 )74 mrAsWriterTo, ok := mr.(WriterTo)75 if !ok {76 t.Fatalf("expected cast to WriterTo to succeed")77 }78 sink := &strings.Builder{}79 n, err := mrAsWriterTo.WriteTo(sink)80 if err != nil {81 t.Fatalf("expected no error; got %v", err)82 }83 if n != 7 {84 t.Errorf("expected read 7 bytes; got %d", n)85 }86 if result := sink.String(); result != "foo bar" {87 t.Errorf(`expected "foo bar"; got %q`, result)88 }89}90 91func TestMultiWriter(t *testing.T) {92 sink := new(bytes.Buffer)93 // Hide bytes.Buffer's WriteString method:94 testMultiWriter(t, struct {95 Writer96 fmt.Stringer97 }{sink, sink})98}99 100func TestMultiWriter_String(t *testing.T) {101 testMultiWriter(t, new(bytes.Buffer))102}103 104// Test that a multiWriter.WriteString calls results in at most 1 allocation,105// even if multiple targets don't support WriteString.106func TestMultiWriter_WriteStringSingleAlloc(t *testing.T) {107 var sink1, sink2 bytes.Buffer108 type simpleWriter struct { // hide bytes.Buffer's WriteString109 Writer110 }111 mw := MultiWriter(simpleWriter{&sink1}, simpleWriter{&sink2})112 allocs := int(testing.AllocsPerRun(1000, func() {113 WriteString(mw, "foo")114 }))115 if allocs != 1 {116 t.Errorf("num allocations = %d; want 1", allocs)117 }118}119 120type writeStringChecker struct{ called bool }121 122func (c *writeStringChecker) WriteString(s string) (n int, err error) {123 c.called = true124 return len(s), nil125}126 127func (c *writeStringChecker) Write(p []byte) (n int, err error) {128 return len(p), nil129}130 131func TestMultiWriter_StringCheckCall(t *testing.T) {132 var c writeStringChecker133 mw := MultiWriter(&c)134 WriteString(mw, "foo")135 if !c.called {136 t.Error("did not see WriteString call to writeStringChecker")137 }138}139 140func testMultiWriter(t *testing.T, sink interface {141 Writer142 fmt.Stringer143}) {144 sha1 := sha1.New()145 mw := MultiWriter(sha1, sink)146 147 sourceString := "My input text."148 source := strings.NewReader(sourceString)149 written, err := Copy(mw, source)150 151 if written != int64(len(sourceString)) {152 t.Errorf("short write of %d, not %d", written, len(sourceString))153 }154 155 if err != nil {156 t.Errorf("unexpected error: %v", err)157 }158 159 sha1hex := fmt.Sprintf("%x", sha1.Sum(nil))160 if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" {161 t.Error("incorrect sha1 value")162 }163 164 if sink.String() != sourceString {165 t.Errorf("expected %q; got %q", sourceString, sink.String())166 }167}168 169// writerFunc is a Writer implemented by the underlying func.170type writerFunc func(p []byte) (int, error)171 172func (f writerFunc) Write(p []byte) (int, error) {173 return f(p)174}175 176// Test that MultiWriter properly flattens chained multiWriters.177func TestMultiWriterSingleChainFlatten(t *testing.T) {178 pc := make([]uintptr, 1000) // 1000 should fit the full stack179 n := runtime.Callers(0, pc)180 var myDepth = callDepth(pc[:n])181 var writeDepth int // will contain the depth from which writerFunc.Writer was called182 var w Writer = MultiWriter(writerFunc(func(p []byte) (int, error) {183 n := runtime.Callers(1, pc)184 writeDepth += callDepth(pc[:n])185 return 0, nil186 }))187 188 mw := w189 // chain a bunch of multiWriters190 for i := 0; i < 100; i++ {191 mw = MultiWriter(w)192 }193 194 mw = MultiWriter(w, mw, w, mw)195 mw.Write(nil) // don't care about errors, just want to check the call-depth for Write196 197 if writeDepth != 4*(myDepth+2) { // 2 should be multiWriter.Write and writerFunc.Write198 t.Errorf("multiWriter did not flatten chained multiWriters: expected writeDepth %d, got %d",199 4*(myDepth+2), writeDepth)200 }201}202 203func TestMultiWriterError(t *testing.T) {204 f1 := writerFunc(func(p []byte) (int, error) {205 return len(p) / 2, ErrShortWrite206 })207 f2 := writerFunc(func(p []byte) (int, error) {208 t.Errorf("MultiWriter called f2.Write")209 return len(p), nil210 })211 w := MultiWriter(f1, f2)212 n, err := w.Write(make([]byte, 100))213 if n != 50 || err != ErrShortWrite {214 t.Errorf("Write = %d, %v, want 50, ErrShortWrite", n, err)215 }216}217 218// Test that MultiReader copies the input slice and is insulated from future modification.219func TestMultiReaderCopy(t *testing.T) {220 slice := []Reader{strings.NewReader("hello world")}221 r := MultiReader(slice...)222 slice[0] = nil223 data, err := ReadAll(r)224 if err != nil || string(data) != "hello world" {225 t.Errorf("ReadAll() = %q, %v, want %q, nil", data, err, "hello world")226 }227}228 229// Test that MultiWriter copies the input slice and is insulated from future modification.230func TestMultiWriterCopy(t *testing.T) {231 var buf strings.Builder232 slice := []Writer{&buf}233 w := MultiWriter(slice...)234 slice[0] = nil235 n, err := w.Write([]byte("hello world"))236 if err != nil || n != 11 {237 t.Errorf("Write(`hello world`) = %d, %v, want 11, nil", n, err)238 }239 if buf.String() != "hello world" {240 t.Errorf("buf.String() = %q, want %q", buf.String(), "hello world")241 }242}243 244// readerFunc is a Reader implemented by the underlying func.245type readerFunc func(p []byte) (int, error)246 247func (f readerFunc) Read(p []byte) (int, error) {248 return f(p)249}250 251// callDepth returns the logical call depth for the given PCs.252func callDepth(callers []uintptr) (depth int) {253 frames := runtime.CallersFrames(callers)254 more := true255 for more {256 _, more = frames.Next()257 depth++258 }259 return260}261 262// Test that MultiReader properly flattens chained multiReaders when Read is called263func TestMultiReaderFlatten(t *testing.T) {264 pc := make([]uintptr, 1000) // 1000 should fit the full stack265 n := runtime.Callers(0, pc)266 var myDepth = callDepth(pc[:n])267 var readDepth int // will contain the depth from which fakeReader.Read was called268 var r Reader = MultiReader(readerFunc(func(p []byte) (int, error) {269 n := runtime.Callers(1, pc)270 readDepth = callDepth(pc[:n])271 return 0, errors.New("irrelevant")272 }))273 274 // chain a bunch of multiReaders275 for i := 0; i < 100; i++ {276 r = MultiReader(r)277 }278 279 r.Read(nil) // don't care about errors, just want to check the call-depth for Read280 281 if readDepth != myDepth+2 { // 2 should be multiReader.Read and fakeReader.Read282 t.Errorf("multiReader did not flatten chained multiReaders: expected readDepth %d, got %d",283 myDepth+2, readDepth)284 }285}286 287// byteAndEOFReader is a Reader which reads one byte (the underlying288// byte) and EOF at once in its Read call.289type byteAndEOFReader byte290 291func (b byteAndEOFReader) Read(p []byte) (n int, err error) {292 if len(p) == 0 {293 // Read(0 bytes) is useless. We expect no such useless294 // calls in this test.295 panic("unexpected call")296 }297 p[0] = byte(b)298 return 1, EOF299}300 301// This used to yield bytes forever; issue 16795.302func TestMultiReaderSingleByteWithEOF(t *testing.T) {303 got, err := ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))304 if err != nil {305 t.Fatal(err)306 }307 const want = "ab"308 if string(got) != want {309 t.Errorf("got %q; want %q", got, want)310 }311}312 313// Test that a reader returning (n, EOF) at the end of a MultiReader314// chain continues to return EOF on its final read, rather than315// yielding a (0, EOF).316func TestMultiReaderFinalEOF(t *testing.T) {317 r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))318 buf := make([]byte, 2)319 n, err := r.Read(buf)320 if n != 1 || err != EOF {321 t.Errorf("got %v, %v; want 1, EOF", n, err)322 }323}324 325func TestMultiReaderFreesExhaustedReaders(t *testing.T) {326 var mr Reader327 closed := make(chan struct{})328 // The closure ensures that we don't have a live reference to buf1329 // on our stack after MultiReader is inlined (Issue 18819). This330 // is a work around for a limitation in liveness analysis.331 func() {332 buf1 := bytes.NewReader([]byte("foo"))333 buf2 := bytes.NewReader([]byte("bar"))334 mr = MultiReader(buf1, buf2)335 runtime.AddCleanup(buf1, func(ch chan struct{}) { close(ch) }, closed)336 }()337 338 buf := make([]byte, 4)339 if n, err := ReadFull(mr, buf); err != nil || string(buf) != "foob" {340 t.Fatalf(`ReadFull = %d (%q), %v; want 3, "foo", nil`, n, buf[:n], err)341 }342 343 runtime.GC()344 select {345 case <-closed:346 case <-time.After(5 * time.Second):347 t.Fatal("timeout waiting for collection of buf1")348 }349 350 if n, err := ReadFull(mr, buf[:2]); err != nil || string(buf[:2]) != "ar" {351 t.Fatalf(`ReadFull = %d (%q), %v; want 2, "ar", nil`, n, buf[:n], err)352 }353}354 355func TestInterleavedMultiReader(t *testing.T) {356 r1 := strings.NewReader("123")357 r2 := strings.NewReader("45678")358 359 mr1 := MultiReader(r1, r2)360 mr2 := MultiReader(mr1)361 362 buf := make([]byte, 4)363 364 // Have mr2 use mr1's []Readers.365 // Consume r1 (and clear it for GC to handle) and consume part of r2.366 n, err := ReadFull(mr2, buf)367 if got := string(buf[:n]); got != "1234" || err != nil {368 t.Errorf(`ReadFull(mr2) = (%q, %v), want ("1234", nil)`, got, err)369 }370 371 // Consume the rest of r2 via mr1.372 // This should not panic even though mr2 cleared r1.373 n, err = ReadFull(mr1, buf)374 if got := string(buf[:n]); got != "5678" || err != nil {375 t.Errorf(`ReadFull(mr1) = (%q, %v), want ("5678", nil)`, got, err)376 }377}378 