Skip to content

Commit f1353f1

Browse files
committed
feat(parameter): add dependson for the parameter group
1 parent 43cf589 commit f1353f1

File tree

13 files changed

+733
-102
lines changed

13 files changed

+733
-102
lines changed

internal/helpers/contains.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package helpers
2+
3+
import (
4+
"reflect"
5+
"strings"
6+
)
7+
8+
// Contains asserts that the specified string, list(array, slice...) or map contains the
9+
// specified substring or element.
10+
//
11+
// helpers.Contains("Hello World", "World")
12+
// helpers.Contains(["Hello", "World"], "World")
13+
// helpers.Contains({"Hello": "World"}, "Hello")
14+
func Contains(s, contains interface{}, msgAndArgs ...interface{}) bool {
15+
ok, found := containsElement(s, contains)
16+
if !ok {
17+
return false
18+
}
19+
20+
return found
21+
}
22+
23+
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
24+
// specified substring or element.
25+
//
26+
// helpers.NotContains("Hello World", "Earth")
27+
// helpers.NotContains(["Hello", "World"], "Earth")
28+
// helpers.NotContains({"Hello": "World"}, "Earth")
29+
func NotContains(s, contains interface{}, msgAndArgs ...interface{}) bool {
30+
ok, found := containsElement(s, contains)
31+
if !ok {
32+
return false
33+
}
34+
35+
return !found
36+
}
37+
38+
// containsElement try loop over the list check if the list includes the element.
39+
// return (false, false) if impossible.
40+
// return (true, false) if element was not found.
41+
// return (true, true) if element was found.
42+
func containsElement(list interface{}, element interface{}) (ok, found bool) {
43+
44+
listValue := reflect.ValueOf(list)
45+
listType := reflect.TypeOf(list)
46+
if listType == nil {
47+
return false, false
48+
}
49+
listKind := listType.Kind()
50+
defer func() {
51+
if e := recover(); e != nil {
52+
ok = false
53+
found = false
54+
}
55+
}()
56+
57+
if listKind == reflect.String {
58+
elementValue := reflect.ValueOf(element)
59+
return true, strings.Contains(listValue.String(), elementValue.String())
60+
}
61+
62+
if listKind == reflect.Map {
63+
mapKeys := listValue.MapKeys()
64+
for i := 0; i < len(mapKeys); i++ {
65+
if ObjectsAreEqual(mapKeys[i].Interface(), element) {
66+
return true, true
67+
}
68+
}
69+
return true, false
70+
}
71+
72+
for i := 0; i < listValue.Len(); i++ {
73+
if ObjectsAreEqual(listValue.Index(i).Interface(), element) {
74+
return true, true
75+
}
76+
}
77+
return true, false
78+
79+
}

internal/helpers/contains_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package helpers
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
func TestContainsNotContains(t *testing.T) {
9+
10+
type A struct {
11+
Name, Value string
12+
}
13+
list := []string{"Foo", "Bar"}
14+
15+
complexList := []*A{
16+
{"b", "c"},
17+
{"d", "e"},
18+
{"g", "h"},
19+
{"j", "k"},
20+
}
21+
simpleMap := map[interface{}]interface{}{"Foo": "Bar"}
22+
var zeroMap map[interface{}]interface{}
23+
24+
cases := []struct {
25+
expected interface{}
26+
actual interface{}
27+
result bool
28+
}{
29+
{"Hello World", "Hello", true},
30+
{"Hello World", "Salut", false},
31+
{list, "Bar", true},
32+
{list, "Salut", false},
33+
{complexList, &A{"g", "h"}, true},
34+
{complexList, &A{"g", "e"}, false},
35+
{simpleMap, "Foo", true},
36+
{simpleMap, "Bar", false},
37+
{zeroMap, "Bar", false},
38+
}
39+
40+
for _, c := range cases {
41+
t.Run(fmt.Sprintf("Contains(%#v, %#v)", c.expected, c.actual), func(t *testing.T) {
42+
res := Contains(c.expected, c.actual)
43+
44+
if res != c.result {
45+
if res {
46+
t.Errorf("Contains(%#v, %#v) should return true:\n\t%#v contains %#v", c.expected, c.actual, c.expected, c.actual)
47+
} else {
48+
t.Errorf("Contains(%#v, %#v) should return false:\n\t%#v does not contain %#v", c.expected, c.actual, c.expected, c.actual)
49+
}
50+
}
51+
})
52+
}
53+
54+
for _, c := range cases {
55+
t.Run(fmt.Sprintf("NotContains(%#v, %#v)", c.expected, c.actual), func(t *testing.T) {
56+
res := NotContains(c.expected, c.actual)
57+
58+
// NotContains should be inverse of Contains. If it's not, something is wrong
59+
if res == Contains(c.expected, c.actual) {
60+
if res {
61+
t.Errorf("NotContains(%#v, %#v) should return true:\n\t%#v does not contains %#v", c.expected, c.actual, c.expected, c.actual)
62+
} else {
63+
t.Errorf("NotContains(%#v, %#v) should return false:\n\t%#v contains %#v", c.expected, c.actual, c.expected, c.actual)
64+
}
65+
}
66+
})
67+
}
68+
}

