github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/edit/tty/rune_reader_unix_test.go (about) 1 // +build !windows,!plan9 2 3 package tty 4 5 import ( 6 "fmt" 7 "math/rand" 8 "os" 9 "testing" 10 "time" 11 12 "github.com/u-root/u-root/cmds/elvish/sys" 13 ) 14 15 // Pretty arbitrary numbers. May not reveal deadlocks on all machines. 16 17 var ( 18 DeadlockNWrite = 1024 19 DeadlockRun = 64 20 DeadlockTimeout = 500 * time.Millisecond 21 DeadlockMaxJitter = time.Millisecond 22 ) 23 24 func jitter() { 25 time.Sleep(time.Duration(float64(DeadlockMaxJitter) * rand.Float64())) 26 } 27 28 // stopTester tries to trigger a potential race condition where 29 // (*RuneReader).Stop deadlocks and blocks forever. It inserts random jitters to 30 // try to trigger race condition. 31 func stopTester() { 32 r, w, err := os.Pipe() 33 if err != nil { 34 panic(err) 35 } 36 defer r.Close() 37 defer w.Close() 38 39 ar := newRuneReader(r) 40 defer ar.Close() 41 fmt.Fprintf(w, "%*s", DeadlockNWrite, "") 42 go func() { 43 jitter() 44 ar.Start() 45 }() 46 47 jitter() 48 // Is there a deadlock that makes this call block indefinitely. 49 ar.Stop() 50 } 51 52 func TestRuneReaderStopAlwaysStops(t *testing.T) { 53 isatty := sys.IsATTY(os.Stdin) 54 rand.Seed(time.Now().UTC().UnixNano()) 55 56 timer := time.NewTimer(DeadlockTimeout) 57 for i := 0; i < DeadlockRun; i++ { 58 if isatty { 59 fmt.Printf("\r%d/%d ", i+1, DeadlockRun) 60 } 61 62 done := make(chan bool) 63 go func() { 64 stopTester() 65 close(done) 66 }() 67 68 select { 69 case <-done: 70 // no deadlock trigerred 71 case <-timer.C: 72 // deadlock 73 t.Errorf("%s", sys.DumpStack()) 74 t.Fatalf("AsyncReader deadlock trigerred on run %d/%d, stack trace:\n%s", i, DeadlockRun, sys.DumpStack()) 75 } 76 timer.Reset(DeadlockTimeout) 77 } 78 if isatty { 79 fmt.Print("\r \r") 80 } 81 } 82 83 var ReadTimeout = time.Second 84 85 func TestRuneReader(t *testing.T) { 86 r, w, err := os.Pipe() 87 if err != nil { 88 panic(err) 89 } 90 defer r.Close() 91 defer w.Close() 92 93 ar := newRuneReader(r) 94 defer ar.Close() 95 ar.Start() 96 97 go func() { 98 var i rune 99 for i = 0; i <= 1280; i += 10 { 100 w.WriteString(string(i)) 101 } 102 }() 103 104 var i rune 105 timer := time.NewTimer(ReadTimeout) 106 for i = 0; i <= 1280; i += 10 { 107 select { 108 case r := <-ar.Chan(): 109 if r != i { 110 t.Fatalf("expect %q, got %q\n", i, r) 111 } 112 case <-timer.C: 113 t.Fatalf("read timeout (i = %d)", i) 114 } 115 timer.Reset(ReadTimeout) 116 } 117 ar.Stop() 118 }