508 lines
12 KiB
Go
508 lines
12 KiB
Go
/*
|
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
testKey = "KEY"
|
|
)
|
|
|
|
func TestEnvBool(t *testing.T) {
|
|
cases := []struct {
|
|
value string
|
|
def bool
|
|
exp bool
|
|
err bool
|
|
}{
|
|
{"true", true, true, false},
|
|
{"true", false, true, false},
|
|
{"", true, true, false},
|
|
{"", false, false, false},
|
|
{"false", true, false, false},
|
|
{"false", false, false, false},
|
|
{"", true, true, false},
|
|
{"", false, false, false},
|
|
{"no true", false, false, true},
|
|
{"no false", false, false, true},
|
|
}
|
|
|
|
for _, testCase := range cases {
|
|
os.Setenv(testKey, testCase.value)
|
|
val, err := envBoolOrError(testCase.def, testKey)
|
|
if err != nil && !testCase.err {
|
|
t.Fatalf("%q: unexpected error: %v", testCase.value, err)
|
|
}
|
|
if err == nil && testCase.err {
|
|
t.Fatalf("%q: unexpected success", testCase.value)
|
|
}
|
|
if val != testCase.exp {
|
|
t.Fatalf("%q: expected %v but %v returned", testCase.value, testCase.exp, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnvString(t *testing.T) {
|
|
cases := []struct {
|
|
value string
|
|
def string
|
|
exp string
|
|
}{
|
|
{"true", "true", "true"},
|
|
{"true", "false", "true"},
|
|
{"", "true", "true"},
|
|
{"", "false", "false"},
|
|
{"false", "true", "false"},
|
|
{"false", "false", "false"},
|
|
{"", "true", "true"},
|
|
{"", "false", "false"},
|
|
}
|
|
|
|
for _, testCase := range cases {
|
|
os.Setenv(testKey, testCase.value)
|
|
val := envString(testCase.def, testKey)
|
|
if val != testCase.exp {
|
|
t.Fatalf("%q: expected %v but %v returned", testCase.value, testCase.exp, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnvInt(t *testing.T) {
|
|
cases := []struct {
|
|
value string
|
|
def int
|
|
exp int
|
|
err bool
|
|
}{
|
|
{"0", 1, 0, false},
|
|
{"", 0, 0, false},
|
|
{"-1", 0, -1, false},
|
|
{"abcd", 0, 0, true},
|
|
{"abcd", 0, 0, true},
|
|
}
|
|
|
|
for _, testCase := range cases {
|
|
os.Setenv(testKey, testCase.value)
|
|
val, err := envIntOrError(testCase.def, testKey)
|
|
if err != nil && !testCase.err {
|
|
t.Fatalf("%q: unexpected error: %v", testCase.value, err)
|
|
}
|
|
if err == nil && testCase.err {
|
|
t.Fatalf("%q: unexpected success", testCase.value)
|
|
}
|
|
if val != testCase.exp {
|
|
t.Fatalf("%q: expected %v but %v returned", testCase.value, testCase.exp, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnvFloat(t *testing.T) {
|
|
cases := []struct {
|
|
value string
|
|
def float64
|
|
exp float64
|
|
err bool
|
|
}{
|
|
{"0.5", 0, 0.5, false},
|
|
{"", 0.5, 0.5, false},
|
|
{"-0.5", 0, -0.5, false},
|
|
{"abcd", 0, 0, true},
|
|
}
|
|
|
|
for _, testCase := range cases {
|
|
os.Setenv(testKey, testCase.value)
|
|
val, err := envFloatOrError(testCase.def, testKey)
|
|
if err != nil && !testCase.err {
|
|
t.Fatalf("%q: unexpected error: %v", testCase.value, err)
|
|
}
|
|
if err == nil && testCase.err {
|
|
t.Fatalf("%q: unexpected success", testCase.value)
|
|
}
|
|
if val != testCase.exp {
|
|
t.Fatalf("%q: expected %v but %v returned", testCase.value, testCase.exp, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnvDuration(t *testing.T) {
|
|
cases := []struct {
|
|
value string
|
|
def time.Duration
|
|
exp time.Duration
|
|
err bool
|
|
}{
|
|
{"1s", 0, time.Second, false},
|
|
{"", time.Minute, time.Minute, false},
|
|
{"1h", 0, time.Hour, false},
|
|
{"abcd", 0, 0, true},
|
|
}
|
|
|
|
for _, testCase := range cases {
|
|
os.Setenv(testKey, testCase.value)
|
|
val, err := envDurationOrError(testCase.def, testKey)
|
|
if err != nil && !testCase.err {
|
|
t.Fatalf("%q: unexpected error: %v", testCase.value, err)
|
|
}
|
|
if err == nil && testCase.err {
|
|
t.Fatalf("%q: unexpected success", testCase.value)
|
|
}
|
|
if val != testCase.exp {
|
|
t.Fatalf("%q: expected %v but %v returned", testCase.value, testCase.exp, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMakeAbsPath(t *testing.T) {
|
|
cases := []struct {
|
|
path string
|
|
root string
|
|
exp string
|
|
}{{
|
|
path: "", root: "", exp: "",
|
|
}, {
|
|
path: "", root: "/root", exp: "",
|
|
}, {
|
|
path: "path", root: "/root", exp: "/root/path",
|
|
}, {
|
|
path: "p/a/t/h", root: "/root", exp: "/root/p/a/t/h",
|
|
}, {
|
|
path: "/path", root: "/root", exp: "/path",
|
|
}, {
|
|
path: "/p/a/t/h", root: "/root", exp: "/p/a/t/h",
|
|
}}
|
|
|
|
for _, tc := range cases {
|
|
res := makeAbsPath(tc.path, tc.root)
|
|
if res != tc.exp {
|
|
t.Errorf("expected: %q, got: %q", tc.exp, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWorktreePath(t *testing.T) {
|
|
testCases := []string{
|
|
"",
|
|
"/",
|
|
"//",
|
|
"/dir",
|
|
"/dir/",
|
|
"/dir//",
|
|
"/dir/sub",
|
|
"/dir/sub/",
|
|
"/dir//sub",
|
|
"/dir//sub/",
|
|
"dir",
|
|
"dir/sub",
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
if want, got := tc, worktree(tc).Path(); want != got {
|
|
t.Errorf("expected %q, got %q", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWorktreeHash(t *testing.T) {
|
|
testCases := []struct {
|
|
in worktree
|
|
exp string
|
|
}{{
|
|
in: "",
|
|
exp: "",
|
|
}, {
|
|
in: "/",
|
|
exp: "/",
|
|
}, {
|
|
in: "/one",
|
|
exp: "one",
|
|
}, {
|
|
in: "/one/two",
|
|
exp: "two",
|
|
}, {
|
|
in: "/one/two/",
|
|
exp: "two",
|
|
}, {
|
|
in: "/one//two",
|
|
exp: "two",
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
if want, got := tc.exp, tc.in.Hash(); want != got {
|
|
t.Errorf("%q: expected %q, got %q", tc.in, want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestManualHasNoTabs(t *testing.T) {
|
|
if strings.Contains(manual, "\t") {
|
|
t.Fatal("the manual text contains a tab")
|
|
}
|
|
}
|
|
|
|
func TestParseGitConfigs(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
input string
|
|
expect []keyVal
|
|
fail bool
|
|
}{{
|
|
name: "empty",
|
|
input: ``,
|
|
expect: []keyVal{},
|
|
}, {
|
|
name: "one-pair",
|
|
input: `k:v`,
|
|
expect: []keyVal{keyVal{"k", "v"}},
|
|
}, {
|
|
name: "one-pair-qkey",
|
|
input: `"k":v`,
|
|
expect: []keyVal{keyVal{"k", "v"}},
|
|
}, {
|
|
name: "one-pair-qval",
|
|
input: `k:"v"`,
|
|
expect: []keyVal{keyVal{"k", "v"}},
|
|
}, {
|
|
name: "one-pair-qkey-qval",
|
|
input: `"k":"v"`,
|
|
expect: []keyVal{keyVal{"k", "v"}},
|
|
}, {
|
|
name: "multi-pair",
|
|
input: `k1:v1,"k2":v2,k3:"v3","k4":"v4"`,
|
|
expect: []keyVal{{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}},
|
|
}, {
|
|
name: "garbage",
|
|
input: `abc123`,
|
|
fail: true,
|
|
}, {
|
|
name: "key-section-var",
|
|
input: `sect.var:v`,
|
|
expect: []keyVal{keyVal{"sect.var", "v"}},
|
|
}, {
|
|
name: "key-section-subsection-var",
|
|
input: `sect.sub.var:v`,
|
|
expect: []keyVal{keyVal{"sect.sub.var", "v"}},
|
|
}, {
|
|
name: "key-subsection-with-space",
|
|
input: `k.sect.sub section:v`,
|
|
expect: []keyVal{keyVal{"k.sect.sub section", "v"}},
|
|
}, {
|
|
name: "key-subsection-with-escape",
|
|
input: `k.sect.sub\tsection:v`,
|
|
expect: []keyVal{keyVal{"k.sect.sub\\tsection", "v"}},
|
|
}, {
|
|
name: "key-subsection-with-comma",
|
|
input: `k.sect.sub,section:v`,
|
|
expect: []keyVal{keyVal{"k.sect.sub,section", "v"}},
|
|
}, {
|
|
name: "qkey-subsection-with-space",
|
|
input: `"k.sect.sub section":v`,
|
|
expect: []keyVal{keyVal{"k.sect.sub section", "v"}},
|
|
}, {
|
|
name: "qkey-subsection-with-escapes",
|
|
input: `"k.sect.sub\t\n\\section":v`,
|
|
expect: []keyVal{keyVal{"k.sect.sub\t\n\\section", "v"}},
|
|
}, {
|
|
name: "qkey-subsection-with-comma",
|
|
input: `"k.sect.sub,section":v`,
|
|
expect: []keyVal{keyVal{"k.sect.sub,section", "v"}},
|
|
}, {
|
|
name: "qkey-subsection-with-colon",
|
|
input: `"k.sect.sub:section":v`,
|
|
expect: []keyVal{keyVal{"k.sect.sub:section", "v"}},
|
|
}, {
|
|
name: "invalid-qkey",
|
|
input: `"k\xk":v"`,
|
|
fail: true,
|
|
}, {
|
|
name: "val-spaces",
|
|
input: `k1:v 1,k2:v 2`,
|
|
expect: []keyVal{{"k1", "v 1"}, {"k2", "v 2"}},
|
|
}, {
|
|
name: "qval-spaces",
|
|
input: `k1:" v 1 ",k2:" v 2 "`,
|
|
expect: []keyVal{{"k1", " v 1 "}, {"k2", " v 2 "}},
|
|
}, {
|
|
name: "mix-val-qval",
|
|
input: `k1:v 1,k2:" v 2 "`,
|
|
expect: []keyVal{{"k1", "v 1"}, {"k2", " v 2 "}},
|
|
}, {
|
|
name: "garbage-after-qval",
|
|
input: `k1:"v1"x,k2:"v2"`,
|
|
fail: true,
|
|
}, {
|
|
name: "dangling-comma",
|
|
input: `k1:"v1",k2:"v2",`,
|
|
expect: []keyVal{{"k1", "v1"}, {"k2", "v2"}},
|
|
}, {
|
|
name: "val-with-escapes",
|
|
input: `k1:v\n\t\\\"\,1`,
|
|
expect: []keyVal{{"k1", "v\n\t\\\",1"}},
|
|
}, {
|
|
name: "qval-with-escapes",
|
|
input: `k1:"v\n\t\\\"\,1"`,
|
|
expect: []keyVal{{"k1", "v\n\t\\\",1"}},
|
|
}, {
|
|
name: "qval-with-comma",
|
|
input: `k1:"v,1"`,
|
|
expect: []keyVal{{"k1", "v,1"}},
|
|
}, {
|
|
name: "qkey-missing-close",
|
|
input: `"k1:v1`,
|
|
fail: true,
|
|
}, {
|
|
name: "qval-missing-close",
|
|
input: `k1:"v1`,
|
|
fail: true,
|
|
}}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
kvs, err := parseGitConfigs(tc.input)
|
|
if err != nil && !tc.fail {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if err == nil && tc.fail {
|
|
t.Errorf("unexpected success")
|
|
}
|
|
if !reflect.DeepEqual(kvs, tc.expect) {
|
|
t.Errorf("bad result:\n\texpected: %#v\n\t got: %#v", tc.expect, kvs)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDirIsEmpty(t *testing.T) {
|
|
root := t.TempDir()
|
|
|
|
// Brand new should be empty.
|
|
if empty, err := dirIsEmpty(root); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
} else if !empty {
|
|
t.Errorf("expected %q to be deemed empty", root)
|
|
}
|
|
|
|
// Holding normal files should not be empty.
|
|
dir := filepath.Join(root, "files")
|
|
if err := os.Mkdir(dir, 0755); err != nil {
|
|
t.Fatalf("failed to make a temp subdir: %v", err)
|
|
}
|
|
for _, file := range []string{"a", "b", "c"} {
|
|
path := filepath.Join(dir, file)
|
|
if err := os.WriteFile(path, []byte{}, 0755); err != nil {
|
|
t.Fatalf("failed to write a file: %v", err)
|
|
}
|
|
if empty, err := dirIsEmpty(dir); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
} else if empty {
|
|
t.Errorf("expected %q to be deemed not-empty", dir)
|
|
}
|
|
}
|
|
|
|
// Holding dot-files should not be empty.
|
|
dir = filepath.Join(root, "dot-files")
|
|
if err := os.Mkdir(dir, 0755); err != nil {
|
|
t.Fatalf("failed to make a temp subdir: %v", err)
|
|
}
|
|
for _, file := range []string{".a", ".b", ".c"} {
|
|
path := filepath.Join(dir, file)
|
|
if err := os.WriteFile(path, []byte{}, 0755); err != nil {
|
|
t.Fatalf("failed to write a file: %v", err)
|
|
}
|
|
if empty, err := dirIsEmpty(dir); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
} else if empty {
|
|
t.Errorf("expected %q to be deemed not-empty", dir)
|
|
}
|
|
}
|
|
|
|
// Holding dirs should not be empty.
|
|
dir = filepath.Join(root, "dirs")
|
|
if err := os.Mkdir(dir, 0755); err != nil {
|
|
t.Fatalf("failed to make a temp subdir: %v", err)
|
|
}
|
|
for _, subdir := range []string{"a", "b", "c"} {
|
|
path := filepath.Join(dir, subdir)
|
|
if err := os.Mkdir(path, 0755); err != nil {
|
|
t.Fatalf("failed to make a subdir: %v", err)
|
|
}
|
|
if empty, err := dirIsEmpty(dir); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
} else if empty {
|
|
t.Errorf("expected %q to be deemed not-empty", dir)
|
|
}
|
|
}
|
|
|
|
// Test error path.
|
|
if _, err := dirIsEmpty(filepath.Join(root, "does-not-exist")); err == nil {
|
|
t.Errorf("unexpected success for non-existent dir")
|
|
}
|
|
}
|
|
|
|
func TestRemoveDirContents(t *testing.T) {
|
|
root := t.TempDir()
|
|
|
|
// Brand new should be empty.
|
|
if empty, err := dirIsEmpty(root); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
} else if !empty {
|
|
t.Errorf("expected %q to be deemed empty", root)
|
|
}
|
|
|
|
// Test removal.
|
|
if err := removeDirContents(root, nil); err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Populate the dir.
|
|
for _, file := range []string{"f1", "f2", ".f3", ".f4"} {
|
|
path := filepath.Join(root, file)
|
|
if err := os.WriteFile(path, []byte{}, 0755); err != nil {
|
|
t.Fatalf("failed to write a file: %v", err)
|
|
}
|
|
}
|
|
for _, subdir := range []string{"d1", "d2", "d3"} {
|
|
path := filepath.Join(root, subdir)
|
|
if err := os.Mkdir(path, 0755); err != nil {
|
|
t.Fatalf("failed to make a subdir: %v", err)
|
|
}
|
|
}
|
|
|
|
// It should be deemed not-empty
|
|
if empty, err := dirIsEmpty(root); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
} else if empty {
|
|
t.Errorf("expected %q to be deemed not-empty", root)
|
|
}
|
|
|
|
// Test removal.
|
|
if err := removeDirContents(root, nil); err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Test error path.
|
|
if err := removeDirContents(filepath.Join(root, "does-not-exist"), nil); err == nil {
|
|
t.Errorf("unexpected success for non-existent dir")
|
|
}
|
|
}
|