/usr/share/gocode/src/github.com/howeyc/gopass/pass_test.go is in golang-github-howeyc-gopass-dev 0.0~git20160303.0.66487b2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | package gopass
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"testing"
"time"
)
// TestGetPasswd tests the password creation and output based on a byte buffer
// as input to mock the underlying getch() methods.
func TestGetPasswd(t *testing.T) {
type testData struct {
input []byte
// Due to how backspaces are written, it is easier to manually write
// each expected output for the masked cases.
masked string
password string
byesLeft int
reason string
}
ds := []testData{
testData{[]byte("abc\n"), "***\n", "abc", 0, "Password parsing should stop at \\n"},
testData{[]byte("abc\r"), "***\n", "abc", 0, "Password parsing should stop at \\r"},
testData{[]byte("a\nbc\n"), "*\n", "a", 3, "Password parsing should stop at \\n"},
testData{[]byte("*!]|\n"), "****\n", "*!]|", 0, "Special characters shouldn't affect the password."},
testData{[]byte("abc\r\n"), "***\n", "abc", 1,
"Password parsing should stop at \\r; Windows LINE_MODE should be unset so \\r is not converted to \\r\\n."},
testData{[]byte{'a', 'b', 'c', 8, '\n'}, "***\b \b\n", "ab", 0, "Backspace byte should remove the last read byte."},
testData{[]byte{'a', 'b', 127, 'c', '\n'}, "**\b \b*\n", "ac", 0, "Delete byte should remove the last read byte."},
testData{[]byte{'a', 'b', 127, 'c', 8, 127, '\n'}, "**\b \b*\b \b\b \b\n", "", 0, "Successive deletes continue to delete."},
testData{[]byte{8, 8, 8, '\n'}, "\n", "", 0, "Deletes before characters are noops."},
testData{[]byte{8, 8, 8, 'a', 'b', 'c', '\n'}, "***\n", "abc", 0, "Deletes before characters are noops."},
testData{[]byte{'a', 'b', 0, 'c', '\n'}, "***\n", "abc", 0,
"Nil byte should be ignored due; may get unintended nil bytes from syscalls on Windows."},
}
// Redirecting output for tests as they print to os.Stdout but we want to
// capture and test the output.
origStdOut := os.Stdout
for _, masked := range []bool{true, false} {
for _, d := range ds {
pipeBytesToStdin(d.input)
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err.Error())
}
os.Stdout = w
result, err := getPasswd(masked)
os.Stdout = origStdOut
if err != nil {
t.Errorf("Error getting password:", err.Error())
}
leftOnBuffer := flushStdin()
// Test output (masked and unmasked). Delete/backspace actually
// deletes, overwrites and deletes again. As a result, we need to
// remove those from the pipe afterwards to mimic the console's
// interpretation of those bytes.
w.Close()
output, err := ioutil.ReadAll(r)
if err != nil {
t.Fatal(err.Error())
}
var expectedOutput []byte
if masked {
expectedOutput = []byte(d.masked)
} else {
expectedOutput = []byte("\n")
}
if bytes.Compare(expectedOutput, output) != 0 {
t.Errorf("Expected output to equal %v (%q) but got %v (%q) instead when masked=%v. %s", expectedOutput, string(expectedOutput), output, string(output), masked, d.reason)
}
if string(result) != d.password {
t.Errorf("Expected %q but got %q instead when masked=%v. %s", d.password, result, masked, d.reason)
}
if leftOnBuffer != d.byesLeft {
t.Errorf("Expected %v bytes left on buffer but instead got %v when masked=%v. %s", d.byesLeft, leftOnBuffer, masked, d.reason)
}
}
}
}
// TestPipe ensures we get our expected pipe behavior.
func TestPipe(t *testing.T) {
type testData struct {
input string
password string
expError error
}
ds := []testData{
testData{"abc", "abc", io.EOF},
testData{"abc\n", "abc", nil},
testData{"abc\r", "abc", nil},
testData{"abc\r\n", "abc", nil},
}
for _, d := range ds {
_, err := pipeToStdin(d.input)
if err != nil {
t.Log("Error writing input to stdin:", err)
t.FailNow()
}
pass, err := GetPasswd()
if string(pass) != d.password {
t.Errorf("Expected %q but got %q instead.", d.password, string(pass))
}
if err != d.expError {
t.Errorf("Expected %v but got %q instead.", d.expError, err)
}
}
}
// flushStdin reads from stdin for .5 seconds to ensure no bytes are left on
// the buffer. Returns the number of bytes read.
func flushStdin() int {
ch := make(chan byte)
go func(ch chan byte) {
reader := bufio.NewReader(os.Stdin)
for {
b, err := reader.ReadByte()
if err != nil { // Maybe log non io.EOF errors, if you want
close(ch)
return
}
ch <- b
}
close(ch)
}(ch)
numBytes := 0
for {
select {
case _, ok := <-ch:
if !ok {
return numBytes
}
numBytes++
case <-time.After(500 * time.Millisecond):
return numBytes
}
}
return numBytes
}
// pipeToStdin pipes the given string onto os.Stdin by replacing it with an
// os.Pipe. The write end of the pipe is closed so that EOF is read after the
// final byte.
func pipeToStdin(s string) (int, error) {
pipeReader, pipeWriter, err := os.Pipe()
if err != nil {
fmt.Println("Error getting os pipes:", err)
os.Exit(1)
}
os.Stdin = pipeReader
w, err := pipeWriter.WriteString(s)
pipeWriter.Close()
return w, err
}
func pipeBytesToStdin(b []byte) (int, error) {
return pipeToStdin(string(b))
}
// TestGetPasswd_Err tests errors are properly handled from getch()
func TestGetPasswd_Err(t *testing.T) {
var inBuffer *bytes.Buffer
getch = func() (byte, error) {
b, err := inBuffer.ReadByte()
if err != nil {
return 13, err
}
if b == 'z' {
return 'z', fmt.Errorf("Forced error; byte returned should not be considered accurate.")
}
return b, nil
}
defer func() { getch = defaultGetCh }()
for input, expectedPassword := range map[string]string{"abc": "abc", "abzc": "ab"} {
inBuffer = bytes.NewBufferString(input)
p, err := GetPasswdMasked()
if string(p) != expectedPassword {
t.Errorf("Expected %q but got %q instead.", expectedPassword, p)
}
if err == nil {
t.Errorf("Expected error to be returned.")
}
}
}
func TestMaxPasswordLength(t *testing.T) {
type testData struct {
input []byte
expectedErr error
// Helper field to output in case of failure; rather than hundreds of
// bytes.
inputDesc string
}
ds := []testData{
testData{append(bytes.Repeat([]byte{'a'}, maxLength), '\n'), nil, fmt.Sprintf("%v 'a' bytes followed by a newline", maxLength)},
testData{append(bytes.Repeat([]byte{'a'}, maxLength+1), '\n'), ErrMaxLengthExceeded, fmt.Sprintf("%v 'a' bytes followed by a newline", maxLength+1)},
testData{append(bytes.Repeat([]byte{0x00}, maxLength+1), '\n'), ErrMaxLengthExceeded, fmt.Sprintf("%v 0x00 bytes followed by a newline", maxLength+1)},
}
for _, d := range ds {
pipeBytesToStdin(d.input)
_, err := GetPasswd()
if err != d.expectedErr {
t.Errorf("Expected error to be %v; isntead got %v from %v", d.expectedErr, err, d.inputDesc)
}
}
}
|