github.com/ipld/go-ipld-prime@v0.21.0/node/basicnode/map_test.go (about) 1 package basicnode_test 2 3 import ( 4 "fmt" 5 "testing" 6 7 qt "github.com/frankban/quicktest" 8 "github.com/ipld/go-ipld-prime/datamodel" 9 "github.com/ipld/go-ipld-prime/must" 10 "github.com/ipld/go-ipld-prime/node/basicnode" 11 "github.com/ipld/go-ipld-prime/node/tests" 12 "github.com/ipld/go-ipld-prime/printer" 13 ) 14 15 func TestMap(t *testing.T) { 16 tests.SpecTestMapStrInt(t, basicnode.Prototype.Map) 17 tests.SpecTestMapStrMapStrInt(t, basicnode.Prototype.Map) 18 tests.SpecTestMapStrListStr(t, basicnode.Prototype.Map) 19 } 20 21 func BenchmarkMapStrInt_3n_AssembleStandard(b *testing.B) { 22 tests.SpecBenchmarkMapStrInt_3n_AssembleStandard(b, basicnode.Prototype.Map) 23 } 24 func BenchmarkMapStrInt_3n_AssembleEntry(b *testing.B) { 25 tests.SpecBenchmarkMapStrInt_3n_AssembleEntry(b, basicnode.Prototype.Map) 26 } 27 func BenchmarkMapStrInt_3n_Iteration(b *testing.B) { 28 tests.SpecBenchmarkMapStrInt_3n_Iteration(b, basicnode.Prototype.Map) 29 } 30 31 func BenchmarkMapStrInt_25n_AssembleStandard(b *testing.B) { 32 tests.SpecBenchmarkMapStrInt_25n_AssembleStandard(b, basicnode.Prototype.Map) 33 } 34 func BenchmarkMapStrInt_25n_AssembleEntry(b *testing.B) { 35 tests.SpecBenchmarkMapStrInt_25n_AssembleEntry(b, basicnode.Prototype.Map) 36 } 37 func BenchmarkMapStrInt_25n_Iteration(b *testing.B) { 38 tests.SpecBenchmarkMapStrInt_25n_Iteration(b, basicnode.Prototype.Map) 39 } 40 41 func BenchmarkSpec_Marshal_Map3StrInt(b *testing.B) { 42 tests.BenchmarkSpec_Marshal_Map3StrInt(b, basicnode.Prototype.Map) 43 } 44 func BenchmarkSpec_Marshal_MapNStrMap3StrInt(b *testing.B) { 45 tests.BenchmarkSpec_Marshal_MapNStrMap3StrInt(b, basicnode.Prototype.Map) 46 } 47 48 func BenchmarkSpec_Unmarshal_Map3StrInt(b *testing.B) { 49 tests.BenchmarkSpec_Unmarshal_Map3StrInt(b, basicnode.Prototype.Map) 50 } 51 func BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b *testing.B) { 52 tests.BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b, basicnode.Prototype.Map) 53 } 54 55 // Test that the map builder cannot be assigned arbitrary values, and trying to 56 // will result in a sensible error 57 func TestMapAssignError(t *testing.T) { 58 b := basicnode.Prototype.Map.NewBuilder() 59 err := b.AssignBool(true) 60 errExpect := `func called on wrong kind: "AssignBool" called on a map node \(kind: map\), but only makes sense on bool` 61 qt.Check(t, err, qt.ErrorMatches, errExpect) 62 63 err = b.AssignInt(3) 64 errExpect = `func called on wrong kind: "AssignInt" called on a map node \(kind: map\), but only makes sense on int` 65 qt.Check(t, err, qt.ErrorMatches, errExpect) 66 67 err = b.AssignFloat(5.7) 68 errExpect = `func called on wrong kind: "AssignFloat" called on a map node \(kind: map\), but only makes sense on float` 69 qt.Check(t, err, qt.ErrorMatches, errExpect) 70 71 err = b.AssignString("hi") 72 errExpect = `func called on wrong kind: "AssignString" called on a map node \(kind: map\), but only makes sense on string` 73 qt.Check(t, err, qt.ErrorMatches, errExpect) 74 75 err = b.AssignNode(basicnode.NewInt(3)) 76 errExpect = `func called on wrong kind: "AssignNode" called on a map node \(kind: int\), but only makes sense on map` 77 qt.Check(t, err, qt.ErrorMatches, errExpect) 78 79 // TODO(dustmop): BeginList, AssignNull, AssignBytes, AssignLink 80 } 81 82 // Test that the map builder can create map nodes, and AssignNode will copy 83 // such a node, and lookup methods on that node will work correctly 84 func TestMapBuilder(t *testing.T) { 85 b := basicnode.Prototype.Map.NewBuilder() 86 87 // construct a map of three keys, using the MapBuilder 88 ma, err := b.BeginMap(3) 89 if err != nil { 90 t.Fatal(err) 91 } 92 a := ma.AssembleKey() 93 a.AssignString("cat") 94 a = ma.AssembleValue() 95 a.AssignString("meow") 96 97 a, err = ma.AssembleEntry("dog") 98 if err != nil { 99 t.Fatal(err) 100 } 101 a.AssignString("bark") 102 103 a = ma.AssembleKey() 104 a.AssignString("eel") 105 a = ma.AssembleValue() 106 a.AssignString("zap") 107 108 err = ma.Finish() 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 // test the builder's prototypes and its key and value prototypes, while we're here 114 np := b.Prototype() 115 qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__Map") 116 np = ma.KeyPrototype() 117 qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__String") 118 np = ma.ValuePrototype("") 119 qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__Any") 120 121 // compare the printed map 122 mapNode := b.Build() 123 actual := printer.Sprint(mapNode) 124 125 expect := `map{ 126 string{"cat"}: string{"meow"} 127 string{"dog"}: string{"bark"} 128 string{"eel"}: string{"zap"} 129 }` 130 qt.Check(t, expect, qt.Equals, actual) 131 132 // copy the map using AssignNode 133 c := basicnode.Prototype.Map.NewBuilder() 134 err = c.AssignNode(mapNode) 135 if err != nil { 136 t.Fatal(err) 137 } 138 anotherNode := c.Build() 139 140 actual = printer.Sprint(anotherNode) 141 qt.Assert(t, expect, qt.Equals, actual) 142 143 // access values of map, using string 144 r, err := anotherNode.LookupByString("cat") 145 if err != nil { 146 t.Fatal(err) 147 } 148 qt.Check(t, "meow", qt.Equals, must.String(r)) 149 150 // access values of map, using node 151 r, err = anotherNode.LookupByNode(basicnode.NewString("dog")) 152 if err != nil { 153 t.Fatal(err) 154 } 155 qt.Check(t, "bark", qt.Equals, must.String(r)) 156 157 // access values of map, using PathSegment 158 r, err = anotherNode.LookupBySegment(datamodel.ParsePathSegment("eel")) 159 if err != nil { 160 t.Fatal(err) 161 } 162 qt.Check(t, "zap", qt.Equals, must.String(r)) 163 164 // validate the node's prototype 165 np = anotherNode.Prototype() 166 qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__Map") 167 } 168 169 // test that AssignNode will fail if called twice, it expects an empty 170 // node to assign to 171 func TestMapCantAssignNodeTwice(t *testing.T) { 172 b := basicnode.Prototype.Map.NewBuilder() 173 174 // construct a map of three keys, using the MapBuilder 175 ma, err := b.BeginMap(3) 176 if err != nil { 177 t.Fatal(err) 178 } 179 a := ma.AssembleKey() 180 a.AssignString("cat") 181 a = ma.AssembleValue() 182 a.AssignString("meow") 183 184 a, err = ma.AssembleEntry("dog") 185 if err != nil { 186 t.Fatal(err) 187 } 188 a.AssignString("bark") 189 190 a = ma.AssembleKey() 191 a.AssignString("eel") 192 a = ma.AssembleValue() 193 a.AssignString("zap") 194 195 err = ma.Finish() 196 if err != nil { 197 t.Fatal(err) 198 } 199 mapNode := b.Build() 200 201 // copy the map using AssignNode, works the first time 202 c := basicnode.Prototype.Map.NewBuilder() 203 err = c.AssignNode(mapNode) 204 if err != nil { 205 t.Fatal(err) 206 } 207 qt.Assert(t, 208 func() { 209 _ = c.AssignNode(mapNode) 210 }, 211 qt.PanicMatches, 212 // TODO(dustmop): Error message here should be better 213 `misuse`) 214 } 215 216 func TestMapLookupError(t *testing.T) { 217 b := basicnode.Prototype.Map.NewBuilder() 218 219 // construct a map of three keys, using the MapBuilder 220 ma, err := b.BeginMap(3) 221 if err != nil { 222 t.Fatal(err) 223 } 224 a := ma.AssembleKey() 225 a.AssignString("cat") 226 a = ma.AssembleValue() 227 a.AssignString("meow") 228 229 a, err = ma.AssembleEntry("dog") 230 if err != nil { 231 t.Fatal(err) 232 } 233 a.AssignString("bark") 234 235 a = ma.AssembleKey() 236 a.AssignString("eel") 237 a = ma.AssembleValue() 238 a.AssignString("zap") 239 240 err = ma.Finish() 241 if err != nil { 242 t.Fatal(err) 243 } 244 245 mapNode := b.Build() 246 247 _, err = mapNode.LookupByString("frog") 248 qt.Check(t, err, qt.ErrorMatches, `key not found: "frog"`) 249 250 _, err = mapNode.LookupByNode(basicnode.NewInt(3)) 251 // TODO(dustmop): This error message is not great. It's about how the 252 // int node could not be converted when the real problem is that this 253 // method should not accept ints as parameters 254 qt.Check(t, err, qt.ErrorMatches, `func called on wrong kind: "AsString" called on a int node \(kind: int\), but only makes sense on string`) 255 256 _, err = mapNode.LookupByIndex(0) 257 qt.Check(t, err, qt.ErrorMatches, `func called on wrong kind: "LookupByIndex" called on a map node \(kind: map\), but only makes sense on list`) 258 } 259 260 func TestMapNewBuilderUsageError(t *testing.T) { 261 qt.Assert(t, 262 func() { 263 b := basicnode.Prototype.Map.NewBuilder() 264 _ = b.Build() 265 }, 266 qt.PanicMatches, 267 `invalid state: assembler must be 'finished' before Build can be called!`) 268 269 // construct an empty map 270 b := basicnode.Prototype.Map.NewBuilder() 271 ma, err := b.BeginMap(0) 272 if err != nil { 273 t.Fatal(err) 274 } 275 err = ma.Finish() 276 if err != nil { 277 t.Fatal(err) 278 } 279 mapNode := b.Build() 280 actual := printer.Sprint(mapNode) 281 282 expect := `map{}` 283 qt.Check(t, expect, qt.Equals, actual) 284 285 // reset will return the state to 'initial', so Build will panic once again 286 b.Reset() 287 qt.Assert(t, 288 func() { 289 _ = b.Build() 290 }, 291 qt.PanicMatches, 292 `invalid state: assembler must be 'finished' before Build can be called!`) 293 294 // assembling a key without a value will cause Finish to panic 295 b.Reset() 296 ma, err = b.BeginMap(0) 297 if err != nil { 298 t.Fatal(err) 299 } 300 a := ma.AssembleKey() 301 a.AssignString("cat") 302 qt.Assert(t, 303 func() { 304 _ = ma.Finish() 305 }, 306 qt.PanicMatches, 307 // TODO(dustmop): Error message here should be better 308 `misuse`) 309 } 310 311 func TestMapDupKeyError(t *testing.T) { 312 b := basicnode.Prototype.Map.NewBuilder() 313 314 // construct a map with duplicate keys 315 ma, err := b.BeginMap(3) 316 if err != nil { 317 t.Fatal(err) 318 } 319 a := ma.AssembleKey() 320 a.AssignString("cat") 321 a = ma.AssembleValue() 322 a.AssignString("meow") 323 a = ma.AssembleKey() 324 err = a.AssignString("cat") 325 326 qt.Check(t, err, qt.ErrorMatches, `cannot repeat map key "cat"`) 327 }