92 lines
2.5 KiB
Go
92 lines
2.5 KiB
Go
|
// Copyright 2012 Aaron Jacobs. All Rights Reserved.
|
||
|
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package oglematchers
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Given a list of arguments M, ElementsAre returns a matcher that matches
|
||
|
// arrays and slices A where all of the following hold:
|
||
|
//
|
||
|
// * A is the same length as M.
|
||
|
//
|
||
|
// * For each i < len(A) where M[i] is a matcher, A[i] matches M[i].
|
||
|
//
|
||
|
// * For each i < len(A) where M[i] is not a matcher, A[i] matches
|
||
|
// Equals(M[i]).
|
||
|
//
|
||
|
func ElementsAre(M ...interface{}) Matcher {
|
||
|
// Copy over matchers, or convert to Equals(x) for non-matcher x.
|
||
|
subMatchers := make([]Matcher, len(M))
|
||
|
for i, x := range M {
|
||
|
if matcher, ok := x.(Matcher); ok {
|
||
|
subMatchers[i] = matcher
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
subMatchers[i] = Equals(x)
|
||
|
}
|
||
|
|
||
|
return &elementsAreMatcher{subMatchers}
|
||
|
}
|
||
|
|
||
|
type elementsAreMatcher struct {
|
||
|
subMatchers []Matcher
|
||
|
}
|
||
|
|
||
|
func (m *elementsAreMatcher) Description() string {
|
||
|
subDescs := make([]string, len(m.subMatchers))
|
||
|
for i, sm := range m.subMatchers {
|
||
|
subDescs[i] = sm.Description()
|
||
|
}
|
||
|
|
||
|
return fmt.Sprintf("elements are: [%s]", strings.Join(subDescs, ", "))
|
||
|
}
|
||
|
|
||
|
func (m *elementsAreMatcher) Matches(candidates interface{}) error {
|
||
|
// The candidate must be a slice or an array.
|
||
|
v := reflect.ValueOf(candidates)
|
||
|
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
||
|
return NewFatalError("which is not a slice or array")
|
||
|
}
|
||
|
|
||
|
// The length must be correct.
|
||
|
if v.Len() != len(m.subMatchers) {
|
||
|
return errors.New(fmt.Sprintf("which is of length %d", v.Len()))
|
||
|
}
|
||
|
|
||
|
// Check each element.
|
||
|
for i, subMatcher := range m.subMatchers {
|
||
|
c := v.Index(i)
|
||
|
if matchErr := subMatcher.Matches(c.Interface()); matchErr != nil {
|
||
|
// Return an errors indicating which element doesn't match. If the
|
||
|
// matcher error was fatal, make this one fatal too.
|
||
|
err := errors.New(fmt.Sprintf("whose element %d doesn't match", i))
|
||
|
if _, isFatal := matchErr.(*FatalError); isFatal {
|
||
|
err = NewFatalError(err.Error())
|
||
|
}
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|