/
assertions.go
78 lines (72 loc) · 2.45 KB
/
assertions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package testutils
import (
"fmt"
"reflect"
"strings"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/proto"
"github.com/smarty/assertions"
)
// ShouldResembleProto asserts that given two values that contain proto messages
// are equal by comparing their types and ensuring they serialize to the same
// text representation.
//
// Values can either each be a proto.Message or a slice of values that each
// implement proto.Message interface.
func ShouldResembleProto(actual interface{}, expected ...interface{}) string {
if len(expected) != 1 {
return fmt.Sprintf("ShouldResembleProto expects 1 value, got %d", len(expected))
}
exp := expected[0]
// This is very crude... We want to be able to see a diff between expected
// and actual protos, so we just serialize them into a string and compare
// strings. This is much simpler than trying to achieve the same using
// reflection, clearing of XXX_*** fields and ShouldResemble.
if am, ok := protoMessage(actual); ok {
if err := assertions.ShouldHaveSameTypeAs(actual, exp); err != "" {
return err
}
em, _ := protoMessage(exp)
return assertions.ShouldEqual(textPBMultiline.Format(am), textPBMultiline.Format(em))
}
lVal := reflect.ValueOf(actual)
rVal := reflect.ValueOf(exp)
if lVal.Kind() == reflect.Slice {
if rVal.Kind() != reflect.Slice {
return "ShouldResembleProto is expecting both arguments to be a slice if first one is a slice"
}
if err := assertions.ShouldHaveLength(actual, rVal.Len()); err != "" {
return err
}
var left, right strings.Builder
for i := 0; i < lVal.Len(); i++ {
l := lVal.Index(i).Interface()
r := rVal.Index(i).Interface()
if err := assertions.ShouldHaveSameTypeAs(l, r); err != "" {
return err
}
if i != 0 {
left.WriteString("---\n")
right.WriteString("---\n")
}
lm, _ := protoMessage(l)
rm, _ := protoMessage(r)
left.WriteString(textPBMultiline.Format(lm))
right.WriteString(textPBMultiline.Format(rm))
}
return assertions.ShouldEqual(left.String(), right.String())
}
return fmt.Sprintf(
"ShouldResembleProto doesn't know how to handle values of type %T, "+
"expecting a proto.Message or a slice of thereof", actual)
}
var textPBMultiline = prototext.MarshalOptions{
Multiline: true,
}
// protoMessage returns V2 proto message, converting v1 on the fly.
func protoMessage(a interface{}) (proto.Message, bool) {
if m, ok := a.(proto.Message); ok {
return m, true
}
return nil, false
}