hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

checkers.go (3640B)

    1 // Copyright 2019 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package hqt
   15 
   16 import (
   17 	"errors"
   18 	"fmt"
   19 	"reflect"
   20 	"strings"
   21 
   22 	qt "github.com/frankban/quicktest"
   23 	"github.com/gohugoio/hugo/htesting"
   24 	"github.com/google/go-cmp/cmp"
   25 	"github.com/spf13/cast"
   26 )
   27 
   28 // IsSameString asserts that two strings are equal. The two strings
   29 // are normalized (whitespace removed) before doing a ==.
   30 // Also note that two strings can be the same even if they're of different
   31 // types.
   32 var IsSameString qt.Checker = &stringChecker{
   33 	argNames: []string{"got", "want"},
   34 }
   35 
   36 // IsSameType asserts that got is the same type as want.
   37 var IsSameType qt.Checker = &typeChecker{
   38 	argNames: []string{"got", "want"},
   39 }
   40 
   41 type argNames []string
   42 
   43 func (a argNames) ArgNames() []string {
   44 	return a
   45 }
   46 
   47 type typeChecker struct {
   48 	argNames
   49 }
   50 
   51 // Check implements Checker.Check by checking that got and args[0] is of the same type.
   52 func (c *typeChecker) Check(got any, args []any, note func(key string, value any)) (err error) {
   53 	if want := args[0]; reflect.TypeOf(got) != reflect.TypeOf(want) {
   54 		if _, ok := got.(error); ok && want == nil {
   55 			return errors.New("got non-nil error")
   56 		}
   57 		return errors.New("values are not of same type")
   58 	}
   59 	return nil
   60 }
   61 
   62 type stringChecker struct {
   63 	argNames
   64 }
   65 
   66 // Check implements Checker.Check by checking that got and args[0] represents the same normalized text (whitespace etc. removed).
   67 func (c *stringChecker) Check(got any, args []any, note func(key string, value any)) (err error) {
   68 	s1, s2 := cast.ToString(got), cast.ToString(args[0])
   69 
   70 	if s1 == s2 {
   71 		return nil
   72 	}
   73 
   74 	s1, s2 = normalizeString(s1), normalizeString(s2)
   75 
   76 	if s1 == s2 {
   77 		return nil
   78 	}
   79 
   80 	return fmt.Errorf("values are not the same text: %s", strings.Join(htesting.DiffStrings(s1, s2), " | "))
   81 }
   82 
   83 func normalizeString(s string) string {
   84 	s = strings.ReplaceAll(s, "\r\n", "\n")
   85 
   86 	lines := strings.Split(strings.TrimSpace(s), "\n")
   87 	for i, line := range lines {
   88 		lines[i] = strings.Join(strings.Fields(strings.TrimSpace(line)), "")
   89 	}
   90 	return strings.Join(lines, "\n")
   91 }
   92 
   93 // DeepAllowUnexported creates an option to allow compare of unexported types
   94 // in the given list of types.
   95 // see https://github.com/google/go-cmp/issues/40#issuecomment-328615283
   96 func DeepAllowUnexported(vs ...any) cmp.Option {
   97 	m := make(map[reflect.Type]struct{})
   98 	for _, v := range vs {
   99 		structTypes(reflect.ValueOf(v), m)
  100 	}
  101 	var typs []any
  102 	for t := range m {
  103 		typs = append(typs, reflect.New(t).Elem().Interface())
  104 	}
  105 	return cmp.AllowUnexported(typs...)
  106 }
  107 
  108 func structTypes(v reflect.Value, m map[reflect.Type]struct{}) {
  109 	if !v.IsValid() {
  110 		return
  111 	}
  112 	switch v.Kind() {
  113 	case reflect.Ptr:
  114 		if !v.IsNil() {
  115 			structTypes(v.Elem(), m)
  116 		}
  117 	case reflect.Interface:
  118 		if !v.IsNil() {
  119 			structTypes(v.Elem(), m)
  120 		}
  121 	case reflect.Slice, reflect.Array:
  122 		for i := 0; i < v.Len(); i++ {
  123 			structTypes(v.Index(i), m)
  124 		}
  125 	case reflect.Map:
  126 		for _, k := range v.MapKeys() {
  127 			structTypes(v.MapIndex(k), m)
  128 		}
  129 	case reflect.Struct:
  130 		m[v.Type()] = struct{}{}
  131 		for i := 0; i < v.NumField(); i++ {
  132 			structTypes(v.Field(i), m)
  133 		}
  134 	}
  135 }