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  }