identical_to.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // Copyright 2012 Aaron Jacobs. All Rights Reserved.
  2. // Author: aaronjjacobs@gmail.com (Aaron Jacobs)
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. package oglematchers
  16. import (
  17. "errors"
  18. "fmt"
  19. "reflect"
  20. )
  21. // Is the type comparable according to the definition here?
  22. //
  23. // http://weekly.golang.org/doc/go_spec.html#Comparison_operators
  24. //
  25. func isComparable(t reflect.Type) bool {
  26. switch t.Kind() {
  27. case reflect.Array:
  28. return isComparable(t.Elem())
  29. case reflect.Struct:
  30. for i := 0; i < t.NumField(); i++ {
  31. if !isComparable(t.Field(i).Type) {
  32. return false
  33. }
  34. }
  35. return true
  36. case reflect.Slice, reflect.Map, reflect.Func:
  37. return false
  38. }
  39. return true
  40. }
  41. // Should the supplied type be allowed as an argument to IdenticalTo?
  42. func isLegalForIdenticalTo(t reflect.Type) (bool, error) {
  43. // Allow the zero type.
  44. if t == nil {
  45. return true, nil
  46. }
  47. // Reference types are always okay; we compare pointers.
  48. switch t.Kind() {
  49. case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
  50. return true, nil
  51. }
  52. // Reject other non-comparable types.
  53. if !isComparable(t) {
  54. return false, errors.New(fmt.Sprintf("%v is not comparable", t))
  55. }
  56. return true, nil
  57. }
  58. // IdenticalTo(x) returns a matcher that matches values v with type identical
  59. // to x such that:
  60. //
  61. // 1. If v and x are of a reference type (slice, map, function, channel), then
  62. // they are either both nil or are references to the same object.
  63. //
  64. // 2. Otherwise, if v and x are not of a reference type but have a valid type,
  65. // then v == x.
  66. //
  67. // If v and x are both the invalid type (which results from the predeclared nil
  68. // value, or from nil interface variables), then the matcher is satisfied.
  69. //
  70. // This function will panic if x is of a value type that is not comparable. For
  71. // example, x cannot be an array of functions.
  72. func IdenticalTo(x interface{}) Matcher {
  73. t := reflect.TypeOf(x)
  74. // Reject illegal arguments.
  75. if ok, err := isLegalForIdenticalTo(t); !ok {
  76. panic("IdenticalTo: " + err.Error())
  77. }
  78. return &identicalToMatcher{x}
  79. }
  80. type identicalToMatcher struct {
  81. x interface{}
  82. }
  83. func (m *identicalToMatcher) Description() string {
  84. t := reflect.TypeOf(m.x)
  85. return fmt.Sprintf("identical to <%v> %v", t, m.x)
  86. }
  87. func (m *identicalToMatcher) Matches(c interface{}) error {
  88. // Make sure the candidate's type is correct.
  89. t := reflect.TypeOf(m.x)
  90. if ct := reflect.TypeOf(c); t != ct {
  91. return NewFatalError(fmt.Sprintf("which is of type %v", ct))
  92. }
  93. // Special case: two values of the invalid type are always identical.
  94. if t == nil {
  95. return nil
  96. }
  97. // Handle reference types.
  98. switch t.Kind() {
  99. case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
  100. xv := reflect.ValueOf(m.x)
  101. cv := reflect.ValueOf(c)
  102. if xv.Pointer() == cv.Pointer() {
  103. return nil
  104. }
  105. return errors.New("which is not an identical reference")
  106. }
  107. // Are the values equal?
  108. if m.x == c {
  109. return nil
  110. }
  111. return errors.New("")
  112. }