github.com/rigado/snapd@v2.42.5-go-mod+incompatible/testutil/containschecker.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package testutil
    21  
    22  import (
    23  	"fmt"
    24  	"reflect"
    25  	"strings"
    26  
    27  	"gopkg.in/check.v1"
    28  )
    29  
    30  type containsChecker struct {
    31  	*check.CheckerInfo
    32  }
    33  
    34  // Contains is a Checker that looks for a elem in a container.
    35  // The elem can be any object. The container can be an array, slice or string.
    36  var Contains check.Checker = &containsChecker{
    37  	&check.CheckerInfo{Name: "Contains", Params: []string{"container", "elem"}},
    38  }
    39  
    40  func commonEquals(container, elem interface{}, result *bool, error *string) bool {
    41  	containerV := reflect.ValueOf(container)
    42  	elemV := reflect.ValueOf(elem)
    43  	switch containerV.Kind() {
    44  	case reflect.Slice, reflect.Array, reflect.Map:
    45  		containerElemType := containerV.Type().Elem()
    46  		if containerElemType.Kind() == reflect.Interface {
    47  			// Ensure that element implements the type of elements stored in the container.
    48  			if !elemV.Type().Implements(containerElemType) {
    49  				*result = false
    50  				*error = fmt.Sprintf(""+
    51  					"container has items of interface type %s but expected"+
    52  					" element does not implement it", containerElemType)
    53  				return true
    54  			}
    55  		} else {
    56  			// Ensure that type of elements in container is compatible with elem
    57  			if containerElemType != elemV.Type() {
    58  				*result = false
    59  				*error = fmt.Sprintf(
    60  					"container has items of type %s but expected element is a %s",
    61  					containerElemType, elemV.Type())
    62  				return true
    63  			}
    64  		}
    65  	case reflect.String:
    66  		// When container is a string, we expect elem to be a string as well
    67  		if elemV.Kind() != reflect.String {
    68  			*result = false
    69  			*error = fmt.Sprintf("element is a %T but expected a string", elem)
    70  		} else {
    71  			*result = strings.Contains(containerV.String(), elemV.String())
    72  			*error = ""
    73  		}
    74  		return true
    75  	}
    76  	return false
    77  }
    78  
    79  func (c *containsChecker) Check(params []interface{}, names []string) (result bool, error string) {
    80  	defer func() {
    81  		if v := recover(); v != nil {
    82  			result = false
    83  			error = fmt.Sprint(v)
    84  		}
    85  	}()
    86  	var container interface{} = params[0]
    87  	var elem interface{} = params[1]
    88  	if commonEquals(container, elem, &result, &error) {
    89  		return
    90  	}
    91  	// Do the actual test using ==
    92  	switch containerV := reflect.ValueOf(container); containerV.Kind() {
    93  	case reflect.Slice, reflect.Array:
    94  		for length, i := containerV.Len(), 0; i < length; i++ {
    95  			itemV := containerV.Index(i)
    96  			if itemV.Interface() == elem {
    97  				return true, ""
    98  			}
    99  		}
   100  		return false, ""
   101  	case reflect.Map:
   102  		for _, keyV := range containerV.MapKeys() {
   103  			itemV := containerV.MapIndex(keyV)
   104  			if itemV.Interface() == elem {
   105  				return true, ""
   106  			}
   107  		}
   108  		return false, ""
   109  	default:
   110  		return false, fmt.Sprintf("%T is not a supported container", container)
   111  	}
   112  }
   113  
   114  type deepContainsChecker struct {
   115  	*check.CheckerInfo
   116  }
   117  
   118  // DeepContains is a Checker that looks for a elem in a container using
   119  // DeepEqual.  The elem can be any object. The container can be an array, slice
   120  // or string.
   121  var DeepContains check.Checker = &deepContainsChecker{
   122  	&check.CheckerInfo{Name: "DeepContains", Params: []string{"container", "elem"}},
   123  }
   124  
   125  func (c *deepContainsChecker) Check(params []interface{}, names []string) (result bool, error string) {
   126  	var container interface{} = params[0]
   127  	var elem interface{} = params[1]
   128  	if commonEquals(container, elem, &result, &error) {
   129  		return
   130  	}
   131  	// Do the actual test using reflect.DeepEqual
   132  	switch containerV := reflect.ValueOf(container); containerV.Kind() {
   133  	case reflect.Slice, reflect.Array:
   134  		for length, i := containerV.Len(), 0; i < length; i++ {
   135  			itemV := containerV.Index(i)
   136  			if reflect.DeepEqual(itemV.Interface(), elem) {
   137  				return true, ""
   138  			}
   139  		}
   140  		return false, ""
   141  	case reflect.Map:
   142  		for _, keyV := range containerV.MapKeys() {
   143  			itemV := containerV.MapIndex(keyV)
   144  			if reflect.DeepEqual(itemV.Interface(), elem) {
   145  				return true, ""
   146  			}
   147  		}
   148  		return false, ""
   149  	default:
   150  		return false, fmt.Sprintf("%T is not a supported container", container)
   151  	}
   152  }