internal/helpers/empty.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package helpers
2+
3+
import "reflect"
4+
5+
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
6+
// a slice or a channel with len == 0.
7+
//
8+
// helpers.Empty(obj)
9+
func Empty(object interface{}) bool {
10+
return isEmpty(object)
11+
}
12+
13+
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
14+
// a slice or a channel with len == 0.
15+
//
16+
// if helpers.NotEmpty(obj) {
17+
// helpers.Equal("two", obj[1])
18+
// }
19+
func NotEmpty(object interface{}) bool {
20+
return !isEmpty(object)
21+
}
22+
23+
// isEmpty gets whether the specified object is considered empty or not.
24+
func isEmpty(object interface{}) bool {
25+
26+
// get nil case out of the way
27+
if object == nil {
28+
return true
29+
}
30+
31+
objValue := reflect.ValueOf(object)
32+
33+
switch objValue.Kind() {
34+
// collection types are empty when they have no element
35+
case reflect.Chan, reflect.Map, reflect.Slice:
36+
return objValue.Len() == 0
37+
// pointers are empty if nil or if the value they point to is empty
38+
case reflect.Ptr:
39+
if objValue.IsNil() {
40+
return true
41+
}
42+
deref := objValue.Elem().Interface()
43+
return isEmpty(deref)
44+
// for all other types, compare against the zero value
45+
// array types are empty when they match their zero-initialized state
46+
default:
47+
zero := reflect.Zero(objValue.Type())
48+
return reflect.DeepEqual(object, zero.Interface())
49+
}
50+
}

