github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/xmap/should.go (about)

     1  package xmap
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/codingeasygo/util/converter"
    12  )
    13  
    14  const ShouldIsNil ShouldAction = "IsNil"
    15  const ShouldIsNoNil ShouldAction = "IsNoNil"
    16  const ShouldIsZero ShouldAction = "IsZero"
    17  const ShouldIsNoZero ShouldAction = "IsNoZero"
    18  const ShouldIsEmpty ShouldAction = "IsEmpty"
    19  const ShouldIsNoEmpty ShouldAction = "IsNoEmpty"
    20  const ShouldIsInt ShouldAction = "IsInt"
    21  const ShouldIsUint ShouldAction = "IsUint"
    22  const ShouldIsFloat ShouldAction = "IsFloat"
    23  const ShouldEQ ShouldAction = "EQ"
    24  const ShouldGT ShouldAction = "GT"
    25  const ShouldGTE ShouldAction = "GTE"
    26  const ShouldLT ShouldAction = "LT"
    27  const ShouldLTE ShouldAction = "LTE"
    28  
    29  type ShouldAction string
    30  
    31  func (s ShouldAction) isEmpty(val reflect.Value) bool {
    32  	return (val.Kind() == reflect.Map || val.Kind() == reflect.Slice || val.Kind() == reflect.Array || val.Kind() == reflect.String) && val.Len() == 0
    33  }
    34  
    35  func (s ShouldAction) Compare(x, y interface{}) bool {
    36  	a, b := reflect.ValueOf(x), reflect.ValueOf(y)
    37  	if a.CanInt() && b.CanInt() {
    38  		av := a.Convert(reflect.TypeOf(int64(0))).Interface().(int64)
    39  		bv := b.Convert(reflect.TypeOf(int64(0))).Interface().(int64)
    40  		if s == ShouldEQ && av != bv {
    41  			return false
    42  		}
    43  		if s == ShouldGT && av <= bv {
    44  			return false
    45  		}
    46  		if s == ShouldGTE && av < bv {
    47  			return false
    48  		}
    49  		if s == ShouldLT && av >= bv {
    50  			return false
    51  		}
    52  		if s == ShouldLTE && av > bv {
    53  			return false
    54  		}
    55  		return true
    56  	}
    57  	if a.CanUint() && b.CanUint() {
    58  		av := a.Convert(reflect.TypeOf(uint64(0))).Interface().(uint64)
    59  		bv := b.Convert(reflect.TypeOf(uint64(0))).Interface().(uint64)
    60  		if s == ShouldEQ && av != bv {
    61  			return false
    62  		}
    63  		if s == ShouldGT && av <= bv {
    64  			return false
    65  		}
    66  		if s == ShouldGTE && av < bv {
    67  			return false
    68  		}
    69  		if s == ShouldLT && av >= bv {
    70  			return false
    71  		}
    72  		if s == ShouldLTE && av > bv {
    73  			return false
    74  		}
    75  		return true
    76  	}
    77  	if a.CanFloat() && b.CanFloat() {
    78  		av := a.Convert(reflect.TypeOf(float64(0))).Interface().(float64)
    79  		bv := b.Convert(reflect.TypeOf(float64(0))).Interface().(float64)
    80  		if s == ShouldEQ && av != bv {
    81  			return false
    82  		}
    83  		if s == ShouldGT && av <= bv {
    84  			return false
    85  		}
    86  		if s == ShouldGTE && av < bv {
    87  			return false
    88  		}
    89  		if s == ShouldLT && av >= bv {
    90  			return false
    91  		}
    92  		if s == ShouldLTE && av > bv {
    93  			return false
    94  		}
    95  		return true
    96  	}
    97  	return ValueEqual(x, y)
    98  }
    99  
   100  func (s ShouldAction) Check(v interface{}) bool {
   101  	val := reflect.ValueOf(v)
   102  	if !val.IsValid() {
   103  		return s == ShouldIsNil || s == ShouldIsZero
   104  	}
   105  	if s == ShouldIsNil && (val.Kind() != reflect.Ptr || (val.Kind() == reflect.Ptr && !val.IsNil())) {
   106  		return false
   107  	}
   108  	if s == ShouldIsNoNil && val.Kind() == reflect.Ptr && val.IsNil() {
   109  		return false
   110  	}
   111  	if s == ShouldIsZero && !val.IsZero() {
   112  		return false
   113  	}
   114  	if s == ShouldIsNoZero && val.IsZero() {
   115  		return false
   116  	}
   117  	if s == ShouldIsEmpty && !s.isEmpty(val) {
   118  		return false
   119  	}
   120  	if s == ShouldIsNoEmpty && s.isEmpty(val) {
   121  		return false
   122  	}
   123  	if s == ShouldIsInt && !val.CanInt() {
   124  		return false
   125  	}
   126  	if s == ShouldIsUint && !val.CanUint() {
   127  		return false
   128  	}
   129  	if s == ShouldIsFloat && !val.CanFloat() {
   130  		return false
   131  	}
   132  	return true
   133  }
   134  
   135  func (m M) Should(args ...interface{}) (err error) {
   136  	n := len(args)
   137  	for i := 0; i < n; {
   138  		if i+1 >= n {
   139  			err = fmt.Errorf("args[%v] action is not setted", i)
   140  			break
   141  		}
   142  		key, ok := args[i].(string)
   143  		if !ok {
   144  			err = fmt.Errorf("args[%v] key is not string", i)
   145  			break
   146  		}
   147  		val := m.Value(key)
   148  		action, ok := args[i+1].(ShouldAction)
   149  		if !ok {
   150  			if !ValueEqual(val, args[i+1]) {
   151  				err = fmt.Errorf("m.%v(%v,%v)!=args[%v](%v,%v)", key, reflect.TypeOf(val), val, i+1, reflect.TypeOf(args[i+1]), args[i+1])
   152  				break
   153  			}
   154  			i += 2
   155  			continue
   156  		}
   157  		if strings.HasPrefix(string(action), "Is") {
   158  			if !action.Check(val) {
   159  				err = fmt.Errorf("m.%v(%v,%v)!=%v", key, reflect.TypeOf(val), val, action)
   160  				break
   161  			}
   162  			i += 2
   163  			continue
   164  		}
   165  		if i+2 >= n {
   166  			err = fmt.Errorf("args[%v] compare value is not setted", i)
   167  			break
   168  		}
   169  		if !action.Compare(val, args[i+2]) {
   170  			err = fmt.Errorf("m.%v(%v,%v) %v args[%v](%v,%v)", key, reflect.TypeOf(val), val, action, i+2, reflect.TypeOf(args[i+2]), args[i+2])
   171  			break
   172  		}
   173  		i += 3
   174  	}
   175  	return
   176  }
   177  
   178  type Shoulder struct {
   179  	Log        *log.Logger
   180  	testerFail func()
   181  	testerSkip func()
   182  	shouldErr  bool
   183  	shouldArgs []interface{}
   184  	onlyLog    bool
   185  }
   186  
   187  func (s *Shoulder) Should(t *testing.T, args ...interface{}) *Shoulder {
   188  	if t != nil {
   189  		s.testerFail, s.testerSkip, s.shouldArgs = t.Fail, t.SkipNow, args
   190  	}
   191  	s.shouldArgs = append(s.shouldArgs, args...)
   192  	return s
   193  }
   194  
   195  func (s *Shoulder) ShouldError(t *testing.T) *Shoulder {
   196  	if t != nil {
   197  		s.testerFail, s.testerSkip = t.Fail, t.SkipNow
   198  	}
   199  	s.shouldErr = true
   200  	return s
   201  }
   202  
   203  func (s *Shoulder) OnlyLog(only bool) *Shoulder {
   204  	s.onlyLog = only
   205  	return s
   206  }
   207  
   208  func (s *Shoulder) callError(depth int, err error) {
   209  	if s.testerFail == nil {
   210  		panic(err)
   211  	}
   212  	if s.Log == nil {
   213  		s.Log = log.New(os.Stderr, "    ", log.Llongfile)
   214  	}
   215  	s.Log.Output(depth, err.Error())
   216  	if !s.onlyLog {
   217  		s.testerFail()
   218  		s.testerSkip()
   219  	}
   220  }
   221  
   222  func (s *Shoulder) validError(depth int, res M, err error) bool {
   223  	if err != nil {
   224  		s.callError(depth+1, fmt.Errorf("%v, res is %v", err, converter.JSON(res)))
   225  		return false
   226  	}
   227  	return true
   228  }
   229  
   230  func (s *Shoulder) validShould(depth int, res M, err error) bool {
   231  	if len(s.shouldArgs) < 1 {
   232  		return true
   233  	}
   234  	xerr := res.Should(s.shouldArgs...)
   235  	if xerr != nil {
   236  		s.callError(depth+1, fmt.Errorf("%v, res is %v", xerr, converter.JSON(res)))
   237  		return false
   238  	}
   239  	return true
   240  }
   241  
   242  func (s *Shoulder) Valid(depth int, res M, err error) bool {
   243  	if s.shouldErr {
   244  		if err == nil {
   245  			s.callError(depth+1, fmt.Errorf("err is nil, res is %v", converter.JSON(res)))
   246  			return false
   247  		}
   248  	} else {
   249  		if !s.validError(depth+1, res, err) {
   250  			return false
   251  		}
   252  		if !s.validShould(depth+1, res, err) {
   253  			return false
   254  		}
   255  	}
   256  	return true
   257  }