github.com/BenLubar/git-last-modified@v0.1.1-0.20210215221858-9b8031919630/main_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "strings" 9 "testing" 10 11 "github.com/pkg/errors" 12 ) 13 14 var stderr io.Writer = os.Stderr 15 16 func pipeStderr(t *testing.T, r *os.File, wait chan<- struct{}) { 17 defer func() { 18 close(wait) 19 if err := r.Close(); err != nil { 20 t.Error(err) 21 } 22 }() 23 24 var buf [4096]byte 25 for { 26 n, err := r.Read(buf[:]) 27 if err != nil && err != io.EOF { 28 t.Error(err) 29 return 30 } 31 32 n1, err1 := stderr.Write(buf[:n]) 33 if err1 != nil { 34 t.Error(err1) 35 return 36 } 37 38 if n1 != n { 39 t.Error(io.ErrShortWrite) 40 return 41 } 42 43 if err == io.EOF { 44 return 45 } 46 } 47 } 48 49 func testStderr(t *testing.T, f func(), expected string) { 50 r, w, err := os.Pipe() 51 if err != nil { 52 panic(err) 53 } 54 55 realStderr := os.Stderr 56 os.Stderr = w 57 wait := make(chan struct{}) 58 go pipeStderr(t, r, wait) 59 60 var buf bytes.Buffer 61 oldStderr := stderr 62 stderr = &buf 63 defer func() { 64 if err := w.Close(); err != nil { 65 t.Error(err) 66 } 67 <-wait 68 69 os.Stderr = realStderr 70 stderr = oldStderr 71 if actual := strings.Replace(buf.String(), "\r\n", "\n", -1); !strings.EqualFold(actual, expected) { 72 t.Error("Expected stderr does not match actual stderr.") 73 t.Errorf("expected: %q", expected) 74 t.Errorf("actual: %q", actual) 75 } 76 }() 77 78 f() 79 } 80 81 func testExit(t *testing.T, f func(), expectedCode int) { 82 type exited int 83 oldExit := exit 84 exit = func(code int) { 85 panic(exited(code)) 86 } 87 defer func() { 88 exit = oldExit 89 if r := recover(); r == nil { 90 t.Errorf("Expected call to exit(%d), but exit was not called.", expectedCode) 91 } else if code, ok := r.(exited); ok { 92 if int(code) != expectedCode { 93 t.Errorf("Expected call to exit(%d), but exit(%d) was called.", expectedCode, int(code)) 94 } 95 } else { 96 panic(r) 97 } 98 }() 99 100 f() 101 } 102 103 func TestUsage(t *testing.T) { 104 testStderr(t, func() { 105 testExit(t, usage, 2) 106 }, "usage: git last-modified [<options>] [[--] <path>...]\n"+ 107 " -commit-date\n"+ 108 " \tUse the commit date for the last commit this file was involved in instead of the author date.\n"+ 109 " -n\tDry run. Implies -v. Don't modify any file modification times.\n"+ 110 " -q\tQuiet. Don't warn about files specified on the command line that are not in Git.\n"+ 111 " -v\tVerbose. Print each filename and modification time as they are processed.\n") 112 } 113 114 func TestCheckError(t *testing.T) { 115 testStderr(t, func() { 116 checkError(nil) // should not exit 117 }, "") 118 119 err := errors.New("test") 120 121 testStderr(t, func() { 122 testExit(t, func() { 123 checkError(err) 124 }, 1) 125 }, "git-last-modified: test\n") 126 127 *flagVerbose = true 128 defer func() { 129 *flagVerbose = false 130 }() 131 132 testStderr(t, func() { 133 testExit(t, func() { 134 checkError(err) 135 }, 1) 136 }, fmt.Sprintf("git-last-modified: test%+v\n", err.(interface{ StackTrace() errors.StackTrace }).StackTrace())) 137 }