summaryrefslogtreecommitdiff
path: root/vendor/github.com/onsi
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/onsi')
-rw-r--r--vendor/github.com/onsi/gomega/gstruct/elements.go231
-rw-r--r--vendor/github.com/onsi/gomega/gstruct/errors/nested_types.go72
-rw-r--r--vendor/github.com/onsi/gomega/gstruct/fields.go165
-rw-r--r--vendor/github.com/onsi/gomega/gstruct/ignore.go39
-rw-r--r--vendor/github.com/onsi/gomega/gstruct/keys.go126
-rw-r--r--vendor/github.com/onsi/gomega/gstruct/pointer.go58
-rw-r--r--vendor/github.com/onsi/gomega/gstruct/types.go15
7 files changed, 706 insertions, 0 deletions
diff --git a/vendor/github.com/onsi/gomega/gstruct/elements.go b/vendor/github.com/onsi/gomega/gstruct/elements.go
new file mode 100644
index 000000000..b5e5ef2e4
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gstruct/elements.go
@@ -0,0 +1,231 @@
+// untested sections: 6
+
+package gstruct
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "runtime/debug"
+ "strconv"
+
+ "github.com/onsi/gomega/format"
+ errorsutil "github.com/onsi/gomega/gstruct/errors"
+ "github.com/onsi/gomega/types"
+)
+
+//MatchAllElements succeeds if every element of a slice matches the element matcher it maps to
+//through the id function, and every element matcher is matched.
+// idFn := func(element interface{}) string {
+// return fmt.Sprintf("%v", element)
+// }
+//
+// Expect([]string{"a", "b"}).To(MatchAllElements(idFn, Elements{
+// "a": Equal("a"),
+// "b": Equal("b"),
+// }))
+func MatchAllElements(identifier Identifier, elements Elements) types.GomegaMatcher {
+ return &ElementsMatcher{
+ Identifier: identifier,
+ Elements: elements,
+ }
+}
+
+//MatchAllElementsWithIndex succeeds if every element of a slice matches the element matcher it maps to
+//through the id with index function, and every element matcher is matched.
+// idFn := func(index int, element interface{}) string {
+// return strconv.Itoa(index)
+// }
+//
+// Expect([]string{"a", "b"}).To(MatchAllElements(idFn, Elements{
+// "0": Equal("a"),
+// "1": Equal("b"),
+// }))
+func MatchAllElementsWithIndex(identifier IdentifierWithIndex, elements Elements) types.GomegaMatcher {
+ return &ElementsMatcher{
+ Identifier: identifier,
+ Elements: elements,
+ }
+}
+
+//MatchElements succeeds if each element of a slice matches the element matcher it maps to
+//through the id function. It can ignore extra elements and/or missing elements.
+// idFn := func(element interface{}) string {
+// return fmt.Sprintf("%v", element)
+// }
+//
+// Expect([]string{"a", "b", "c"}).To(MatchElements(idFn, IgnoreExtras, Elements{
+// "a": Equal("a"),
+// "b": Equal("b"),
+// }))
+// Expect([]string{"a", "c"}).To(MatchElements(idFn, IgnoreMissing, Elements{
+// "a": Equal("a"),
+// "b": Equal("b"),
+// "c": Equal("c"),
+// "d": Equal("d"),
+// }))
+func MatchElements(identifier Identifier, options Options, elements Elements) types.GomegaMatcher {
+ return &ElementsMatcher{
+ Identifier: identifier,
+ Elements: elements,
+ IgnoreExtras: options&IgnoreExtras != 0,
+ IgnoreMissing: options&IgnoreMissing != 0,
+ AllowDuplicates: options&AllowDuplicates != 0,
+ }
+}
+
+//MatchElementsWithIndex succeeds if each element of a slice matches the element matcher it maps to
+//through the id with index function. It can ignore extra elements and/or missing elements.
+// idFn := func(index int, element interface{}) string {
+// return strconv.Itoa(index)
+// }
+//
+// Expect([]string{"a", "b", "c"}).To(MatchElements(idFn, IgnoreExtras, Elements{
+// "0": Equal("a"),
+// "1": Equal("b"),
+// }))
+// Expect([]string{"a", "c"}).To(MatchElements(idFn, IgnoreMissing, Elements{
+// "0": Equal("a"),
+// "1": Equal("b"),
+// "2": Equal("c"),
+// "3": Equal("d"),
+// }))
+func MatchElementsWithIndex(identifier IdentifierWithIndex, options Options, elements Elements) types.GomegaMatcher {
+ return &ElementsMatcher{
+ Identifier: identifier,
+ Elements: elements,
+ IgnoreExtras: options&IgnoreExtras != 0,
+ IgnoreMissing: options&IgnoreMissing != 0,
+ AllowDuplicates: options&AllowDuplicates != 0,
+ }
+}
+
+// ElementsMatcher is a NestingMatcher that applies custom matchers to each element of a slice mapped
+// by the Identifier function.
+// TODO: Extend this to work with arrays & maps (map the key) as well.
+type ElementsMatcher struct {
+ // Matchers for each element.
+ Elements Elements
+ // Function mapping an element to the string key identifying its matcher.
+ Identifier Identify
+
+ // Whether to ignore extra elements or consider it an error.
+ IgnoreExtras bool
+ // Whether to ignore missing elements or consider it an error.
+ IgnoreMissing bool
+ // Whether to key duplicates when matching IDs.
+ AllowDuplicates bool
+
+ // State.
+ failures []error
+}
+
+// Element ID to matcher.
+type Elements map[string]types.GomegaMatcher
+
+// Function for identifying (mapping) elements.
+type Identifier func(element interface{}) string
+
+// Calls the underlying fucntion with the provided params.
+// Identifier drops the index.
+func (i Identifier) WithIndexAndElement(index int, element interface{}) string {
+ return i(element)
+}
+
+// Uses the index and element to generate an element name
+type IdentifierWithIndex func(index int, element interface{}) string
+
+// Calls the underlying fucntion with the provided params.
+// IdentifierWithIndex uses the index.
+func (i IdentifierWithIndex) WithIndexAndElement(index int, element interface{}) string {
+ return i(index, element)
+}
+
+// Interface for identifing the element
+type Identify interface {
+ WithIndexAndElement(i int, element interface{}) string
+}
+
+// IndexIdentity is a helper function for using an index as
+// the key in the element map
+func IndexIdentity(index int, _ interface{}) string {
+ return strconv.Itoa(index)
+}
+
+func (m *ElementsMatcher) Match(actual interface{}) (success bool, err error) {
+ if reflect.TypeOf(actual).Kind() != reflect.Slice {
+ return false, fmt.Errorf("%v is type %T, expected slice", actual, actual)
+ }
+
+ m.failures = m.matchElements(actual)
+ if len(m.failures) > 0 {
+ return false, nil
+ }
+ return true, nil
+}
+
+func (m *ElementsMatcher) matchElements(actual interface{}) (errs []error) {
+ // Provide more useful error messages in the case of a panic.
+ defer func() {
+ if err := recover(); err != nil {
+ errs = append(errs, fmt.Errorf("panic checking %+v: %v\n%s", actual, err, debug.Stack()))
+ }
+ }()
+
+ val := reflect.ValueOf(actual)
+ elements := map[string]bool{}
+ for i := 0; i < val.Len(); i++ {
+ element := val.Index(i).Interface()
+ id := m.Identifier.WithIndexAndElement(i, element)
+ if elements[id] {
+ if !m.AllowDuplicates {
+ errs = append(errs, fmt.Errorf("found duplicate element ID %s", id))
+ continue
+ }
+ }
+ elements[id] = true
+
+ matcher, expected := m.Elements[id]
+ if !expected {
+ if !m.IgnoreExtras {
+ errs = append(errs, fmt.Errorf("unexpected element %s", id))
+ }
+ continue
+ }
+
+ match, err := matcher.Match(element)
+ if match {
+ continue
+ }
+
+ if err == nil {
+ if nesting, ok := matcher.(errorsutil.NestingMatcher); ok {
+ err = errorsutil.AggregateError(nesting.Failures())
+ } else {
+ err = errors.New(matcher.FailureMessage(element))
+ }
+ }
+ errs = append(errs, errorsutil.Nest(fmt.Sprintf("[%s]", id), err))
+ }
+
+ for id := range m.Elements {
+ if !elements[id] && !m.IgnoreMissing {
+ errs = append(errs, fmt.Errorf("missing expected element %s", id))
+ }
+ }
+
+ return errs
+}
+
+func (m *ElementsMatcher) FailureMessage(actual interface{}) (message string) {
+ failure := errorsutil.AggregateError(m.failures)
+ return format.Message(actual, fmt.Sprintf("to match elements: %v", failure))
+}
+
+func (m *ElementsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+ return format.Message(actual, "not to match elements")
+}
+
+func (m *ElementsMatcher) Failures() []error {
+ return m.failures
+}
diff --git a/vendor/github.com/onsi/gomega/gstruct/errors/nested_types.go b/vendor/github.com/onsi/gomega/gstruct/errors/nested_types.go
new file mode 100644
index 000000000..188492b21
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gstruct/errors/nested_types.go
@@ -0,0 +1,72 @@
+package errors
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/onsi/gomega/types"
+)
+
+// A stateful matcher that nests other matchers within it and preserves the error types of the
+// nested matcher failures.
+type NestingMatcher interface {
+ types.GomegaMatcher
+
+ // Returns the failures of nested matchers.
+ Failures() []error
+}
+
+// An error type for labeling errors on deeply nested matchers.
+type NestedError struct {
+ Path string
+ Err error
+}
+
+func (e *NestedError) Error() string {
+ // Indent Errors.
+ indented := strings.Replace(e.Err.Error(), "\n", "\n\t", -1)
+ return fmt.Sprintf("%s:\n\t%v", e.Path, indented)
+}
+
+// Create a NestedError with the given path.
+// If err is a NestedError, prepend the path to it.
+// If err is an AggregateError, recursively Nest each error.
+func Nest(path string, err error) error {
+ if ag, ok := err.(AggregateError); ok {
+ var errs AggregateError
+ for _, e := range ag {
+ errs = append(errs, Nest(path, e))
+ }
+ return errs
+ }
+ if ne, ok := err.(*NestedError); ok {
+ return &NestedError{
+ Path: path + ne.Path,
+ Err: ne.Err,
+ }
+ }
+ return &NestedError{
+ Path: path,
+ Err: err,
+ }
+}
+
+// An error type for treating multiple errors as a single error.
+type AggregateError []error
+
+// Error is part of the error interface.
+func (err AggregateError) Error() string {
+ if len(err) == 0 {
+ // This should never happen, really.
+ return ""
+ }
+ if len(err) == 1 {
+ return err[0].Error()
+ }
+ result := fmt.Sprintf("[%s", err[0].Error())
+ for i := 1; i < len(err); i++ {
+ result += fmt.Sprintf(", %s", err[i].Error())
+ }
+ result += "]"
+ return result
+}
diff --git a/vendor/github.com/onsi/gomega/gstruct/fields.go b/vendor/github.com/onsi/gomega/gstruct/fields.go
new file mode 100644
index 000000000..faf07b1a2
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gstruct/fields.go
@@ -0,0 +1,165 @@
+// untested sections: 6
+
+package gstruct
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "runtime/debug"
+ "strings"
+
+ "github.com/onsi/gomega/format"
+ errorsutil "github.com/onsi/gomega/gstruct/errors"
+ "github.com/onsi/gomega/types"
+)
+
+//MatchAllFields succeeds if every field of a struct matches the field matcher associated with
+//it, and every element matcher is matched.
+// actual := struct{
+// A int
+// B []bool
+// C string
+// }{
+// A: 5,
+// B: []bool{true, false},
+// C: "foo",
+// }
+//
+// Expect(actual).To(MatchAllFields(Fields{
+// "A": Equal(5),
+// "B": ConsistOf(true, false),
+// "C": Equal("foo"),
+// }))
+func MatchAllFields(fields Fields) types.GomegaMatcher {
+ return &FieldsMatcher{
+ Fields: fields,
+ }
+}
+
+//MatchFields succeeds if each element of a struct matches the field matcher associated with
+//it. It can ignore extra fields and/or missing fields.
+// actual := struct{
+// A int
+// B []bool
+// C string
+// }{
+// A: 5,
+// B: []bool{true, false},
+// C: "foo",
+// }
+//
+// Expect(actual).To(MatchFields(IgnoreExtras, Fields{
+// "A": Equal(5),
+// "B": ConsistOf(true, false),
+// }))
+// Expect(actual).To(MatchFields(IgnoreMissing, Fields{
+// "A": Equal(5),
+// "B": ConsistOf(true, false),
+// "C": Equal("foo"),
+// "D": Equal("extra"),
+// }))
+func MatchFields(options Options, fields Fields) types.GomegaMatcher {
+ return &FieldsMatcher{
+ Fields: fields,
+ IgnoreExtras: options&IgnoreExtras != 0,
+ IgnoreMissing: options&IgnoreMissing != 0,
+ }
+}
+
+type FieldsMatcher struct {
+ // Matchers for each field.
+ Fields Fields
+
+ // Whether to ignore extra elements or consider it an error.
+ IgnoreExtras bool
+ // Whether to ignore missing elements or consider it an error.
+ IgnoreMissing bool
+
+ // State.
+ failures []error
+}
+
+// Field name to matcher.
+type Fields map[string]types.GomegaMatcher
+
+func (m *FieldsMatcher) Match(actual interface{}) (success bool, err error) {
+ if reflect.TypeOf(actual).Kind() != reflect.Struct {
+ return false, fmt.Errorf("%v is type %T, expected struct", actual, actual)
+ }
+
+ m.failures = m.matchFields(actual)
+ if len(m.failures) > 0 {
+ return false, nil
+ }
+ return true, nil
+}
+
+func (m *FieldsMatcher) matchFields(actual interface{}) (errs []error) {
+ val := reflect.ValueOf(actual)
+ typ := val.Type()
+ fields := map[string]bool{}
+ for i := 0; i < val.NumField(); i++ {
+ fieldName := typ.Field(i).Name
+ fields[fieldName] = true
+
+ err := func() (err error) {
+ // This test relies heavily on reflect, which tends to panic.
+ // Recover here to provide more useful error messages in that case.
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("panic checking %+v: %v\n%s", actual, r, debug.Stack())
+ }
+ }()
+
+ matcher, expected := m.Fields[fieldName]
+ if !expected {
+ if !m.IgnoreExtras {
+ return fmt.Errorf("unexpected field %s: %+v", fieldName, actual)
+ }
+ return nil
+ }
+
+ field := val.Field(i).Interface()
+
+ match, err := matcher.Match(field)
+ if err != nil {
+ return err
+ } else if !match {
+ if nesting, ok := matcher.(errorsutil.NestingMatcher); ok {
+ return errorsutil.AggregateError(nesting.Failures())
+ }
+ return errors.New(matcher.FailureMessage(field))
+ }
+ return nil
+ }()
+ if err != nil {
+ errs = append(errs, errorsutil.Nest("."+fieldName, err))
+ }
+ }
+
+ for field := range m.Fields {
+ if !fields[field] && !m.IgnoreMissing {
+ errs = append(errs, fmt.Errorf("missing expected field %s", field))
+ }
+ }
+
+ return errs
+}
+
+func (m *FieldsMatcher) FailureMessage(actual interface{}) (message string) {
+ failures := make([]string, len(m.failures))
+ for i := range m.failures {
+ failures[i] = m.failures[i].Error()
+ }
+ return format.Message(reflect.TypeOf(actual).Name(),
+ fmt.Sprintf("to match fields: {\n%v\n}\n", strings.Join(failures, "\n")))
+}
+
+func (m *FieldsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+ return format.Message(actual, "not to match fields")
+}
+
+func (m *FieldsMatcher) Failures() []error {
+ return m.failures
+}
diff --git a/vendor/github.com/onsi/gomega/gstruct/ignore.go b/vendor/github.com/onsi/gomega/gstruct/ignore.go
new file mode 100644
index 000000000..4396573e4
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gstruct/ignore.go
@@ -0,0 +1,39 @@
+// untested sections: 2
+
+package gstruct
+
+import (
+ "github.com/onsi/gomega/types"
+)
+
+//Ignore ignores the actual value and always succeeds.
+// Expect(nil).To(Ignore())
+// Expect(true).To(Ignore())
+func Ignore() types.GomegaMatcher {
+ return &IgnoreMatcher{true}
+}
+
+//Reject ignores the actual value and always fails. It can be used in conjunction with IgnoreMissing
+//to catch problematic elements, or to verify tests are running.
+// Expect(nil).NotTo(Reject())
+// Expect(true).NotTo(Reject())
+func Reject() types.GomegaMatcher {
+ return &IgnoreMatcher{false}
+}
+
+// A matcher that either always succeeds or always fails.
+type IgnoreMatcher struct {
+ Succeed bool
+}
+
+func (m *IgnoreMatcher) Match(actual interface{}) (bool, error) {
+ return m.Succeed, nil
+}
+
+func (m *IgnoreMatcher) FailureMessage(_ interface{}) (message string) {
+ return "Unconditional failure"
+}
+
+func (m *IgnoreMatcher) NegatedFailureMessage(_ interface{}) (message string) {
+ return "Unconditional success"
+}
diff --git a/vendor/github.com/onsi/gomega/gstruct/keys.go b/vendor/github.com/onsi/gomega/gstruct/keys.go
new file mode 100644
index 000000000..56aed4bab
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gstruct/keys.go
@@ -0,0 +1,126 @@
+// untested sections: 6
+
+package gstruct
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "runtime/debug"
+ "strings"
+
+ "github.com/onsi/gomega/format"
+ errorsutil "github.com/onsi/gomega/gstruct/errors"
+ "github.com/onsi/gomega/types"
+)
+
+func MatchAllKeys(keys Keys) types.GomegaMatcher {
+ return &KeysMatcher{
+ Keys: keys,
+ }
+}
+
+func MatchKeys(options Options, keys Keys) types.GomegaMatcher {
+ return &KeysMatcher{
+ Keys: keys,
+ IgnoreExtras: options&IgnoreExtras != 0,
+ IgnoreMissing: options&IgnoreMissing != 0,
+ }
+}
+
+type KeysMatcher struct {
+ // Matchers for each key.
+ Keys Keys
+
+ // Whether to ignore extra keys or consider it an error.
+ IgnoreExtras bool
+ // Whether to ignore missing keys or consider it an error.
+ IgnoreMissing bool
+
+ // State.
+ failures []error
+}
+
+type Keys map[interface{}]types.GomegaMatcher
+
+func (m *KeysMatcher) Match(actual interface{}) (success bool, err error) {
+ if reflect.TypeOf(actual).Kind() != reflect.Map {
+ return false, fmt.Errorf("%v is type %T, expected map", actual, actual)
+ }
+
+ m.failures = m.matchKeys(actual)
+ if len(m.failures) > 0 {
+ return false, nil
+ }
+ return true, nil
+}
+
+func (m *KeysMatcher) matchKeys(actual interface{}) (errs []error) {
+ actualValue := reflect.ValueOf(actual)
+ keys := map[interface{}]bool{}
+ for _, keyValue := range actualValue.MapKeys() {
+ key := keyValue.Interface()
+ keys[key] = true
+
+ err := func() (err error) {
+ // This test relies heavily on reflect, which tends to panic.
+ // Recover here to provide more useful error messages in that case.
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("panic checking %+v: %v\n%s", actual, r, debug.Stack())
+ }
+ }()
+
+ matcher, ok := m.Keys[key]
+ if !ok {
+ if !m.IgnoreExtras {
+ return fmt.Errorf("unexpected key %s: %+v", key, actual)
+ }
+ return nil
+ }
+
+ valInterface := actualValue.MapIndex(keyValue).Interface()
+
+ match, err := matcher.Match(valInterface)
+ if err != nil {
+ return err
+ }
+
+ if !match {
+ if nesting, ok := matcher.(errorsutil.NestingMatcher); ok {
+ return errorsutil.AggregateError(nesting.Failures())
+ }
+ return errors.New(matcher.FailureMessage(valInterface))
+ }
+ return nil
+ }()
+ if err != nil {
+ errs = append(errs, errorsutil.Nest(fmt.Sprintf(".%#v", key), err))
+ }
+ }
+
+ for key := range m.Keys {
+ if !keys[key] && !m.IgnoreMissing {
+ errs = append(errs, fmt.Errorf("missing expected key %s", key))
+ }
+ }
+
+ return errs
+}
+
+func (m *KeysMatcher) FailureMessage(actual interface{}) (message string) {
+ failures := make([]string, len(m.failures))
+ for i := range m.failures {
+ failures[i] = m.failures[i].Error()
+ }
+ return format.Message(reflect.TypeOf(actual).Name(),
+ fmt.Sprintf("to match keys: {\n%v\n}\n", strings.Join(failures, "\n")))
+}
+
+func (m *KeysMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+ return format.Message(actual, "not to match keys")
+}
+
+func (m *KeysMatcher) Failures() []error {
+ return m.failures
+}
diff --git a/vendor/github.com/onsi/gomega/gstruct/pointer.go b/vendor/github.com/onsi/gomega/gstruct/pointer.go
new file mode 100644
index 000000000..cc828a325
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gstruct/pointer.go
@@ -0,0 +1,58 @@
+// untested sections: 3
+
+package gstruct
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/onsi/gomega/format"
+ "github.com/onsi/gomega/types"
+)
+
+//PointTo applies the given matcher to the value pointed to by actual. It fails if the pointer is
+//nil.
+// actual := 5
+// Expect(&actual).To(PointTo(Equal(5)))
+func PointTo(matcher types.GomegaMatcher) types.GomegaMatcher {
+ return &PointerMatcher{
+ Matcher: matcher,
+ }
+}
+
+type PointerMatcher struct {
+ Matcher types.GomegaMatcher
+
+ // Failure message.
+ failure string
+}
+
+func (m *PointerMatcher) Match(actual interface{}) (bool, error) {
+ val := reflect.ValueOf(actual)
+
+ // return error if actual type is not a pointer
+ if val.Kind() != reflect.Ptr {
+ return false, fmt.Errorf("PointerMatcher expects a pointer but we have '%s'", val.Kind())
+ }
+
+ if !val.IsValid() || val.IsNil() {
+ m.failure = format.Message(actual, "not to be <nil>")
+ return false, nil
+ }
+
+ // Forward the value.
+ elem := val.Elem().Interface()
+ match, err := m.Matcher.Match(elem)
+ if !match {
+ m.failure = m.Matcher.FailureMessage(elem)
+ }
+ return match, err
+}
+
+func (m *PointerMatcher) FailureMessage(_ interface{}) (message string) {
+ return m.failure
+}
+
+func (m *PointerMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+ return m.Matcher.NegatedFailureMessage(actual)
+}
diff --git a/vendor/github.com/onsi/gomega/gstruct/types.go b/vendor/github.com/onsi/gomega/gstruct/types.go
new file mode 100644
index 000000000..48cbbe8f6
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gstruct/types.go
@@ -0,0 +1,15 @@
+package gstruct
+
+//Options is the type for options passed to some matchers.
+type Options int
+
+const (
+ //IgnoreExtras tells the matcher to ignore extra elements or fields, rather than triggering a failure.
+ IgnoreExtras Options = 1 << iota
+ //IgnoreMissing tells the matcher to ignore missing elements or fields, rather than triggering a failure.
+ IgnoreMissing
+ //AllowDuplicates tells the matcher to permit multiple members of the slice to produce the same ID when
+ //considered by the indentifier function. All members that map to a given key must still match successfully
+ //with the matcher that is provided for that key.
+ AllowDuplicates
+)