/* Copyright 2018 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package io import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" "syscall" "testing" ) func writeToPipe(namedPipe string, flaky bool, i int) { pipe, err := os.OpenFile(namedPipe, os.O_WRONLY, 0600) if err != nil { return } // The first two reads should never be consistent but all // subsequent reads should be outstr := fmt.Sprintf("Foobar %t", (i <= 0)) if flaky { outstr = fmt.Sprintf("Foobar %d", i) } pipe.Write([]byte(outstr)) pipe.Close() } func makePipe(t *testing.T) string { tmp, err := ioutil.TempDir("", "pipe-test") if err != nil { t.Fatal(err) } pipe := filepath.Join(tmp, "pipe") syscall.Mkfifo(pipe, 0600) return pipe } func writer(namedPipe string, flaky bool, c <-chan int, d <-chan bool) { // Make sure something is in the fifo otherwise the first iteration of // ConsistentRead will block forever writeToPipe(namedPipe, flaky, -1) for { select { case i := <-c: writeToPipe(namedPipe, flaky, i) case <-d: os.RemoveAll(namedPipe) return } } } func TestConsistentRead(t *testing.T) { pipe := makePipe(t) prog, done := make(chan int), make(chan bool) go writer(pipe, false, prog, done) if _, err := consistentReadSync(pipe, 3, func(i int) { prog <- i }); err != nil { t.Fatal(err) } done <- true } func TestConsistentReadFlakyReader(t *testing.T) { pipe := makePipe(t) prog, done := make(chan int), make(chan bool) go writer(pipe, true, prog, done) var err error if _, err = consistentReadSync(pipe, 3, func(i int) { prog <- i }); err == nil { t.Fatal("flaky reader returned consistent results") } if !IsInconsistentReadError(err) { t.Errorf("Unexpected error returned, expected InconsistentReadError, got: %T / %q", err, err) } } func TestReadAtMost(t *testing.T) { testCases := []struct { limit int64 data string errMsg string }{ {4, "hell", "the read limit is reached"}, {5, "hello", "the read limit is reached"}, {6, "hello", ""}, } for _, tc := range testCases { r := strings.NewReader("hello") data, err := ReadAtMost(r, tc.limit) if string(data) != tc.data { t.Errorf("Read limit %d: expected \"%s\", got \"%s\"", tc.limit, tc.data, string(data)) } if err == nil && tc.errMsg != "" { t.Errorf("Read limit %d: expected error with message \"%s\", got no error", tc.limit, tc.errMsg) } if err != nil && err.Error() != tc.errMsg { t.Errorf("Read limit %d: expected error with message \"%s\", got error with message \"%s\"", tc.limit, tc.errMsg, err.Error()) } } }