internal/helpers/empty_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package helpers
2+
3+
import (
4+
"errors"
5+
"os"
6+
"testing"
7+
"time"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestEmpty(t *testing.T) {
13+
14+
chWithValue := make(chan struct{}, 1)
15+
chWithValue <- struct{}{}
16+
var tiP *time.Time
17+
var tiNP time.Time
18+
var s *string
19+
var f *os.File
20+
sP := &s
21+
x := 1
22+
xP := &x
23+
24+
type TString string
25+
type TStruct struct {
26+
x int
27+
}
28+
29+
assert.True(t, Empty(""), "Empty string is empty")
30+
assert.True(t, Empty(nil), "Nil is empty")
31+
assert.True(t, Empty([]string{}), "Empty string array is empty")
32+
assert.True(t, Empty(0), "Zero int value is empty")
33+
assert.True(t, Empty(false), "False value is empty")
34+
assert.True(t, Empty(make(chan struct{})), "Channel without values is empty")
35+
assert.True(t, Empty(s), "Nil string pointer is empty")
36+
assert.True(t, Empty(f), "Nil os.File pointer is empty")
37+
assert.True(t, Empty(tiP), "Nil time.Time pointer is empty")
38+
assert.True(t, Empty(tiNP), "time.Time is empty")
39+
assert.True(t, Empty(TStruct{}), "struct with zero values is empty")
40+
assert.True(t, Empty(TString("")), "empty aliased string is empty")
41+
assert.True(t, Empty(sP), "ptr to nil value is empty")
42+
assert.True(t, Empty([1]int{}), "array is state")
43+
44+
assert.False(t, Empty("something"), "Non Empty string is not empty")
45+
assert.False(t, Empty(errors.New("something")), "Non nil object is not empty")
46+
assert.False(t, Empty([]string{"something"}), "Non empty string array is not empty")
47+
assert.False(t, Empty(1), "Non-zero int value is not empty")
48+
assert.False(t, Empty(true), "True value is not empty")
49+
assert.False(t, Empty(chWithValue), "Channel with values is not empty")
50+
assert.False(t, Empty(TStruct{x: 1}), "struct with initialized values is empty")
51+
assert.False(t, Empty(TString("abc")), "non-empty aliased string is empty")
52+
assert.False(t, Empty(xP), "ptr to non-nil value is not empty")
53+
assert.False(t, Empty([1]int{42}), "array is not state")
54+
}
55+
56+
func TestNotEmpty(t *testing.T) {
57+
chWithValue := make(chan struct{}, 1)
58+
chWithValue <- struct{}{}
59+
60+
assert.False(t, NotEmpty(""), "Empty string is empty")
61+
assert.False(t, NotEmpty(nil), "Nil is empty")
62+
assert.False(t, NotEmpty([]string{}), "Empty string array is empty")
63+
assert.False(t, NotEmpty(0), "Zero int value is empty")
64+
assert.False(t, NotEmpty(false), "False value is empty")
65+
assert.False(t, NotEmpty(make(chan struct{})), "Channel without values is empty")
66+
assert.False(t, NotEmpty([1]int{}), "array is state")
67+
68+
assert.True(t, NotEmpty("something"), "Non Empty string is not empty")
69+
assert.True(t, NotEmpty(errors.New("something")), "Non nil object is not empty")
70+
assert.True(t, NotEmpty([]string{"something"}), "Non empty string array is not empty")
71+
assert.True(t, NotEmpty(1), "Non-zero int value is not empty")
72+
assert.True(t, NotEmpty(true), "True value is not empty")
73+
assert.True(t, NotEmpty(chWithValue), "Channel with values is not empty")
74+
assert.True(t, NotEmpty([1]int{42}), "array is not state")
75+
}

internal/helpers/equal.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package helpers
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"reflect"
7+
)
8+
9+
// Equal asserts that two objects are equal.
10+
//
11+
// helpers.Equal(123, 123)
12+
//
13+
// Pointer variable equality is determined based on the equality of the
14+
// referenced values (as opposed to the memory addresses). Function equality
15+
// cannot be determined and will always fail.
16+
func Equal(expected, actual interface{}) bool {
17+
if err := validateEqualArgs(expected, actual); err != nil {
18+
// invalid operation
19+
return false
20+
}
21+
return ObjectsAreEqual(expected, actual)
22+
}
23+
24+
// NotEqual asserts that the specified values are NOT equal.
25+
//
26+
// helpers.NotEqual(obj1, obj2)
27+
//
28+
// Pointer variable equality is determined based on the equality of the
29+
// referenced values (as opposed to the memory addresses).
30+
func NotEqual(expected, actual interface{}, msgAndArgs ...interface{}) bool {
31+
if err := validateEqualArgs(expected, actual); err != nil {
32+
// invalid operation
33+
return false
34+
}
35+
36+
return !ObjectsAreEqual(expected, actual)
37+
}
38+
39+
// ObjectsAreEqual determines if two objects are considered equal.
40+
//
41+
// This function does no assertion of any kind.
42+
func ObjectsAreEqual(expected, actual interface{}) bool {
43+
if expected == nil || actual == nil {
44+
return expected == actual
45+
}
46+
47+
exp, ok := expected.([]byte)
48+
if !ok {
49+
return reflect.DeepEqual(expected, actual)
50+
}
51+
52+
act, ok := actual.([]byte)
53+
if !ok {
54+
return false
55+
}
56+
if exp == nil || act == nil {
57+
return exp == nil && act == nil
58+
}
59+
return bytes.Equal(exp, act)
60+
}
61+
62+
// validateEqualArgs checks whether provided arguments can be safely used in the
63+
// Equal/NotEqual functions.
64+
func validateEqualArgs(expected, actual interface{}) error {
65+
if expected == nil && actual == nil {
66+
return nil
67+
}
68+
69+
if isFunction(expected) || isFunction(actual) {
70+
return errors.New("cannot take func type as argument")
71+
}
72+
return nil
73+
}
74+
75+
func isFunction(arg interface{}) bool {
76+
if arg == nil {
77+
return false
78+
}
79+
return reflect.TypeOf(arg).Kind() == reflect.Func
80+
}

0 commit comments

Comments
 (0)