Break up main.go
Add new files for abspath, credential, and env functions.
This commit is contained in:
parent
ff51ca92dc
commit
8fa652dafa
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// absPath is an absolute path string. This type is intended to make it clear
|
||||
// when strings are absolute paths vs something else. This does not verify or
|
||||
// mutate the input, so careless callers could make instances of this type that
|
||||
// are not actually absolute paths, or even "".
|
||||
type absPath string
|
||||
|
||||
// String returns abs as a string.
|
||||
func (abs absPath) String() string {
|
||||
return string(abs)
|
||||
}
|
||||
|
||||
// Canonical returns a canonicalized form of abs, similar to filepath.Abs
|
||||
// (including filepath.Clean). Unlike filepath.Clean, this preserves "" as a
|
||||
// special case.
|
||||
func (abs absPath) Canonical() (absPath, error) {
|
||||
if abs == "" {
|
||||
return abs, nil
|
||||
}
|
||||
|
||||
result, err := filepath.Abs(abs.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return absPath(result), nil
|
||||
}
|
||||
|
||||
// Join appends more path elements to abs, like filepath.Join.
|
||||
func (abs absPath) Join(elems ...string) absPath {
|
||||
all := make([]string, 0, 1+len(elems))
|
||||
all = append(all, abs.String())
|
||||
all = append(all, elems...)
|
||||
return absPath(filepath.Join(all...))
|
||||
}
|
||||
|
||||
// Split breaks abs into stem and leaf parts (often directory and file, but not
|
||||
// necessarily), similar to filepath.Split. Unlike filepath.Split, the
|
||||
// resulting stem part does not have any trailing path separators.
|
||||
func (abs absPath) Split() (string, string) {
|
||||
if abs == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// filepath.Split promises that dir+base == input, but trailing slashes on
|
||||
// the dir is confusing and ugly.
|
||||
pathSep := string(os.PathSeparator)
|
||||
dir, base := filepath.Split(strings.TrimRight(abs.String(), pathSep))
|
||||
dir = strings.TrimRight(dir, pathSep)
|
||||
if len(dir) == 0 {
|
||||
dir = string(os.PathSeparator)
|
||||
}
|
||||
|
||||
return dir, base
|
||||
}
|
||||
|
||||
// Dir returns the stem part of abs without the leaf, like filepath.Dir.
|
||||
func (abs absPath) Dir() string {
|
||||
dir, _ := abs.Split()
|
||||
return dir
|
||||
}
|
||||
|
||||
// Base returns the leaf part of abs without the stem, like filepath.Base.
|
||||
func (abs absPath) Base() string {
|
||||
_, base := abs.Split()
|
||||
return base
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAbsPathString(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, absPath(tc).String(); want != got {
|
||||
t.Errorf("expected %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathCanonical(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
exp absPath
|
||||
}{{
|
||||
in: "",
|
||||
exp: "",
|
||||
}, {
|
||||
in: "/",
|
||||
exp: "/",
|
||||
}, {
|
||||
in: "/one",
|
||||
exp: "/one",
|
||||
}, {
|
||||
in: "/one/two",
|
||||
exp: "/one/two",
|
||||
}, {
|
||||
in: "/one/two/",
|
||||
exp: "/one/two",
|
||||
}, {
|
||||
in: "/one//two",
|
||||
exp: "/one/two",
|
||||
}, {
|
||||
in: "/one/two/../three",
|
||||
exp: "/one/three",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
want := tc.exp
|
||||
got, err := tc.in.Canonical()
|
||||
if err != nil {
|
||||
t.Errorf("%q: unexpected error: %v", tc.in, err)
|
||||
} else if want != got {
|
||||
t.Errorf("%q: expected %q, got %q", tc.in, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathJoin(t *testing.T) {
|
||||
testCases := []struct {
|
||||
base absPath
|
||||
more []string
|
||||
expect absPath
|
||||
}{{
|
||||
base: "/dir",
|
||||
more: nil,
|
||||
expect: "/dir",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one"},
|
||||
expect: "/dir/one",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one", "two"},
|
||||
expect: "/dir/one/two",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one", "two", "three"},
|
||||
expect: "/dir/one/two/three",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"with/slash"},
|
||||
expect: "/dir/with/slash",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"with/trailingslash/"},
|
||||
expect: "/dir/with/trailingslash",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"with//twoslash"},
|
||||
expect: "/dir/with/twoslash",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one/1", "two/2", "three/3"},
|
||||
expect: "/dir/one/1/two/2/three/3",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if want, got := tc.expect, tc.base.Join(tc.more...); want != got {
|
||||
t.Errorf("(%q, %q): expected %q, got %q", tc.base, tc.more, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathSplit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
expDir string
|
||||
expBase string
|
||||
}{{
|
||||
in: "",
|
||||
expDir: "",
|
||||
expBase: "",
|
||||
}, {
|
||||
in: "/",
|
||||
expDir: "/",
|
||||
expBase: "",
|
||||
}, {
|
||||
in: "//",
|
||||
expDir: "/",
|
||||
expBase: "",
|
||||
}, {
|
||||
in: "/one",
|
||||
expDir: "/",
|
||||
expBase: "one",
|
||||
}, {
|
||||
in: "/one/two",
|
||||
expDir: "/one",
|
||||
expBase: "two",
|
||||
}, {
|
||||
in: "/one/two/",
|
||||
expDir: "/one",
|
||||
expBase: "two",
|
||||
}, {
|
||||
in: "/one//two",
|
||||
expDir: "/one",
|
||||
expBase: "two",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
wantDir, wantBase := tc.expDir, tc.expBase
|
||||
if gotDir, gotBase := tc.in.Split(); wantDir != gotDir || wantBase != gotBase {
|
||||
t.Errorf("%q: expected (%q, %q), got (%q, %q)", tc.in, wantDir, wantBase, gotDir, gotBase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathDir(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
exp string
|
||||
}{{
|
||||
in: "",
|
||||
exp: "",
|
||||
}, {
|
||||
in: "/",
|
||||
exp: "/",
|
||||
}, {
|
||||
in: "/one",
|
||||
exp: "/",
|
||||
}, {
|
||||
in: "/one/two",
|
||||
exp: "/one",
|
||||
}, {
|
||||
in: "/one/two/",
|
||||
exp: "/one",
|
||||
}, {
|
||||
in: "/one//two",
|
||||
exp: "/one",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if want, got := tc.exp, tc.in.Dir(); want != got {
|
||||
t.Errorf("%q: expected %q, got %q", tc.in, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathBase(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
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.Base(); want != got {
|
||||
t.Errorf("%q: expected %q, got %q", tc.in, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
Copyright 2014 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type credential struct {
|
||||
URL string `json:"url"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
PasswordFile string `json:"password-file,omitempty"`
|
||||
}
|
||||
|
||||
func (c credential) String() string {
|
||||
jb, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<encoding error: %v>", err)
|
||||
}
|
||||
return string(jb)
|
||||
}
|
||||
|
||||
// credentialSliceValue is for flags.
|
||||
type credentialSliceValue struct {
|
||||
value []credential
|
||||
changed bool
|
||||
}
|
||||
|
||||
var _ pflag.Value = &credentialSliceValue{}
|
||||
var _ pflag.SliceValue = &credentialSliceValue{}
|
||||
|
||||
// pflagCredentialSlice is like pflag.StringSlice()
|
||||
func pflagCredentialSlice(name, def, usage string) *[]credential {
|
||||
p := &credentialSliceValue{}
|
||||
_ = p.Set(def)
|
||||
pflag.Var(p, name, usage)
|
||||
return &p.value
|
||||
}
|
||||
|
||||
// unmarshal is like json.Unmarshal, but fails on unknown fields.
|
||||
func (cs credentialSliceValue) unmarshal(val string, out any) error {
|
||||
dec := json.NewDecoder(strings.NewReader(val))
|
||||
dec.DisallowUnknownFields()
|
||||
return dec.Decode(out)
|
||||
}
|
||||
|
||||
// decodeList handles a string-encoded JSON object.
|
||||
func (cs credentialSliceValue) decodeObject(val string) (credential, error) {
|
||||
var cred credential
|
||||
if err := cs.unmarshal(val, &cred); err != nil {
|
||||
return credential{}, err
|
||||
}
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
// decodeList handles a string-encoded JSON list.
|
||||
func (cs credentialSliceValue) decodeList(val string) ([]credential, error) {
|
||||
var creds []credential
|
||||
if err := cs.unmarshal(val, &creds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// decode handles a string-encoded JSON object or list.
|
||||
func (cs credentialSliceValue) decode(val string) ([]credential, error) {
|
||||
s := strings.TrimSpace(val)
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
// If it tastes like an object...
|
||||
if s[0] == '{' {
|
||||
cred, err := cs.decodeObject(s)
|
||||
return []credential{cred}, err
|
||||
}
|
||||
// If it tastes like a list...
|
||||
if s[0] == '[' {
|
||||
return cs.decodeList(s)
|
||||
}
|
||||
// Otherwise, bad
|
||||
return nil, fmt.Errorf("not a JSON object or list")
|
||||
}
|
||||
|
||||
func (cs *credentialSliceValue) Set(val string) error {
|
||||
v, err := cs.decode(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cs.changed {
|
||||
cs.value = v
|
||||
} else {
|
||||
cs.value = append(cs.value, v...)
|
||||
}
|
||||
cs.changed = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs credentialSliceValue) Type() string {
|
||||
return "credentialSlice"
|
||||
}
|
||||
|
||||
func (cs credentialSliceValue) String() string {
|
||||
if len(cs.value) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
jb, err := json.Marshal(cs.value)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<encoding error: %v>", err)
|
||||
}
|
||||
return string(jb)
|
||||
}
|
||||
|
||||
func (cs *credentialSliceValue) Append(val string) error {
|
||||
v, err := cs.decodeObject(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.value = append(cs.value, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *credentialSliceValue) Replace(val []string) error {
|
||||
creds := []credential{}
|
||||
for _, s := range val {
|
||||
v, err := cs.decodeObject(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
creds = append(creds, v)
|
||||
}
|
||||
cs.value = creds
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs credentialSliceValue) GetSlice() []string {
|
||||
if len(cs.value) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := []string{}
|
||||
for _, cred := range cs.value {
|
||||
ret = append(ret, cred.String())
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
Copyright 2014 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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func envString(def string, key string, alts ...string) string {
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return val
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return val
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func envStringArray(def string, key string, alts ...string) []string {
|
||||
parse := func(s string) []string {
|
||||
return strings.Split(s, ":")
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(val)
|
||||
}
|
||||
}
|
||||
return parse(def)
|
||||
}
|
||||
|
||||
func envBoolOrError(def bool, key string, alts ...string) (bool, error) {
|
||||
parse := func(key, val string) (bool, error) {
|
||||
parsed, err := strconv.ParseBool(val)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
return false, fmt.Errorf("ERROR: invalid bool env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envBool(def bool, key string, alts ...string) bool {
|
||||
val, err := envBoolOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return false
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func envIntOrError(def int, key string, alts ...string) (int, error) {
|
||||
parse := func(key, val string) (int, error) {
|
||||
parsed, err := strconv.ParseInt(val, 0, 0)
|
||||
if err == nil {
|
||||
return int(parsed), nil
|
||||
}
|
||||
return 0, fmt.Errorf("ERROR: invalid int env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envInt(def int, key string, alts ...string) int {
|
||||
val, err := envIntOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func envFloatOrError(def float64, key string, alts ...string) (float64, error) {
|
||||
parse := func(key, val string) (float64, error) {
|
||||
parsed, err := strconv.ParseFloat(val, 64)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
return 0, fmt.Errorf("ERROR: invalid float env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envFloat(def float64, key string, alts ...string) float64 {
|
||||
val, err := envFloatOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func envDurationOrError(def time.Duration, key string, alts ...string) (time.Duration, error) {
|
||||
parse := func(key, val string) (time.Duration, error) {
|
||||
parsed, err := time.ParseDuration(val)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
return 0, fmt.Errorf("ERROR: invalid duration env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envDuration(def time.Duration, key string, alts ...string) time.Duration {
|
||||
val, err := envDurationOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
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"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
}
|
||||
357
main.go
357
main.go
|
|
@ -21,7 +21,6 @@ package main // import "k8s.io/git-sync/cmd/git-sync"
|
|||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -106,362 +105,6 @@ const (
|
|||
|
||||
const defaultDirMode = os.FileMode(0775) // subject to umask
|
||||
|
||||
type credential struct {
|
||||
URL string `json:"url"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
PasswordFile string `json:"password-file,omitempty"`
|
||||
}
|
||||
|
||||
func (c credential) String() string {
|
||||
jb, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<encoding error: %v>", err)
|
||||
}
|
||||
return string(jb)
|
||||
}
|
||||
|
||||
// credentialSliceValue is for flags.
|
||||
type credentialSliceValue struct {
|
||||
value []credential
|
||||
changed bool
|
||||
}
|
||||
|
||||
var _ pflag.Value = &credentialSliceValue{}
|
||||
var _ pflag.SliceValue = &credentialSliceValue{}
|
||||
|
||||
// pflagCredentialSlice is like pflag.StringSlice()
|
||||
func pflagCredentialSlice(name, def, usage string) *[]credential {
|
||||
p := &credentialSliceValue{}
|
||||
_ = p.Set(def)
|
||||
pflag.Var(p, name, usage)
|
||||
return &p.value
|
||||
}
|
||||
|
||||
// unmarshal is like json.Unmarshal, but fails on unknown fields.
|
||||
func (cs credentialSliceValue) unmarshal(val string, out any) error {
|
||||
dec := json.NewDecoder(strings.NewReader(val))
|
||||
dec.DisallowUnknownFields()
|
||||
return dec.Decode(out)
|
||||
}
|
||||
|
||||
// decodeList handles a string-encoded JSON object.
|
||||
func (cs credentialSliceValue) decodeObject(val string) (credential, error) {
|
||||
var cred credential
|
||||
if err := cs.unmarshal(val, &cred); err != nil {
|
||||
return credential{}, err
|
||||
}
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
// decodeList handles a string-encoded JSON list.
|
||||
func (cs credentialSliceValue) decodeList(val string) ([]credential, error) {
|
||||
var creds []credential
|
||||
if err := cs.unmarshal(val, &creds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// decode handles a string-encoded JSON object or list.
|
||||
func (cs credentialSliceValue) decode(val string) ([]credential, error) {
|
||||
s := strings.TrimSpace(val)
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
// If it tastes like an object...
|
||||
if s[0] == '{' {
|
||||
cred, err := cs.decodeObject(s)
|
||||
return []credential{cred}, err
|
||||
}
|
||||
// If it tastes like a list...
|
||||
if s[0] == '[' {
|
||||
return cs.decodeList(s)
|
||||
}
|
||||
// Otherwise, bad
|
||||
return nil, fmt.Errorf("not a JSON object or list")
|
||||
}
|
||||
|
||||
func (cs *credentialSliceValue) Set(val string) error {
|
||||
v, err := cs.decode(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cs.changed {
|
||||
cs.value = v
|
||||
} else {
|
||||
cs.value = append(cs.value, v...)
|
||||
}
|
||||
cs.changed = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs credentialSliceValue) Type() string {
|
||||
return "credentialSlice"
|
||||
}
|
||||
|
||||
func (cs credentialSliceValue) String() string {
|
||||
if len(cs.value) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
jb, err := json.Marshal(cs.value)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<encoding error: %v>", err)
|
||||
}
|
||||
return string(jb)
|
||||
}
|
||||
|
||||
func (cs *credentialSliceValue) Append(val string) error {
|
||||
v, err := cs.decodeObject(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.value = append(cs.value, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *credentialSliceValue) Replace(val []string) error {
|
||||
creds := []credential{}
|
||||
for _, s := range val {
|
||||
v, err := cs.decodeObject(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
creds = append(creds, v)
|
||||
}
|
||||
cs.value = creds
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs credentialSliceValue) GetSlice() []string {
|
||||
if len(cs.value) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := []string{}
|
||||
for _, cred := range cs.value {
|
||||
ret = append(ret, cred.String())
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func envString(def string, key string, alts ...string) string {
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return val
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return val
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func envStringArray(def string, key string, alts ...string) []string {
|
||||
parse := func(s string) []string {
|
||||
return strings.Split(s, ":")
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(val)
|
||||
}
|
||||
}
|
||||
return parse(def)
|
||||
}
|
||||
|
||||
func envBoolOrError(def bool, key string, alts ...string) (bool, error) {
|
||||
parse := func(key, val string) (bool, error) {
|
||||
parsed, err := strconv.ParseBool(val)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
return false, fmt.Errorf("ERROR: invalid bool env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envBool(def bool, key string, alts ...string) bool {
|
||||
val, err := envBoolOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return false
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func envIntOrError(def int, key string, alts ...string) (int, error) {
|
||||
parse := func(key, val string) (int, error) {
|
||||
parsed, err := strconv.ParseInt(val, 0, 0)
|
||||
if err == nil {
|
||||
return int(parsed), nil
|
||||
}
|
||||
return 0, fmt.Errorf("ERROR: invalid int env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envInt(def int, key string, alts ...string) int {
|
||||
val, err := envIntOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func envFloatOrError(def float64, key string, alts ...string) (float64, error) {
|
||||
parse := func(key, val string) (float64, error) {
|
||||
parsed, err := strconv.ParseFloat(val, 64)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
return 0, fmt.Errorf("ERROR: invalid float env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envFloat(def float64, key string, alts ...string) float64 {
|
||||
val, err := envFloatOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func envDurationOrError(def time.Duration, key string, alts ...string) (time.Duration, error) {
|
||||
parse := func(key, val string) (time.Duration, error) {
|
||||
parsed, err := time.ParseDuration(val)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
return 0, fmt.Errorf("ERROR: invalid duration env %s=%q: %w", key, val, err)
|
||||
}
|
||||
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return parse(key, val)
|
||||
}
|
||||
for _, alt := range alts {
|
||||
if val := os.Getenv(alt); val != "" {
|
||||
fmt.Fprintf(os.Stderr, "env %s has been deprecated, use %s instead\n", alt, key)
|
||||
return parse(alt, val)
|
||||
}
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func envDuration(def time.Duration, key string, alts ...string) time.Duration {
|
||||
val, err := envDurationOrError(def, key, alts...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// absPath is an absolute path string. This type is intended to make it clear
|
||||
// when strings are absolute paths vs something else. This does not verify or
|
||||
// mutate the input, so careless callers could make instances of this type that
|
||||
// are not actually absolute paths, or even "".
|
||||
type absPath string
|
||||
|
||||
// String returns abs as a string.
|
||||
func (abs absPath) String() string {
|
||||
return string(abs)
|
||||
}
|
||||
|
||||
// Canonical returns a canonicalized form of abs, similar to filepath.Abs
|
||||
// (including filepath.Clean). Unlike filepath.Clean, this preserves "" as a
|
||||
// special case.
|
||||
func (abs absPath) Canonical() (absPath, error) {
|
||||
if abs == "" {
|
||||
return abs, nil
|
||||
}
|
||||
|
||||
result, err := filepath.Abs(abs.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return absPath(result), nil
|
||||
}
|
||||
|
||||
// Join appends more path elements to abs, like filepath.Join.
|
||||
func (abs absPath) Join(elems ...string) absPath {
|
||||
all := make([]string, 0, 1+len(elems))
|
||||
all = append(all, abs.String())
|
||||
all = append(all, elems...)
|
||||
return absPath(filepath.Join(all...))
|
||||
}
|
||||
|
||||
// Split breaks abs into stem and leaf parts (often directory and file, but not
|
||||
// necessarily), similar to filepath.Split. Unlike filepath.Split, the
|
||||
// resulting stem part does not have any trailing path separators.
|
||||
func (abs absPath) Split() (string, string) {
|
||||
if abs == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// filepath.Split promises that dir+base == input, but trailing slashes on
|
||||
// the dir is confusing and ugly.
|
||||
pathSep := string(os.PathSeparator)
|
||||
dir, base := filepath.Split(strings.TrimRight(abs.String(), pathSep))
|
||||
dir = strings.TrimRight(dir, pathSep)
|
||||
if len(dir) == 0 {
|
||||
dir = string(os.PathSeparator)
|
||||
}
|
||||
|
||||
return dir, base
|
||||
}
|
||||
|
||||
// Dir returns the stem part of abs without the leaf, like filepath.Dir.
|
||||
func (abs absPath) Dir() string {
|
||||
dir, _ := abs.Split()
|
||||
return dir
|
||||
}
|
||||
|
||||
// Base returns the leaf part of abs without the stem, like filepath.Base.
|
||||
func (abs absPath) Base() string {
|
||||
_, base := abs.Split()
|
||||
return base
|
||||
}
|
||||
|
||||
// repoSync represents the remote repo and the local sync of it.
|
||||
type repoSync struct {
|
||||
cmd string // the git command to run
|
||||
|
|
|
|||
360
main_test.go
360
main_test.go
|
|
@ -27,154 +27,6 @@ import (
|
|||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
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
|
||||
|
|
@ -396,218 +248,6 @@ func TestParseGitConfigs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAbsPathString(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, absPath(tc).String(); want != got {
|
||||
t.Errorf("expected %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathCanonical(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
exp absPath
|
||||
}{{
|
||||
in: "",
|
||||
exp: "",
|
||||
}, {
|
||||
in: "/",
|
||||
exp: "/",
|
||||
}, {
|
||||
in: "/one",
|
||||
exp: "/one",
|
||||
}, {
|
||||
in: "/one/two",
|
||||
exp: "/one/two",
|
||||
}, {
|
||||
in: "/one/two/",
|
||||
exp: "/one/two",
|
||||
}, {
|
||||
in: "/one//two",
|
||||
exp: "/one/two",
|
||||
}, {
|
||||
in: "/one/two/../three",
|
||||
exp: "/one/three",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
want := tc.exp
|
||||
got, err := tc.in.Canonical()
|
||||
if err != nil {
|
||||
t.Errorf("%q: unexpected error: %v", tc.in, err)
|
||||
} else if want != got {
|
||||
t.Errorf("%q: expected %q, got %q", tc.in, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathJoin(t *testing.T) {
|
||||
testCases := []struct {
|
||||
base absPath
|
||||
more []string
|
||||
expect absPath
|
||||
}{{
|
||||
base: "/dir",
|
||||
more: nil,
|
||||
expect: "/dir",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one"},
|
||||
expect: "/dir/one",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one", "two"},
|
||||
expect: "/dir/one/two",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one", "two", "three"},
|
||||
expect: "/dir/one/two/three",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"with/slash"},
|
||||
expect: "/dir/with/slash",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"with/trailingslash/"},
|
||||
expect: "/dir/with/trailingslash",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"with//twoslash"},
|
||||
expect: "/dir/with/twoslash",
|
||||
}, {
|
||||
base: "/dir",
|
||||
more: []string{"one/1", "two/2", "three/3"},
|
||||
expect: "/dir/one/1/two/2/three/3",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if want, got := tc.expect, tc.base.Join(tc.more...); want != got {
|
||||
t.Errorf("(%q, %q): expected %q, got %q", tc.base, tc.more, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathSplit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
expDir string
|
||||
expBase string
|
||||
}{{
|
||||
in: "",
|
||||
expDir: "",
|
||||
expBase: "",
|
||||
}, {
|
||||
in: "/",
|
||||
expDir: "/",
|
||||
expBase: "",
|
||||
}, {
|
||||
in: "//",
|
||||
expDir: "/",
|
||||
expBase: "",
|
||||
}, {
|
||||
in: "/one",
|
||||
expDir: "/",
|
||||
expBase: "one",
|
||||
}, {
|
||||
in: "/one/two",
|
||||
expDir: "/one",
|
||||
expBase: "two",
|
||||
}, {
|
||||
in: "/one/two/",
|
||||
expDir: "/one",
|
||||
expBase: "two",
|
||||
}, {
|
||||
in: "/one//two",
|
||||
expDir: "/one",
|
||||
expBase: "two",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
wantDir, wantBase := tc.expDir, tc.expBase
|
||||
if gotDir, gotBase := tc.in.Split(); wantDir != gotDir || wantBase != gotBase {
|
||||
t.Errorf("%q: expected (%q, %q), got (%q, %q)", tc.in, wantDir, wantBase, gotDir, gotBase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathDir(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
exp string
|
||||
}{{
|
||||
in: "",
|
||||
exp: "",
|
||||
}, {
|
||||
in: "/",
|
||||
exp: "/",
|
||||
}, {
|
||||
in: "/one",
|
||||
exp: "/",
|
||||
}, {
|
||||
in: "/one/two",
|
||||
exp: "/one",
|
||||
}, {
|
||||
in: "/one/two/",
|
||||
exp: "/one",
|
||||
}, {
|
||||
in: "/one//two",
|
||||
exp: "/one",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if want, got := tc.exp, tc.in.Dir(); want != got {
|
||||
t.Errorf("%q: expected %q, got %q", tc.in, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsPathBase(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in absPath
|
||||
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.Base(); want != got {
|
||||
t.Errorf("%q: expected %q, got %q", tc.in, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirIsEmpty(t *testing.T) {
|
||||
root := absPath(t.TempDir())
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue