go.mway.dev/x@v0.0.0-20240520034138-950aede9a3fb/container/tree/tree_test.go (about) 1 // Copyright (c) 2024 Matt Way 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to 5 // deal in the Software without restriction, including without limitation the 6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 // sell copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 // IN THE THE SOFTWARE. 20 21 package tree_test 22 23 import ( 24 "io" 25 "strings" 26 "testing" 27 28 "github.com/stretchr/testify/require" 29 "go.mway.dev/x/container/tree" 30 ) 31 32 func TestBasicNode(t *testing.T) { 33 var zero tree.BasicNode[string, string] 34 require.Zero(t, zero.Key()) 35 require.Zero(t, zero.Value()) 36 zero.SetValue(t.Name()) 37 require.Equal(t, t.Name(), zero.Value()) 38 39 require.NotPanics(t, func() { 40 var node *tree.BasicNode[string, string] 41 node.SetParent(nil) 42 node.SetValue("foo") 43 require.Equal(t, 0, node.Len()) 44 }) 45 46 root := tree.NewBasicNode(t.Name(), t.Name()) 47 require.Equal(t, t.Name(), root.Key()) 48 require.Equal(t, t.Name(), root.Value()) 49 require.Nil(t, root.Parent()) 50 require.Nil(t, root.Children()) 51 require.Nil(t, root.Child("child1")) 52 require.Equal(t, 1, root.Len()) 53 54 child1 := root.Add("child1", "child1") 55 require.Equal(t, root, child1.Parent()) 56 require.Equal(t, child1, root.Child("child1")) 57 child1.Add("grandchild11", "grandchild11") 58 child1.Add("grandchild12", "grandchild12") 59 require.Len(t, child1.Children(), 2) 60 require.Equal(t, 3, child1.Len()) 61 require.Equal(t, 4, root.Len()) 62 63 child2 := root.Add("child2", "child2") 64 require.Equal(t, root, child2.Parent()) 65 require.Equal(t, child2, root.Child("child2")) 66 require.Len(t, root.Children(), 2) 67 child2.Add("grandchild21", "grandchild21") 68 child2.Add("grandchild22", "grandchild22") 69 require.Len(t, child2.Children(), 2) 70 require.Equal(t, 3, child2.Len()) 71 require.Equal(t, 7, root.Len()) 72 73 var ( 74 wantKeys = []string{ 75 t.Name(), 76 "child1", 77 "grandchild11", 78 "grandchild12", 79 "child2", 80 "grandchild21", 81 "grandchild22", 82 } 83 haveKeys []string 84 ) 85 err := root.Walk(func(node *tree.BasicNode[string, string]) error { 86 haveKeys = append(haveKeys, node.Key()) 87 return nil 88 }) 89 require.NoError(t, err) 90 require.Equal(t, wantKeys, haveKeys) 91 92 var ( 93 wantRevKeys = []string{ 94 "grandchild11", 95 "grandchild12", 96 "child1", 97 "grandchild21", 98 "grandchild22", 99 "child2", 100 t.Name(), 101 } 102 haveRevKeys []string 103 ) 104 err = root.WalkRev(func(node *tree.BasicNode[string, string]) error { 105 haveRevKeys = append(haveRevKeys, node.Key()) 106 return nil 107 }) 108 require.NoError(t, err) 109 require.Equal(t, wantRevKeys, haveRevKeys) 110 111 var calls int 112 err = root.Walk(func(*tree.BasicNode[string, string]) error { 113 calls++ 114 return io.EOF 115 }) 116 require.ErrorIs(t, err, io.EOF) 117 err = root.WalkRev(func(*tree.BasicNode[string, string]) error { 118 calls++ 119 return io.EOF 120 }) 121 require.ErrorIs(t, err, io.EOF) 122 require.Equal(t, 2, calls) 123 } 124 125 func TestBasicNode_EmptyOrNil(t *testing.T) { 126 cases := map[string]struct { 127 node *tree.BasicNode[string, string] 128 wantCalls int 129 }{ 130 "nil": { 131 node: nil, 132 wantCalls: 0, 133 }, 134 "empty": { 135 node: &tree.BasicNode[string, string]{}, 136 wantCalls: 2, 137 }, 138 } 139 140 for name, tt := range cases { 141 t.Run(name, func(t *testing.T) { 142 require.NotPanics(t, func() { 143 require.Zero(t, tt.node.Key()) 144 require.Zero(t, tt.node.Value()) 145 require.Nil(t, tt.node.Parent()) 146 require.Nil(t, tt.node.Children()) 147 require.Nil(t, tt.node.Child("foo")) 148 require.Equal(t, tt.node == nil, tt.node.Len() == 0) 149 150 child := tt.node.Add("foo", "bar") 151 require.NotNil(t, child) 152 require.Equal(t, tt.node, child.Parent()) 153 require.Equal(t, 1, child.Len()) 154 155 var calls int 156 tt.node.Walk(func(*tree.BasicNode[string, string]) error { //nolint:errcheck 157 calls++ 158 return nil 159 }) 160 tt.node.WalkRev(func(*tree.BasicNode[string, string]) error { //nolint:errcheck 161 calls++ 162 return nil 163 }) 164 require.Equal(t, tt.wantCalls*2, calls) 165 }) 166 }) 167 } 168 } 169 170 func TestBasicNode_Path(t *testing.T) { 171 a := tree.NewBasicNode("a", "a") 172 b := a.Add("b", "b") 173 b.Add("c", "c") 174 175 var ( 176 expect = [][]string{ 177 {"a"}, 178 {"a", "b"}, 179 {"a", "b", "c"}, 180 } 181 expectRev = [][]string{ 182 {"a"}, 183 {"b", "a"}, 184 {"c", "b", "a"}, 185 } 186 ) 187 188 var i int 189 a.Walk(func(n *tree.BasicNode[string, string]) error { //nolint:errcheck 190 require.Equal(t, expect[i], n.Path()) 191 require.Equal(t, expectRev[i], n.PathRev()) 192 i++ 193 return nil 194 }) 195 } 196 197 func TestBasicNode_SetParent(t *testing.T) { 198 a := tree.NewBasicNode("a", "b") 199 b := tree.NewBasicNode("b", "b") 200 c := tree.NewBasicNode("c", "c") 201 c.SetParent(b) 202 c.SetParent(a) 203 204 var ( 205 expect = map[string]int{ 206 "a": 1, 207 "c": 1, 208 } 209 actual = make(map[string]int) 210 ) 211 212 a.Walk(func(n *tree.BasicNode[string, string]) error { //nolint:errcheck 213 actual[n.Key()]++ 214 return nil 215 }) 216 217 require.Equal(t, expect, actual) 218 } 219 220 func TestBasicNode_Remove(t *testing.T) { 221 a := tree.NewBasicNode("a", "b") 222 b := tree.NewBasicNode("b", "b") 223 c := tree.NewBasicNode("c", "c") 224 c.SetParent(b) 225 226 removedC, ok := b.Remove(c.Key()) 227 require.True(t, ok) 228 require.Equal(t, c, removedC) 229 230 var ( 231 expect = map[string]int{ 232 "a": 1, 233 } 234 actual = make(map[string]int) 235 ) 236 237 a.Walk(func(n *tree.BasicNode[string, string]) error { //nolint:errcheck 238 actual[n.Key()]++ 239 return nil 240 }) 241 242 require.Equal(t, expect, actual) 243 } 244 245 func TestBasicNode_WalkErrors(t *testing.T) { 246 root := tree.NewBasicNode(t.Name(), t.Name()+"value") 247 require.Equal(t, t.Name(), root.Key()) 248 require.Equal(t, t.Name()+"value", root.Value()) 249 require.Nil(t, root.Parent()) 250 require.Nil(t, root.Children()) 251 require.Nil(t, root.Child("child1")) 252 require.Equal(t, 1, root.Len()) 253 254 child1 := root.Add("child1", "value") 255 require.Equal(t, root, child1.Parent()) 256 require.Equal(t, child1, root.Child("child1")) 257 child1.Add("grandchild11", "value") 258 child1.Add("grandchild12", "value") 259 require.Len(t, child1.Children(), 2) 260 require.Equal(t, 3, child1.Len()) 261 require.Equal(t, 4, root.Len()) 262 263 child2 := root.Add("child2", "childv2") 264 require.Equal(t, root, child2.Parent()) 265 require.Equal(t, child2, root.Child("child2")) 266 require.Len(t, root.Children(), 2) 267 child2.Add("grandchild21", "value") 268 child2.Add("grandchild22", "value") 269 require.Len(t, child2.Children(), 2) 270 require.Equal(t, 3, child2.Len()) 271 require.Equal(t, 7, root.Len()) 272 273 rootKey := t.Name() 274 275 cases := map[string]struct { //nolint:govet 276 pred func(key string) error 277 want []string 278 wantRev []string 279 wantErr error 280 }{ 281 "abort at child2": { 282 pred: func(key string) error { 283 if key == "child2" { 284 return io.EOF 285 } 286 return nil 287 }, 288 want: []string{ 289 rootKey, 290 "child1", 291 "grandchild11", 292 "grandchild12", 293 }, 294 wantRev: []string{ 295 "grandchild11", 296 "grandchild12", 297 "child1", 298 "grandchild21", 299 "grandchild22", 300 }, 301 wantErr: io.EOF, 302 }, 303 "no grandchildren": { 304 pred: func(key string) error { 305 if strings.HasPrefix(key, "grand") { 306 return tree.ErrSkipSubtree 307 } 308 return nil 309 }, 310 want: []string{ 311 rootKey, 312 "child1", 313 "child2", 314 }, 315 wantRev: []string{ 316 "child1", 317 "child2", 318 rootKey, 319 }, 320 wantErr: nil, 321 }, 322 "root only": { 323 pred: func(key string) error { 324 if key != rootKey { 325 return tree.ErrSkipSubtree 326 } 327 return nil 328 }, 329 want: []string{ 330 rootKey, 331 }, 332 wantRev: []string{ 333 rootKey, 334 }, 335 wantErr: nil, 336 }, 337 "no child1": { 338 pred: func(key string) error { 339 if key == "child1" { 340 return tree.ErrSkipSubtree 341 } 342 return nil 343 }, 344 want: []string{ 345 rootKey, 346 "child2", 347 "grandchild21", 348 "grandchild22", 349 }, 350 wantRev: []string{ 351 "grandchild11", 352 "grandchild12", 353 "grandchild21", 354 "grandchild22", 355 "child2", 356 rootKey, 357 }, 358 wantErr: nil, 359 }, 360 } 361 362 for name, tt := range cases { 363 t.Run(name, func(t *testing.T) { 364 var ( 365 seen []string 366 walker = func(node *tree.BasicNode[string, string]) (err error) { 367 if err = tt.pred(node.Key()); err == nil { 368 seen = append(seen, node.Key()) 369 } 370 return err 371 } 372 ) 373 374 err := root.Walk(walker) 375 require.Equal(t, tt.want, seen) 376 require.ErrorIs(t, err, tt.wantErr) 377 378 seen = seen[:0] 379 err = root.WalkRev(walker) 380 require.Equal(t, tt.wantRev, seen) 381 require.ErrorIs(t, err, tt.wantErr) 382 }) 383 } 384 }