1717package basics_test
1818
1919import (
20- "fmt"
2120 "reflect"
22- "strings"
2321 "testing"
2422
2523 "github.com/algorand/go-algorand/data/basics"
2624 "github.com/algorand/go-algorand/data/bookkeeping"
2725 "github.com/algorand/go-algorand/test/partitiontest"
28- "github.com/stretchr/testify/require "
26+ "github.com/algorand/go-algorand/test/reflectionhelpers "
2927)
3028
31- type typePath []string
32-
33- func (p typePath ) addMapKey () typePath {
34- return append (p , "map_key" )
35- }
36-
37- func (p typePath ) addValue () typePath {
38- return append (p , "value" )
39- }
40-
41- func (p typePath ) addField (fieldName string ) typePath {
42- return append (p , "field " + fieldName )
43- }
44-
45- func (p typePath ) validatePathFrom (t reflect.Type ) error {
46- if len (p ) == 0 {
47- // path is empty, so it's vacuously valid
48- return nil
49- }
50-
51- value := p [0 ]
52- switch {
53- case value == "map_key" :
54- return p [1 :].validatePathFrom (t .Key ())
55- case value == "value" :
56- return p [1 :].validatePathFrom (t .Elem ())
57- case strings .HasPrefix (value , "field " ):
58- fieldName := value [len ("field " ):]
59- fieldType , ok := t .FieldByName (fieldName )
60- if ! ok {
61- return fmt .Errorf ("Type '%s' does not have the field '%s'" , t .Name (), fieldName )
62- }
63- return p [1 :].validatePathFrom (fieldType .Type )
64- default :
65- return fmt .Errorf ("Unexpected item in path: %s" , value )
66- }
67- }
68-
69- func (p typePath ) Equals (other typePath ) bool {
70- if len (p ) != len (other ) {
71- return false
72- }
73- for i := range p {
74- if p [i ] != other [i ] {
75- return false
76- }
77- }
78- return true
79- }
80-
81- func (p typePath ) String () string {
82- return strings .Join (p , "->" )
83- }
84-
85- func checkReferencedTypes (seen map [reflect.Type ]bool , path typePath , typeStack []reflect.Type , check func (path typePath , stack []reflect.Type ) bool ) {
86- currentType := typeStack [len (typeStack )- 1 ]
87-
88- if _ , seenType := seen [currentType ]; seenType {
89- return
90- }
91-
92- if ! check (path , typeStack ) {
93- // if currentType is not ok, don't visit its children
94- return
95- }
96-
97- // add currentType to seen set, to avoid infinite recursion if currentType references itself
98- seen [currentType ] = true
99-
100- // after currentType's children are visited, "forget" the type, so we can examine it again if needed
101- // if this didn't happen, only 1 error per invalid type would get reported
102- defer delete (seen , currentType )
103-
104- switch currentType .Kind () {
105- case reflect .Map :
106- newPath := path .addMapKey ()
107- newStack := append (typeStack , currentType .Key ())
108- checkReferencedTypes (seen , newPath , newStack , check )
109- fallthrough
110- case reflect .Array , reflect .Slice , reflect .Ptr :
111- newPath := path .addValue ()
112- newStack := append (typeStack , currentType .Elem ())
113- checkReferencedTypes (seen , newPath , newStack , check )
114- case reflect .Struct :
115- for i := 0 ; i < currentType .NumField (); i ++ {
116- field := currentType .Field (i )
117- newPath := path .addField (field .Name )
118- newStack := append (typeStack , field .Type )
119- checkReferencedTypes (seen , newPath , newStack , check )
120- }
121- }
122- }
123-
124- func makeTypeCheckFunction (t * testing.T , exceptions []typePath , startType reflect.Type ) func (path typePath , stack []reflect.Type ) bool {
29+ func makeTypeCheckFunction (t * testing.T , exceptions []reflectionhelpers.TypePath , startType reflect.Type ) reflectionhelpers.ReferencedTypesIterationAction {
12530 for _ , exception := range exceptions {
126- err := exception . validatePathFrom ( startType )
127- require . NoError ( t , err )
31+ // ensure all exceptions can resolve without panicking
32+ exception . ResolveType ( startType )
12833 }
12934
130- return func (path typePath , stack []reflect.Type ) bool {
35+ return func (path reflectionhelpers. TypePath , stack []reflect.Type ) bool {
13136 currentType := stack [len (stack )- 1 ]
13237
13338 for _ , exception := range exceptions {
13439 if path .Equals (exception ) {
135- t .Logf ("Skipping exception for path: %s" , path . String () )
40+ t .Logf ("Skipping exception for path: %s" , path )
13641 return true
13742 }
13843 }
13944
14045 switch currentType .Kind () {
14146 case reflect .String :
142- t .Errorf ("Invalid string type referenced from %s . Use []byte instead. Full path: %s" , startType . Name () , path . String () )
47+ t .Errorf ("Invalid string type referenced from %v . Use []byte instead. Full path: %s" , startType , path )
14348 return false
14449 case reflect .Chan , reflect .Func , reflect .Interface , reflect .UnsafePointer :
14550 // raise an error if one of these strange types is referenced too
146- t .Errorf ("Invalid type %s referenced from %s . Full path: %s" , currentType . Name () , startType . Name () , path . String () )
51+ t .Errorf ("Invalid type %v referenced from %v . Full path: %s" , currentType , startType , path )
14752 return false
14853 default :
14954 return true
@@ -157,26 +62,24 @@ func TestBlockFields(t *testing.T) {
15762 typeToCheck := reflect .TypeOf (bookkeeping.Block {})
15863
15964 // These exceptions are for pre-existing usages of string. Only add to this list if you really need to use string.
160- exceptions := []typePath {
161- typePath {}.addField ("BlockHeader" ).addField ("GenesisID" ),
162- typePath {}.addField ("BlockHeader" ).addField ("UpgradeState" ).addField ("CurrentProtocol" ),
163- typePath {}.addField ("BlockHeader" ).addField ("UpgradeState" ).addField ("NextProtocol" ),
164- typePath {}.addField ("BlockHeader" ).addField ("UpgradeVote" ).addField ("UpgradePropose" ),
165- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("SignedTxn" ).addField ("Txn" ).addField ("Type" ),
166- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("SignedTxn" ).addField ("Txn" ).addField ("Header" ).addField ("GenesisID" ),
167- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("SignedTxn" ).addField ("Txn" ).addField ("AssetConfigTxnFields" ).addField ("AssetParams" ).addField ("UnitName" ),
168- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("SignedTxn" ).addField ("Txn" ).addField ("AssetConfigTxnFields" ).addField ("AssetParams" ).addField ("AssetName" ),
169- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("SignedTxn" ).addField ("Txn" ).addField ("AssetConfigTxnFields" ).addField ("AssetParams" ).addField ("URL" ),
170- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("ApplyData" ).addField ("EvalDelta" ).addField ("GlobalDelta" ).addMapKey (),
171- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("ApplyData" ).addField ("EvalDelta" ).addField ("GlobalDelta" ).addValue ().addField ("Bytes" ),
172- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("ApplyData" ).addField ("EvalDelta" ).addField ("LocalDeltas" ).addValue ().addMapKey (),
173- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("ApplyData" ).addField ("EvalDelta" ).addField ("LocalDeltas" ).addValue ().addValue ().addField ("Bytes" ),
174- typePath {}.addField ("Payset" ).addValue ().addField ("SignedTxnWithAD" ).addField ("ApplyData" ).addField ("EvalDelta" ).addField ("Logs" ).addValue (),
65+ exceptions := []reflectionhelpers. TypePath {
66+ reflectionhelpers. TypePath {}.AddField ("BlockHeader" ).AddField ("GenesisID" ),
67+ reflectionhelpers. TypePath {}.AddField ("BlockHeader" ).AddField ("UpgradeState" ).AddField ("CurrentProtocol" ),
68+ reflectionhelpers. TypePath {}.AddField ("BlockHeader" ).AddField ("UpgradeState" ).AddField ("NextProtocol" ),
69+ reflectionhelpers. TypePath {}.AddField ("BlockHeader" ).AddField ("UpgradeVote" ).AddField ("UpgradePropose" ),
70+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("SignedTxn" ).AddField ("Txn" ).AddField ("Type" ),
71+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("SignedTxn" ).AddField ("Txn" ).AddField ("Header" ).AddField ("GenesisID" ),
72+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("SignedTxn" ).AddField ("Txn" ).AddField ("AssetConfigTxnFields" ).AddField ("AssetParams" ).AddField ("UnitName" ),
73+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("SignedTxn" ).AddField ("Txn" ).AddField ("AssetConfigTxnFields" ).AddField ("AssetParams" ).AddField ("AssetName" ),
74+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("SignedTxn" ).AddField ("Txn" ).AddField ("AssetConfigTxnFields" ).AddField ("AssetParams" ).AddField ("URL" ),
75+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("ApplyData" ).AddField ("EvalDelta" ).AddField ("GlobalDelta" ).AddMapKey (),
76+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("ApplyData" ).AddField ("EvalDelta" ).AddField ("GlobalDelta" ).AddValue ().AddField ("Bytes" ),
77+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("ApplyData" ).AddField ("EvalDelta" ).AddField ("LocalDeltas" ).AddValue ().AddMapKey (),
78+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("ApplyData" ).AddField ("EvalDelta" ).AddField ("LocalDeltas" ).AddValue ().AddValue ().AddField ("Bytes" ),
79+ reflectionhelpers. TypePath {}.AddField ("Payset" ).AddValue ().AddField ("SignedTxnWithAD" ).AddField ("ApplyData" ).AddField ("EvalDelta" ).AddField ("Logs" ).AddValue (),
17580 }
17681
177- seen := make (map [reflect.Type ]bool )
178-
179- checkReferencedTypes (seen , nil , []reflect.Type {typeToCheck }, makeTypeCheckFunction (t , exceptions , typeToCheck ))
82+ reflectionhelpers .IterateReferencedTypes (typeToCheck , makeTypeCheckFunction (t , exceptions , typeToCheck ))
18083}
18184
18285func TestAccountDataFields (t * testing.T ) {
@@ -185,17 +88,15 @@ func TestAccountDataFields(t *testing.T) {
18588 typeToCheck := reflect .TypeOf (basics.AccountData {})
18689
18790 // These exceptions are for pre-existing usages of string. Only add to this list if you really need to use string.
188- exceptions := []typePath {
189- typePath {}.addField ("AssetParams" ).addValue ().addField ("UnitName" ),
190- typePath {}.addField ("AssetParams" ).addValue ().addField ("AssetName" ),
191- typePath {}.addField ("AssetParams" ).addValue ().addField ("URL" ),
192- typePath {}.addField ("AppLocalStates" ).addValue ().addField ("KeyValue" ).addMapKey (),
193- typePath {}.addField ("AppLocalStates" ).addValue ().addField ("KeyValue" ).addValue ().addField ("Bytes" ),
194- typePath {}.addField ("AppParams" ).addValue ().addField ("GlobalState" ).addMapKey (),
195- typePath {}.addField ("AppParams" ).addValue ().addField ("GlobalState" ).addValue ().addField ("Bytes" ),
91+ exceptions := []reflectionhelpers. TypePath {
92+ reflectionhelpers. TypePath {}.AddField ("AssetParams" ).AddValue ().AddField ("UnitName" ),
93+ reflectionhelpers. TypePath {}.AddField ("AssetParams" ).AddValue ().AddField ("AssetName" ),
94+ reflectionhelpers. TypePath {}.AddField ("AssetParams" ).AddValue ().AddField ("URL" ),
95+ reflectionhelpers. TypePath {}.AddField ("AppLocalStates" ).AddValue ().AddField ("KeyValue" ).AddMapKey (),
96+ reflectionhelpers. TypePath {}.AddField ("AppLocalStates" ).AddValue ().AddField ("KeyValue" ).AddValue ().AddField ("Bytes" ),
97+ reflectionhelpers. TypePath {}.AddField ("AppParams" ).AddValue ().AddField ("GlobalState" ).AddMapKey (),
98+ reflectionhelpers. TypePath {}.AddField ("AppParams" ).AddValue ().AddField ("GlobalState" ).AddValue ().AddField ("Bytes" ),
19699 }
197100
198- seen := make (map [reflect.Type ]bool )
199-
200- checkReferencedTypes (seen , nil , []reflect.Type {typeToCheck }, makeTypeCheckFunction (t , exceptions , typeToCheck ))
101+ reflectionhelpers .IterateReferencedTypes (typeToCheck , makeTypeCheckFunction (t , exceptions , typeToCheck ))
201102}
0 commit comments