Skip to content

Commit

Permalink
api: OptionValues IsTrue|IsFalse
Browse files Browse the repository at this point in the history
This patch adds the IsTrue|IsFalse methods to the OptionValuesList
helper. The functions return true|false based on the same logic common
to vCenter.
  • Loading branch information
akutz committed Aug 13, 2024
1 parent 3b6a726 commit 5a03649
Show file tree
Hide file tree
Showing 2 changed files with 314 additions and 0 deletions.
61 changes: 61 additions & 0 deletions object/option_value_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package object
import (
"fmt"
"reflect"
"slices"
"strings"

"github.com/vmware/govmomi/vim25/types"
)
Expand All @@ -44,6 +46,65 @@ func OptionValueListFromMap[T any](in map[string]T) OptionValueList {
return out
}

// IsTrue returns true if the specified key exists with an empty value or value
// equal to 1, "1", "on", "t", true, "true", "y", or "yes".
// All string comparisons are case-insensitive.
func (ov OptionValueList) IsTrue(key string) bool {
return ov.isTrueOrFalse(key, true, 1, "", "1", "on", "t", "true", "y", "yes")
}

// IsFalse returns true if the specified key exists and has a value equal to
// 0, "0", "f", false, "false", "n", "no", or "off".
// All string comparisons are case-insensitive.
func (ov OptionValueList) IsFalse(key string) bool {
return ov.isTrueOrFalse(key, false, 0, "0", "f", "false", "n", "no", "off")
}

func (ov OptionValueList) isTrueOrFalse(
key string,
boolVal bool,
numVal int,
strVals ...string) bool {

val, ok := ov.Get(key)
if !ok {
return false
}

switch tval := val.(type) {
case string:
return slices.Contains(strVals, strings.ToLower(tval))
case bool:
return tval == boolVal
case uint:
return tval == uint(numVal)
case uint8:
return tval == uint8(numVal)
case uint16:
return tval == uint16(numVal)
case uint32:
return tval == uint32(numVal)
case uint64:
return tval == uint64(numVal)
case int:
return tval == int(numVal)
case int8:
return tval == int8(numVal)
case int16:
return tval == int16(numVal)
case int32:
return tval == int32(numVal)
case int64:
return tval == int64(numVal)
case float32:
return tval == float32(numVal)
case float64:
return tval == float64(numVal)
}

return false
}

// Get returns the value if exists, otherwise nil is returned. The second return
// value is a flag indicating whether the value exists or nil was the actual
// value.
Expand Down
253 changes: 253 additions & 0 deletions object/option_value_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package object_test

import (
"fmt"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -112,6 +114,233 @@ func TestOptionValueList(t *testing.T) {
})
})

t.Run("IsTrueOrFalse", func(t *testing.T) {

type testCase struct {
name string
left object.OptionValueList
key string
ok bool
}

addBoolTestCases := func(b, ok bool) []testCase {
return []testCase{
{
name: fmt.Sprintf("a key with %v should return %v", b, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: b},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with %v should return %v", !b, !ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: !b},
},
key: sza,
ok: !ok,
},
}
}

addNumericalTestCases := func(i int, ok bool) []testCase {
return []testCase{
{
name: fmt.Sprintf("a key with byte(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: byte(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with uint(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: uint(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with uint8(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: uint8(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with uint16(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: uint16(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with uint32(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: uint32(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with uint64(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: uint64(i)},
},
key: sza,
ok: ok,
},

{
name: fmt.Sprintf("a key with int(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: int(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with int8(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: int8(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with int16(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: int16(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with int32(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: int32(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with int64(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: int64(i)},
},
key: sza,
ok: ok,
},

{
name: fmt.Sprintf("a key with float32(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: float32(i)},
},
key: sza,
ok: ok,
},
{
name: fmt.Sprintf("a key with float64(%v) should return %v", i, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: float64(i)},
},
key: sza,
ok: ok,
},
}
}

addTestCasesForPermutedString := func(s string, ok bool) []testCase {
var testCases []testCase
for _, s := range permuteByCase(s) {
testCases = append(testCases, testCase{
name: fmt.Sprintf("a key with %q should return %v", s, ok),
left: object.OptionValueList{
&types.OptionValue{Key: sza, Value: s},
},
key: sza,
ok: ok,
})
}
return testCases
}

addTestCasesForPermutedStrings := func(ok bool, args ...string) []testCase {
var testCases []testCase
for i := range args {
testCases = append(testCases, addTestCasesForPermutedString(args[i], ok)...)
}
return testCases
}

baseTestCases := []testCase{
{
name: "a nil receiver should not panic and return false",
left: nil,
key: "",
ok: false,
},
{
name: "a non-existent key should return false",
left: object.OptionValueList{},
key: "",
ok: false,
},
}

runTests := func(t *testing.T, expected bool) {
testCases := append([]testCase{}, baseTestCases...)

for i := range baseTestCases {
tc := testCases[i]
t.Run(tc.name, func(t *testing.T) {
var ok bool
if expected {
assert.NotPanics(t, func() { ok = tc.left.IsTrue(tc.key) })
assert.Equal(t, tc.ok, ok)
} else {
assert.NotPanics(t, func() { ok = tc.left.IsFalse(tc.key) })
assert.Equal(t, tc.ok, ok)
}
})
}

testCases = append([]testCase{}, addBoolTestCases(true, expected)...)
testCases = append(testCases, addNumericalTestCases(0, !expected)...)
testCases = append(testCases, addNumericalTestCases(1, expected)...)
testCases = append(testCases, addTestCasesForPermutedStrings(expected, "", "1", "on", "t", "true", "y", "yes")...)
testCases = append(testCases, addTestCasesForPermutedStrings(!expected, "0", "f", "false", "n", "no", "off")...)

for i := range testCases {
tc := testCases[i]
t.Run(tc.name, func(t *testing.T) {
var ok bool
if expected {
assert.NotPanics(t, func() { ok = tc.left.IsTrue(tc.key) })
assert.Equal(t, tc.ok, ok)
assert.NotPanics(t, func() { ok = tc.left.IsFalse(tc.key) })
assert.Equal(t, !tc.ok, ok)
} else {
assert.NotPanics(t, func() { ok = tc.left.IsTrue(tc.key) })
assert.Equal(t, !tc.ok, ok)
assert.NotPanics(t, func() { ok = tc.left.IsFalse(tc.key) })
assert.Equal(t, tc.ok, ok)
}

})
}
}

t.Run("IsTrue", func(t *testing.T) { runTests(t, true) })
t.Run("IsFalse", func(t *testing.T) { runTests(t, false) })

})

t.Run("Get", func(t *testing.T) {
testCases := []struct {
name string
Expand Down Expand Up @@ -601,3 +830,27 @@ func TestOptionValueList(t *testing.T) {
}
})
}

func permuteByCase(s string) []string {
if len(s) == 0 {
return []string{s}
}

if len(s) == 1 {
lc := strings.ToLower(s)
uc := strings.ToUpper(s)
if lc == uc {
return []string{s}
}
return []string{lc, uc}
}

var p []string
for _, i := range permuteByCase(s[0:1]) {
for _, j := range permuteByCase(s[1:]) {
p = append(p, fmt.Sprintf("%s%s", i, j))
}
}

return p
}

0 comments on commit 5a03649

Please sign in to comment.