Switch from glide to govendor

This commit is contained in:
Manfred Touron
2017-12-19 13:55:52 +01:00
parent ccffd8bfe2
commit 230480afd1
1871 changed files with 302 additions and 801202 deletions

View File

@@ -1,4 +0,0 @@
_testdata/
_testdata2/
logfmt-fuzz.zip
logfmt.test.exe

View File

@@ -1,15 +0,0 @@
language: go
sudo: false
go:
- 1.3
- 1.4
- 1.5
- 1.6
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- goveralls -service=travis-ci

View File

@@ -1,75 +0,0 @@
package logfmt
import (
"bufio"
"bytes"
"testing"
kr "github.com/kr/logfmt"
)
func BenchmarkDecodeKeyval(b *testing.B) {
const rows = 10000
data := []byte{}
for i := 0; i < rows; i++ {
data = append(data, "a=1 b=\"bar\" ƒ=2h3s r=\"esc\\tmore stuff\" d x=sf \n"...)
}
b.SetBytes(int64(len(data)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
var (
dec = NewDecoder(bytes.NewReader(data))
j = 0
)
for dec.ScanRecord() {
for dec.ScanKeyval() {
}
j++
}
if err := dec.Err(); err != nil {
b.Errorf("got %v, want %v", err, nil)
}
if j != rows {
b.Errorf("got %v, want %v", j, rows)
}
}
}
func BenchmarkKRDecode(b *testing.B) {
const rows = 10000
data := []byte{}
for i := 0; i < rows; i++ {
data = append(data, "a=1 b=\"bar\" ƒ=2h3s r=\"esc\\tmore stuff\" d x=sf \n"...)
}
b.SetBytes(int64(len(data)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
var (
s = bufio.NewScanner(bytes.NewReader(data))
err error
j = 0
dh discardHandler
)
for err == nil && s.Scan() {
err = kr.Unmarshal(s.Bytes(), &dh)
j++
}
if err == nil {
err = s.Err()
}
if err != nil {
b.Errorf("got %v, want %v", err, nil)
}
if j != rows {
b.Errorf("got %v, want %v", j, rows)
}
}
}
type discardHandler struct{}
func (discardHandler) HandleLogfmt(key, val []byte) error {
return nil
}

View File

@@ -1,184 +0,0 @@
package logfmt
import (
"bytes"
"fmt"
"reflect"
"strings"
"testing"
)
type kv struct {
k, v []byte
}
func (s kv) String() string {
return fmt.Sprintf("{k:%q v:%q}", s.k, s.v)
}
func TestDecoder_scan(t *testing.T) {
tests := []struct {
data string
want [][]kv
}{
{"", nil},
{"\n\n", [][]kv{nil, nil}},
{`x= `, [][]kv{{{[]byte("x"), nil}}}},
{`y=`, [][]kv{{{[]byte("y"), nil}}}},
{`y`, [][]kv{{{[]byte("y"), nil}}}},
{`y=f`, [][]kv{{{[]byte("y"), []byte("f")}}}},
{"y=\"\\tf\"", [][]kv{{{[]byte("y"), []byte("\tf")}}}},
{"a=1\n", [][]kv{{{[]byte("a"), []byte("1")}}}},
{
`a=1 b="bar" ƒ=2h3s r="esc\t" d x=sf `,
[][]kv{{
{[]byte("a"), []byte("1")},
{[]byte("b"), []byte("bar")},
{[]byte("ƒ"), []byte("2h3s")},
{[]byte("r"), []byte("esc\t")},
{[]byte("d"), nil},
{[]byte("x"), []byte("sf")},
}},
},
{
"y=f\ny=g",
[][]kv{
{{[]byte("y"), []byte("f")}},
{{[]byte("y"), []byte("g")}},
},
},
{
"y=f \n\x1e y=g",
[][]kv{
{{[]byte("y"), []byte("f")}},
{{[]byte("y"), []byte("g")}},
},
},
{
"y= d y=g",
[][]kv{{
{[]byte("y"), nil},
{[]byte("d"), nil},
{[]byte("y"), []byte("g")},
}},
},
{
"y=\"f\"\ny=g",
[][]kv{
{{[]byte("y"), []byte("f")}},
{{[]byte("y"), []byte("g")}},
},
},
{
"y=\"f\\n\"y=g",
[][]kv{{
{[]byte("y"), []byte("f\n")},
{[]byte("y"), []byte("g")},
}},
},
}
for _, test := range tests {
var got [][]kv
dec := NewDecoder(strings.NewReader(test.data))
for dec.ScanRecord() {
var kvs []kv
for dec.ScanKeyval() {
k := dec.Key()
v := dec.Value()
if k != nil {
kvs = append(kvs, kv{k, v})
}
}
got = append(got, kvs)
}
if err := dec.Err(); err != nil {
t.Errorf("got err: %v", err)
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf("\n in: %q\n got: %+v\nwant: %+v", test.data, got, test.want)
}
}
}
func TestDecoder_errors(t *testing.T) {
tests := []struct {
data string
want error
}{
{"a=1\n=bar", &SyntaxError{Msg: "unexpected '='", Line: 2, Pos: 1}},
{"a=1\n\"k\"=bar", &SyntaxError{Msg: "unexpected '\"'", Line: 2, Pos: 1}},
{"a=1\nk\"ey=bar", &SyntaxError{Msg: "unexpected '\"'", Line: 2, Pos: 2}},
{"a=1\nk=b\"ar", &SyntaxError{Msg: "unexpected '\"'", Line: 2, Pos: 4}},
{"a=1\nk=b =ar", &SyntaxError{Msg: "unexpected '='", Line: 2, Pos: 5}},
{"a==", &SyntaxError{Msg: "unexpected '='", Line: 1, Pos: 3}},
{"a=1\nk=b=ar", &SyntaxError{Msg: "unexpected '='", Line: 2, Pos: 4}},
{"a=\"1", &SyntaxError{Msg: "unterminated quoted value", Line: 1, Pos: 5}},
{"a=\"1\\", &SyntaxError{Msg: "unterminated quoted value", Line: 1, Pos: 6}},
{"a=\"\\t1", &SyntaxError{Msg: "unterminated quoted value", Line: 1, Pos: 7}},
{"a=\"\\u1\"", &SyntaxError{Msg: "invalid quoted value", Line: 1, Pos: 8}},
{"a\ufffd=bar", &SyntaxError{Msg: "invalid key", Line: 1, Pos: 5}},
{"\x80=bar", &SyntaxError{Msg: "invalid key", Line: 1, Pos: 2}},
{"\x80", &SyntaxError{Msg: "invalid key", Line: 1, Pos: 2}},
}
for _, test := range tests {
dec := NewDecoder(strings.NewReader(test.data))
for dec.ScanRecord() {
for dec.ScanKeyval() {
}
}
if got, want := dec.Err(), test.want; !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want: %v", got, want)
}
}
}
func TestDecoder_decode_encode(t *testing.T) {
tests := []struct {
in, out string
}{
{"", ""},
{"\n", "\n"},
{"\n \n", "\n\n"},
{
"a=1\nb=2\n",
"a=1\nb=2\n",
},
{
"a=1 b=\"bar\" ƒ=2h3s r=\"esc\\t\" d x=sf ",
"a=1 b=bar ƒ=2h3s r=\"esc\\t\" d= x=sf\n",
},
}
for _, test := range tests {
dec := NewDecoder(strings.NewReader(test.in))
buf := bytes.Buffer{}
enc := NewEncoder(&buf)
var err error
loop:
for dec.ScanRecord() && err == nil {
for dec.ScanKeyval() {
if dec.Key() == nil {
continue
}
if err = enc.EncodeKeyval(dec.Key(), dec.Value()); err != nil {
break loop
}
}
enc.EndRecord()
}
if err == nil {
err = dec.Err()
}
if err != nil {
t.Errorf("got err: %v", err)
}
if got, want := buf.String(), test.out; got != want {
t.Errorf("\n got: %q\nwant: %q", got, want)
}
}
}

View File

@@ -1,233 +0,0 @@
package logfmt
import (
"bytes"
"errors"
"fmt"
"reflect"
"testing"
)
func TestSafeString(t *testing.T) {
_, ok := safeString((*stringStringer)(nil))
if got, want := ok, false; got != want {
t.Errorf(" got %v, want %v", got, want)
}
}
func TestSafeMarshal(t *testing.T) {
kb, err := safeMarshal((*stringMarshaler)(nil))
if got := kb; got != nil {
t.Errorf(" got %v, want nil", got)
}
if got, want := err, error(nil); got != want {
t.Errorf(" got %v, want %v", got, want)
}
}
func TestWriteKeyStrings(t *testing.T) {
keygen := []func(string) interface{}{
func(s string) interface{} { return s },
func(s string) interface{} { return stringData(s) },
func(s string) interface{} { return stringStringer(s) },
func(s string) interface{} { return stringMarshaler(s) },
}
data := []struct {
key string
want string
err error
}{
{key: "k", want: "k"},
{key: `\`, want: `\`},
{key: "\n", err: ErrInvalidKey},
{key: "\x00", err: ErrInvalidKey},
{key: "\x10", err: ErrInvalidKey},
{key: "\x1F", err: ErrInvalidKey},
{key: "", err: ErrInvalidKey},
{key: " ", err: ErrInvalidKey},
{key: "=", err: ErrInvalidKey},
{key: `"`, err: ErrInvalidKey},
}
for _, g := range keygen {
for _, d := range data {
w := &bytes.Buffer{}
key := g(d.key)
err := writeKey(w, key)
if err != d.err {
t.Errorf("%#v (%[1]T): got error: %v, want error: %v", key, err, d.err)
}
if err != nil {
continue
}
if got, want := w.String(), d.want; got != want {
t.Errorf("%#v (%[1]T): got '%s', want '%s'", key, got, want)
}
}
}
}
func TestWriteKey(t *testing.T) {
var (
nilPtr *int
one = 1
ptr = &one
)
data := []struct {
key interface{}
want string
err error
}{
{key: nil, err: ErrNilKey},
{key: nilPtr, err: ErrNilKey},
{key: (*stringStringer)(nil), err: ErrNilKey},
{key: (*stringMarshaler)(nil), err: ErrNilKey},
{key: (*stringerMarshaler)(nil), err: ErrNilKey},
{key: ptr, want: "1"},
{key: errorMarshaler{}, err: &MarshalerError{Type: reflect.TypeOf(errorMarshaler{}), Err: errMarshaling}},
{key: make(chan int), err: ErrUnsupportedKeyType},
{key: []int{}, err: ErrUnsupportedKeyType},
{key: map[int]int{}, err: ErrUnsupportedKeyType},
{key: [2]int{}, err: ErrUnsupportedKeyType},
{key: struct{}{}, err: ErrUnsupportedKeyType},
{key: fmt.Sprint, err: ErrUnsupportedKeyType},
}
for _, d := range data {
w := &bytes.Buffer{}
err := writeKey(w, d.key)
if !reflect.DeepEqual(err, d.err) {
t.Errorf("%#v: got error: %v, want error: %v", d.key, err, d.err)
}
if err != nil {
continue
}
if got, want := w.String(), d.want; got != want {
t.Errorf("%#v: got '%s', want '%s'", d.key, got, want)
}
}
}
func TestWriteValueStrings(t *testing.T) {
keygen := []func(string) interface{}{
func(s string) interface{} { return s },
func(s string) interface{} { return errors.New(s) },
func(s string) interface{} { return stringData(s) },
func(s string) interface{} { return stringStringer(s) },
func(s string) interface{} { return stringMarshaler(s) },
}
data := []struct {
value string
want string
err error
}{
{value: "", want: ""},
{value: "v", want: "v"},
{value: " ", want: `" "`},
{value: "=", want: `"="`},
{value: `\`, want: `\`},
{value: `"`, want: `"\""`},
{value: `\"`, want: `"\\\""`},
{value: "\n", want: `"\n"`},
{value: "\x00", want: `"\u0000"`},
{value: "\x10", want: `"\u0010"`},
{value: "\x1F", want: `"\u001f"`},
{value: "µ", want: `µ`},
}
for _, g := range keygen {
for _, d := range data {
w := &bytes.Buffer{}
value := g(d.value)
err := writeValue(w, value)
if err != d.err {
t.Errorf("%#v (%[1]T): got error: %v, want error: %v", value, err, d.err)
}
if err != nil {
continue
}
if got, want := w.String(), d.want; got != want {
t.Errorf("%#v (%[1]T): got '%s', want '%s'", value, got, want)
}
}
}
}
func TestWriteValue(t *testing.T) {
var (
nilPtr *int
one = 1
ptr = &one
)
data := []struct {
value interface{}
want string
err error
}{
{value: nil, want: "null"},
{value: nilPtr, want: "null"},
{value: (*stringStringer)(nil), want: "null"},
{value: (*stringMarshaler)(nil), want: "null"},
{value: (*stringerMarshaler)(nil), want: "null"},
{value: ptr, want: "1"},
{value: errorMarshaler{}, err: &MarshalerError{Type: reflect.TypeOf(errorMarshaler{}), Err: errMarshaling}},
{value: make(chan int), err: ErrUnsupportedValueType},
{value: []int{}, err: ErrUnsupportedValueType},
{value: map[int]int{}, err: ErrUnsupportedValueType},
{value: [2]int{}, err: ErrUnsupportedValueType},
{value: struct{}{}, err: ErrUnsupportedValueType},
{value: fmt.Sprint, err: ErrUnsupportedValueType},
}
for _, d := range data {
w := &bytes.Buffer{}
err := writeValue(w, d.value)
if !reflect.DeepEqual(err, d.err) {
t.Errorf("%#v: got error: %v, want error: %v", d.value, err, d.err)
}
if err != nil {
continue
}
if got, want := w.String(), d.want; got != want {
t.Errorf("%#v: got '%s', want '%s'", d.value, got, want)
}
}
}
type stringData string
type stringStringer string
func (s stringStringer) String() string {
return string(s)
}
type stringMarshaler string
func (s stringMarshaler) MarshalText() ([]byte, error) {
return []byte(s), nil
}
type stringerMarshaler string
func (s stringerMarshaler) String() string {
return "String() called"
}
func (s stringerMarshaler) MarshalText() ([]byte, error) {
return []byte(s), nil
}
var errMarshaling = errors.New("marshal error")
type errorMarshaler struct{}
func (errorMarshaler) MarshalText() ([]byte, error) {
return nil, errMarshaling
}

View File

@@ -1,206 +0,0 @@
package logfmt_test
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"reflect"
"testing"
"time"
"github.com/go-logfmt/logfmt"
)
func TestEncodeKeyval(t *testing.T) {
data := []struct {
key, value interface{}
want string
err error
}{
{key: "k", value: "v", want: "k=v"},
{key: "k", value: nil, want: "k=null"},
{key: `\`, value: "v", want: `\=v`},
{key: "k", value: "", want: "k="},
{key: "k", value: "null", want: `k="null"`},
{key: "k", value: "<nil>", want: `k=<nil>`},
{key: "k", value: true, want: "k=true"},
{key: "k", value: 1, want: "k=1"},
{key: "k", value: 1.025, want: "k=1.025"},
{key: "k", value: 1e-3, want: "k=0.001"},
{key: "k", value: 3.5 + 2i, want: "k=(3.5+2i)"},
{key: "k", value: "v v", want: `k="v v"`},
{key: "k", value: " ", want: `k=" "`},
{key: "k", value: `"`, want: `k="\""`},
{key: "k", value: `=`, want: `k="="`},
{key: "k", value: `\`, want: `k=\`},
{key: "k", value: `=\`, want: `k="=\\"`},
{key: "k", value: `\"`, want: `k="\\\""`},
{key: "k", value: [2]int{2, 19}, err: logfmt.ErrUnsupportedValueType},
{key: "k", value: []string{"e1", "e 2"}, err: logfmt.ErrUnsupportedValueType},
{key: "k", value: structData{"a a", 9}, err: logfmt.ErrUnsupportedValueType},
{key: "k", value: decimalMarshaler{5, 9}, want: "k=5.9"},
{key: "k", value: (*decimalMarshaler)(nil), want: "k=null"},
{key: "k", value: decimalStringer{5, 9}, want: "k=5.9"},
{key: "k", value: (*decimalStringer)(nil), want: "k=null"},
{key: "k", value: marshalerStringer{5, 9}, want: "k=5.9"},
{key: "k", value: (*marshalerStringer)(nil), want: "k=null"},
{key: "k", value: new(nilMarshaler), want: "k=notnilmarshaler"},
{key: "k", value: (*nilMarshaler)(nil), want: "k=nilmarshaler"},
{key: (*marshalerStringer)(nil), value: "v", err: logfmt.ErrNilKey},
{key: decimalMarshaler{5, 9}, value: "v", want: "5.9=v"},
{key: (*decimalMarshaler)(nil), value: "v", err: logfmt.ErrNilKey},
{key: decimalStringer{5, 9}, value: "v", want: "5.9=v"},
{key: (*decimalStringer)(nil), value: "v", err: logfmt.ErrNilKey},
{key: marshalerStringer{5, 9}, value: "v", want: "5.9=v"},
{key: "k", value: "\xbd", want: `k="\ufffd"`},
{key: "k", value: "\ufffd\x00", want: `k="\ufffd\u0000"`},
{key: "k", value: "\ufffd", want: `k="\ufffd"`},
{key: "k", value: []byte("\ufffd\x00"), want: `k="\ufffd\u0000"`},
{key: "k", value: []byte("\ufffd"), want: `k="\ufffd"`},
}
for _, d := range data {
w := &bytes.Buffer{}
enc := logfmt.NewEncoder(w)
err := enc.EncodeKeyval(d.key, d.value)
if err != d.err {
t.Errorf("%#v, %#v: got error: %v, want error: %v", d.key, d.value, err, d.err)
}
if got, want := w.String(), d.want; got != want {
t.Errorf("%#v, %#v: got '%s', want '%s'", d.key, d.value, got, want)
}
}
}
func TestMarshalKeyvals(t *testing.T) {
one := 1
ptr := &one
nilPtr := (*int)(nil)
data := []struct {
in []interface{}
want []byte
err error
}{
{in: nil, want: nil},
{in: kv(), want: nil},
{in: kv(nil, "v"), err: logfmt.ErrNilKey},
{in: kv(nilPtr, "v"), err: logfmt.ErrNilKey},
{in: kv("\ufffd"), err: logfmt.ErrInvalidKey},
{in: kv("\xbd"), err: logfmt.ErrInvalidKey},
{in: kv("k"), want: []byte("k=null")},
{in: kv("k", nil), want: []byte("k=null")},
{in: kv("k", ""), want: []byte("k=")},
{in: kv("k", "null"), want: []byte(`k="null"`)},
{in: kv("k", "v"), want: []byte("k=v")},
{in: kv("k", true), want: []byte("k=true")},
{in: kv("k", 1), want: []byte("k=1")},
{in: kv("k", ptr), want: []byte("k=1")},
{in: kv("k", nilPtr), want: []byte("k=null")},
{in: kv("k", 1.025), want: []byte("k=1.025")},
{in: kv("k", 1e-3), want: []byte("k=0.001")},
{in: kv("k", "v v"), want: []byte(`k="v v"`)},
{in: kv("k", `"`), want: []byte(`k="\""`)},
{in: kv("k", `=`), want: []byte(`k="="`)},
{in: kv("k", `\`), want: []byte(`k=\`)},
{in: kv("k", `=\`), want: []byte(`k="=\\"`)},
{in: kv("k", `\"`), want: []byte(`k="\\\""`)},
{in: kv("k1", "v1", "k2", "v2"), want: []byte("k1=v1 k2=v2")},
{in: kv("k1", "v1", "k2", [2]int{}), want: []byte("k1=v1 k2=\"unsupported value type\"")},
{in: kv([2]int{}, "v1", "k2", "v2"), want: []byte("k2=v2")},
{in: kv("k", time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)), want: []byte("k=2009-11-10T23:00:00Z")},
{in: kv("k", errorMarshaler{}), want: []byte("k=\"error marshaling value of type logfmt_test.errorMarshaler: marshal error\"")},
{in: kv("k", decimalMarshaler{5, 9}), want: []byte("k=5.9")},
{in: kv("k", (*decimalMarshaler)(nil)), want: []byte("k=null")},
{in: kv("k", decimalStringer{5, 9}), want: []byte("k=5.9")},
{in: kv("k", (*decimalStringer)(nil)), want: []byte("k=null")},
{in: kv("k", marshalerStringer{5, 9}), want: []byte("k=5.9")},
{in: kv("k", (*marshalerStringer)(nil)), want: []byte("k=null")},
{in: kv(one, "v"), want: []byte("1=v")},
{in: kv(ptr, "v"), want: []byte("1=v")},
{in: kv((*marshalerStringer)(nil), "v"), err: logfmt.ErrNilKey},
{in: kv(decimalMarshaler{5, 9}, "v"), want: []byte("5.9=v")},
{in: kv((*decimalMarshaler)(nil), "v"), err: logfmt.ErrNilKey},
{in: kv(decimalStringer{5, 9}, "v"), want: []byte("5.9=v")},
{in: kv((*decimalStringer)(nil), "v"), err: logfmt.ErrNilKey},
{in: kv(marshalerStringer{5, 9}, "v"), want: []byte("5.9=v")},
}
for _, d := range data {
got, err := logfmt.MarshalKeyvals(d.in...)
if err != d.err {
t.Errorf("%#v: got error: %v, want error: %v", d.in, err, d.err)
}
if !reflect.DeepEqual(got, d.want) {
t.Errorf("%#v: got '%s', want '%s'", d.in, got, d.want)
}
}
}
func kv(keyvals ...interface{}) []interface{} {
return keyvals
}
type structData struct {
A string `logfmt:"fieldA"`
B int
}
type nilMarshaler int
func (m *nilMarshaler) MarshalText() ([]byte, error) {
if m == nil {
return []byte("nilmarshaler"), nil
}
return []byte("notnilmarshaler"), nil
}
type decimalMarshaler struct {
a, b int
}
func (t decimalMarshaler) MarshalText() ([]byte, error) {
buf := &bytes.Buffer{}
fmt.Fprintf(buf, "%d.%d", t.a, t.b)
return buf.Bytes(), nil
}
type decimalStringer struct {
a, b int
}
func (s decimalStringer) String() string {
return fmt.Sprintf("%d.%d", s.a, s.b)
}
type marshalerStringer struct {
a, b int
}
func (t marshalerStringer) MarshalText() ([]byte, error) {
buf := &bytes.Buffer{}
fmt.Fprintf(buf, "%d.%d", t.a, t.b)
return buf.Bytes(), nil
}
func (t marshalerStringer) String() string {
return fmt.Sprint(t.a + t.b)
}
var errMarshal = errors.New("marshal error")
type errorMarshaler struct{}
func (errorMarshaler) MarshalText() ([]byte, error) {
return nil, errMarshal
}
func BenchmarkEncodeKeyval(b *testing.B) {
b.ReportAllocs()
enc := logfmt.NewEncoder(ioutil.Discard)
for i := 0; i < b.N; i++ {
enc.EncodeKeyval("sk", "10")
enc.EncodeKeyval("some-key", "a rather long string with spaces")
}
}

View File

@@ -1,60 +0,0 @@
package logfmt_test
import (
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/go-logfmt/logfmt"
)
func ExampleEncoder() {
check := func(err error) {
if err != nil {
panic(err)
}
}
e := logfmt.NewEncoder(os.Stdout)
check(e.EncodeKeyval("id", 1))
check(e.EncodeKeyval("dur", time.Second+time.Millisecond))
check(e.EndRecord())
check(e.EncodeKeyval("id", 1))
check(e.EncodeKeyval("path", "/path/to/file"))
check(e.EncodeKeyval("err", errors.New("file not found")))
check(e.EndRecord())
// Output:
// id=1 dur=1.001s
// id=1 path=/path/to/file err="file not found"
}
func ExampleDecoder() {
in := `
id=1 dur=1.001s
id=1 path=/path/to/file err="file not found"
`
d := logfmt.NewDecoder(strings.NewReader(in))
for d.ScanRecord() {
for d.ScanKeyval() {
fmt.Printf("k: %s v: %s\n", d.Key(), d.Value())
}
fmt.Println()
}
if d.Err() != nil {
panic(d.Err())
}
// Output:
// k: id v: 1
// k: dur v: 1.001s
//
// k: id v: 1
// k: path v: /path/to/file
// k: err v: file not found
}