go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/graph/edge_lookup_test.go (about) 1 // Copyright (c) 2019 Cisco and/or its affiliates. 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 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package graph 16 17 import ( 18 . "github.com/onsi/gomega" 19 "testing" 20 ) 21 22 type mockIter struct { 23 visitedNodes map[string]struct{} 24 visitedEdges map[visitedEdge]struct{} 25 } 26 27 type visitedEdge struct { 28 sourceNode, relation, label string 29 } 30 31 func newMockIter() *mockIter { 32 m := &mockIter{} 33 m.reset() 34 return m 35 } 36 37 func (m *mockIter) reset() { 38 m.visitedNodes = make(map[string]struct{}) 39 m.visitedEdges = make(map[visitedEdge]struct{}) 40 } 41 42 func (m *mockIter) visitNode(key string) { 43 _, alreadyVisited := m.visitedNodes[key] 44 Expect(alreadyVisited).To(BeFalse()) 45 m.visitedNodes[key] = struct{}{} 46 } 47 48 func (m *mockIter) visitEdge(sourceNode, relation, label string) { 49 edge := visitedEdge{ 50 sourceNode: sourceNode, 51 relation: relation, 52 label: label, 53 } 54 _, alreadyVisited := m.visitedEdges[edge] 55 Expect(alreadyVisited).To(BeFalse()) 56 m.visitedEdges[edge] = struct{}{} 57 } 58 59 func TestLookupOverNodeKeys(t *testing.T) { 60 RegisterTestingT(t) 61 62 mi := newMockIter() 63 64 el := newEdgeLookup(nil) 65 Expect(el).ToNot(BeNil()) 66 67 el.iterTargets("some-key", false, mi.visitNode) 68 Expect(mi.visitedNodes).To(BeEmpty()) 69 70 el.addNodeKey("prefix1/node1") 71 el.addNodeKey("prefix2/node3") 72 el.addNodeKey("prefix1/node2") 73 74 // static key which exists 75 el.iterTargets("prefix1/node1", false, mi.visitNode) 76 Expect(mi.visitedNodes).To(HaveLen(1)) 77 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 78 mi.reset() 79 80 // static key which does not exist 81 el.iterTargets("prefix2/node1", false, mi.visitNode) 82 Expect(mi.visitedNodes).To(BeEmpty()) 83 84 // prefix1 85 el.iterTargets("prefix1/", true, mi.visitNode) 86 Expect(mi.visitedNodes).To(HaveLen(2)) 87 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 88 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 89 mi.reset() 90 91 // prefix2 92 el.iterTargets("prefix2/", true, mi.visitNode) 93 Expect(mi.visitedNodes).To(HaveLen(1)) 94 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 95 mi.reset() 96 97 // empty prefix (select all) 98 el.iterTargets("", true, mi.visitNode) 99 Expect(mi.visitedNodes).To(HaveLen(3)) 100 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 101 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 102 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 103 mi.reset() 104 105 el.delNodeKey("prefix1/node2") 106 107 // static key which no longer exists 108 el.iterTargets("prefix1/node2", false, mi.visitNode) 109 Expect(mi.visitedNodes).To(BeEmpty()) 110 111 // prefix1 112 el.iterTargets("prefix1/", true, mi.visitNode) 113 Expect(mi.visitedNodes).To(HaveLen(1)) 114 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 115 mi.reset() 116 117 // remove the rest 118 el.delNodeKey("prefix1/node1") 119 Expect(el.nodeKeyData).To(HaveLen(1)) // gc-ed 120 Expect(el.nodeKeyOffset).To(HaveLen(1)) // gc-ed 121 el.delNodeKey("prefix2/node3") 122 Expect(el.nodeKeyData).To(HaveLen(0)) // gc-ed 123 Expect(el.nodeKeyOffset).To(HaveLen(0)) // gc-ed 124 125 // empty prefix (select all) 126 el.iterTargets("", true, mi.visitNode) 127 Expect(mi.visitedNodes).To(HaveLen(0)) 128 mi.reset() 129 130 // test "attribute" 131 el.addNodeKey("prefix1/node1/attr1") 132 el.addNodeKey("prefix1/node1") 133 el.addNodeKey("prefix1/node2") 134 el.addNodeKey("prefix1/node1/attr2") 135 136 // prefix1/node1 as key 137 el.iterTargets("prefix1/node1", false, mi.visitNode) 138 Expect(mi.visitedNodes).To(HaveLen(1)) 139 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 140 mi.reset() 141 142 // prefix1/node1 as prefix 143 el.iterTargets("prefix1/node1", true, mi.visitNode) 144 Expect(mi.visitedNodes).To(HaveLen(3)) 145 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 146 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1/attr1")) 147 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1/attr2")) 148 mi.reset() 149 } 150 151 func TestLookupOverNodeKeysWithOverlay(t *testing.T) { 152 RegisterTestingT(t) 153 154 mi := newMockIter() 155 156 el := newEdgeLookup(nil) 157 Expect(el).ToNot(BeNil()) 158 159 el.addNodeKey("prefix1/node1") 160 el.addNodeKey("prefix2/node3") 161 162 elOver := el.makeOverlay() 163 Expect(elOver).ToNot(BeNil()) 164 Expect(elOver.underlay).To(Equal(el)) 165 Expect(el.overlay).To(Equal(elOver)) 166 167 elOver.addNodeKey("prefix1/node2") 168 169 // check in overlay 170 elOver.iterTargets("prefix1/node1", false, mi.visitNode) 171 Expect(mi.visitedNodes).To(HaveLen(1)) 172 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 173 mi.reset() 174 elOver.iterTargets("prefix2/node1", false, mi.visitNode) 175 Expect(mi.visitedNodes).To(BeEmpty()) 176 elOver.iterTargets("prefix1/", true, mi.visitNode) 177 Expect(mi.visitedNodes).To(HaveLen(2)) 178 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 179 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 180 mi.reset() 181 elOver.iterTargets("prefix2/", true, mi.visitNode) 182 Expect(mi.visitedNodes).To(HaveLen(1)) 183 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 184 mi.reset() 185 elOver.iterTargets("", true, mi.visitNode) 186 Expect(mi.visitedNodes).To(HaveLen(3)) 187 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 188 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 189 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 190 mi.reset() 191 192 // check in the underlay before saving 193 el.iterTargets("prefix1/node1", false, mi.visitNode) 194 Expect(mi.visitedNodes).To(HaveLen(1)) 195 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 196 mi.reset() 197 el.iterTargets("prefix1/node2", false, mi.visitNode) 198 Expect(mi.visitedNodes).To(BeEmpty()) 199 el.iterTargets("prefix1/", true, mi.visitNode) 200 Expect(mi.visitedNodes).To(HaveLen(1)) 201 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 202 mi.reset() 203 el.iterTargets("prefix2/", true, mi.visitNode) 204 Expect(mi.visitedNodes).To(HaveLen(1)) 205 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 206 mi.reset() 207 el.iterTargets("", true, mi.visitNode) 208 Expect(mi.visitedNodes).To(HaveLen(2)) 209 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 210 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 211 mi.reset() 212 213 // save and re-check in the underlay 214 elOver.saveOverlay() 215 el.iterTargets("prefix1/node1", false, mi.visitNode) 216 Expect(mi.visitedNodes).To(HaveLen(1)) 217 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 218 mi.reset() 219 el.iterTargets("prefix1/node2", false, mi.visitNode) 220 Expect(mi.visitedNodes).To(HaveLen(1)) 221 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 222 mi.reset() 223 el.iterTargets("prefix1/", true, mi.visitNode) 224 Expect(mi.visitedNodes).To(HaveLen(2)) 225 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 226 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 227 mi.reset() 228 el.iterTargets("prefix2/", true, mi.visitNode) 229 Expect(mi.visitedNodes).To(HaveLen(1)) 230 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 231 mi.reset() 232 el.iterTargets("", true, mi.visitNode) 233 Expect(mi.visitedNodes).To(HaveLen(3)) 234 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 235 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 236 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 237 mi.reset() 238 239 // delete two in overlay 240 elOver.delNodeKey("prefix1/node2") 241 elOver.delNodeKey("prefix2/node3") 242 243 // check in overlay: 244 elOver.iterTargets("prefix1/node1", false, mi.visitNode) 245 Expect(mi.visitedNodes).To(HaveLen(1)) 246 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 247 mi.reset() 248 elOver.iterTargets("prefix1/", true, mi.visitNode) 249 Expect(mi.visitedNodes).To(HaveLen(1)) 250 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 251 mi.reset() 252 elOver.iterTargets("prefix2/", true, mi.visitNode) 253 Expect(mi.visitedNodes).To(BeEmpty()) 254 elOver.iterTargets("", true, mi.visitNode) 255 Expect(mi.visitedNodes).To(HaveLen(1)) 256 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 257 mi.reset() 258 259 // check in underlay before save: 260 el.iterTargets("prefix1/node1", false, mi.visitNode) 261 Expect(mi.visitedNodes).To(HaveLen(1)) 262 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 263 mi.reset() 264 el.iterTargets("prefix1/node2", false, mi.visitNode) 265 Expect(mi.visitedNodes).To(HaveLen(1)) 266 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 267 mi.reset() 268 el.iterTargets("prefix1/", true, mi.visitNode) 269 Expect(mi.visitedNodes).To(HaveLen(2)) 270 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 271 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 272 mi.reset() 273 el.iterTargets("prefix2/", true, mi.visitNode) 274 Expect(mi.visitedNodes).To(HaveLen(1)) 275 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 276 mi.reset() 277 el.iterTargets("", true, mi.visitNode) 278 Expect(mi.visitedNodes).To(HaveLen(3)) 279 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 280 Expect(mi.visitedNodes).To(HaveKey("prefix1/node2")) 281 Expect(mi.visitedNodes).To(HaveKey("prefix2/node3")) 282 mi.reset() 283 284 // save and re-check underlay 285 elOver.saveOverlay() 286 el.iterTargets("prefix1/node1", false, mi.visitNode) 287 Expect(mi.visitedNodes).To(HaveLen(1)) 288 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 289 mi.reset() 290 el.iterTargets("prefix1/", true, mi.visitNode) 291 Expect(mi.visitedNodes).To(HaveLen(1)) 292 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 293 mi.reset() 294 el.iterTargets("prefix2/", true, mi.visitNode) 295 Expect(mi.visitedNodes).To(BeEmpty()) 296 el.iterTargets("", true, mi.visitNode) 297 Expect(mi.visitedNodes).To(HaveLen(1)) 298 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 299 mi.reset() 300 Expect(elOver.nodeKeyData).To(HaveLen(1)) 301 Expect(elOver.nodeKeyOffset).To(HaveLen(1)) 302 Expect(el.nodeKeyData).To(HaveLen(1)) 303 Expect(el.nodeKeyOffset).To(HaveLen(1)) 304 305 // remove the last key in overlay, but do not save 306 Expect(el.makeOverlay()).To(Equal(elOver)) 307 elOver.delNodeKey("prefix1/node1") 308 Expect(elOver.nodeKeyData).To(HaveLen(0)) 309 Expect(elOver.nodeKeyOffset).To(HaveLen(0)) 310 Expect(el.nodeKeyData).To(HaveLen(1)) 311 Expect(el.nodeKeyOffset).To(HaveLen(1)) 312 elOver.iterTargets("", true, mi.visitNode) 313 Expect(mi.visitedNodes).To(BeEmpty()) 314 315 // no changes 316 elOver = el.makeOverlay() 317 elOver.saveOverlay() 318 el.iterTargets("prefix1/node1", false, mi.visitNode) 319 Expect(mi.visitedNodes).To(HaveLen(1)) 320 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 321 mi.reset() 322 el.iterTargets("prefix1/", true, mi.visitNode) 323 Expect(mi.visitedNodes).To(HaveLen(1)) 324 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 325 mi.reset() 326 el.iterTargets("prefix2/", true, mi.visitNode) 327 Expect(mi.visitedNodes).To(BeEmpty()) 328 el.iterTargets("", true, mi.visitNode) 329 Expect(mi.visitedNodes).To(HaveLen(1)) 330 Expect(mi.visitedNodes).To(HaveKey("prefix1/node1")) 331 mi.reset() 332 Expect(elOver.nodeKeyData).To(HaveLen(1)) 333 Expect(elOver.nodeKeyOffset).To(HaveLen(1)) 334 Expect(el.nodeKeyData).To(HaveLen(1)) 335 Expect(el.nodeKeyOffset).To(HaveLen(1)) 336 } 337 338 func TestLookupOverEdges(t *testing.T) { 339 RegisterTestingT(t) 340 341 mi := newMockIter() 342 343 el := newEdgeLookup(nil) 344 Expect(el).ToNot(BeNil()) 345 346 el.iterSources("some-key", mi.visitEdge) 347 Expect(mi.visitedEdges).To(BeEmpty()) 348 349 el.addEdge(edge{ // "prefix1/node2" -> "prefix1/node1" 350 targetKey: "prefix1/node1", 351 isPrefix: false, 352 sourceNode: "prefix1/node2", 353 relation: "depends-on", 354 label: "node1 exists", 355 }) 356 Expect(el.verifyDirDepthBounds()).To(BeNil()) 357 el.addEdge(edge{ // "prefix1/node2" -> "prefix2/node3" 358 targetKey: "prefix2/node3", 359 isPrefix: false, 360 sourceNode: "prefix1/node2", 361 relation: "depends-on", 362 label: "node3 exists", 363 }) 364 Expect(el.verifyDirDepthBounds()).To(BeNil()) 365 366 el.addEdge(edge{ // "prefix2/node3" -> "prefix1/*" 367 targetKey: "prefix1/", 368 isPrefix: true, 369 sourceNode: "prefix2/node3", 370 relation: "depends-on", 371 label: "prefix1 non-empty", 372 }) 373 Expect(el.verifyDirDepthBounds()).To(BeNil()) 374 el.addEdge(edge{ // "prefix2/node3" -> "prefix2/node3" 375 targetKey: "prefix2/node3", 376 isPrefix: false, 377 sourceNode: "prefix2/node3", 378 relation: "myself", 379 label: "edge to itself", 380 }) 381 Expect(el.verifyDirDepthBounds()).To(BeNil()) 382 383 el.addEdge(edge{ 384 targetKey: "", // "prefix1/node1" -> * 385 isPrefix: true, 386 sourceNode: "prefix1/node1", 387 relation: "all", 388 label: "all", 389 }) 390 Expect(el.verifyDirDepthBounds()).To(BeNil()) 391 392 Expect(el.edgeData).To(HaveLen(5)) 393 Expect(el.edgeOffset).To(HaveLen(5)) 394 395 el.iterSources("prefix1/node1", mi.visitEdge) 396 Expect(mi.visitedEdges).To(HaveLen(3)) 397 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 398 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 399 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 400 mi.reset() 401 402 el.iterSources("prefix1/node2", mi.visitEdge) 403 Expect(mi.visitedEdges).To(HaveLen(2)) 404 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 405 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 406 mi.reset() 407 408 el.iterSources("prefix2/node3", mi.visitEdge) 409 Expect(mi.visitedEdges).To(HaveLen(3)) 410 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node3 exists"})) 411 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 412 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 413 mi.reset() 414 415 // delete 2 edges 416 el.delEdge(edge{ // "prefix1/node2" -> "prefix2/node3" 417 targetKey: "prefix2/node3", 418 isPrefix: false, 419 sourceNode: "prefix1/node2", 420 relation: "depends-on", 421 label: "node3 exists", 422 }) 423 Expect(el.verifyDirDepthBounds()).To(BeNil()) 424 el.delEdge(edge{ 425 targetKey: "", // "prefix1/node1" -> * 426 isPrefix: true, 427 sourceNode: "prefix1/node1", 428 relation: "all", 429 label: "all", 430 }) 431 Expect(el.verifyDirDepthBounds()).To(BeNil()) 432 Expect(el.edgeData).To(HaveLen(5)) // not gc-ed yet 433 Expect(el.edgeOffset).To(HaveLen(5)) // not gc-ed yet 434 435 el.iterSources("prefix1/node1", mi.visitEdge) 436 Expect(mi.visitedEdges).To(HaveLen(2)) 437 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 438 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 439 mi.reset() 440 441 el.iterSources("prefix1/node2", mi.visitEdge) 442 Expect(mi.visitedEdges).To(HaveLen(1)) 443 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 444 mi.reset() 445 446 el.iterSources("prefix2/node3", mi.visitEdge) 447 Expect(mi.visitedEdges).To(HaveLen(1)) 448 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 449 mi.reset() 450 451 // delete another edge 452 el.delEdge(edge{ // "prefix2/node3" -> "prefix1/*" 453 targetKey: "prefix1/", 454 isPrefix: true, 455 sourceNode: "prefix2/node3", 456 relation: "depends-on", 457 label: "prefix1 non-empty", 458 }) 459 Expect(el.verifyDirDepthBounds()).To(BeNil()) 460 Expect(el.edgeData).To(HaveLen(2)) // gc-ed 461 Expect(el.edgeOffset).To(HaveLen(2)) // gc-ed 462 463 el.iterSources("prefix1/node1", mi.visitEdge) 464 Expect(mi.visitedEdges).To(HaveLen(1)) 465 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 466 mi.reset() 467 468 el.iterSources("prefix1/node2", mi.visitEdge) 469 Expect(mi.visitedEdges).To(BeEmpty()) 470 mi.reset() 471 472 el.iterSources("prefix2/node3", mi.visitEdge) 473 Expect(mi.visitedEdges).To(HaveLen(1)) 474 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 475 mi.reset() 476 477 // delete the remaining edges 478 el.delEdge(edge{ // "prefix1/node2" -> "prefix1/node1" 479 targetKey: "prefix1/node1", 480 isPrefix: false, 481 sourceNode: "prefix1/node2", 482 relation: "depends-on", 483 label: "node1 exists", 484 }) 485 Expect(el.verifyDirDepthBounds()).To(BeNil()) 486 el.delEdge(edge{ // "prefix2/node3" -> "prefix2/node3" 487 targetKey: "prefix2/node3", 488 isPrefix: false, 489 sourceNode: "prefix2/node3", 490 relation: "myself", 491 label: "edge to itself", 492 }) 493 Expect(el.verifyDirDepthBounds()).To(BeNil()) 494 Expect(el.edgeData).To(BeEmpty()) // gc-ed 495 Expect(el.edgeOffset).To(BeEmpty()) // gc-ed 496 497 el.iterSources("prefix1/node1", mi.visitEdge) 498 Expect(mi.visitedEdges).To(BeEmpty()) 499 500 el.iterSources("prefix1/node2", mi.visitEdge) 501 Expect(mi.visitedEdges).To(BeEmpty()) 502 503 el.iterSources("prefix2/node3", mi.visitEdge) 504 Expect(mi.visitedEdges).To(BeEmpty()) 505 } 506 507 func TestLookupOverEdgesWithOverlay(t *testing.T) { 508 RegisterTestingT(t) 509 510 mi := newMockIter() 511 512 el := newEdgeLookup(nil) 513 Expect(el).ToNot(BeNil()) 514 515 el.addEdge(edge{ // "prefix1/node2" -> "prefix1/node1" 516 targetKey: "prefix1/node1", 517 isPrefix: false, 518 sourceNode: "prefix1/node2", 519 relation: "depends-on", 520 label: "node1 exists", 521 }) 522 Expect(el.verifyDirDepthBounds()).To(BeNil()) 523 el.addEdge(edge{ // "prefix1/node2" -> "prefix2/node3" 524 targetKey: "prefix2/node3", 525 isPrefix: false, 526 sourceNode: "prefix1/node2", 527 relation: "depends-on", 528 label: "node3 exists", 529 }) 530 Expect(el.verifyDirDepthBounds()).To(BeNil()) 531 el.addEdge(edge{ // "prefix2/node3" -> "prefix1/*" 532 targetKey: "prefix1/", 533 isPrefix: true, 534 sourceNode: "prefix2/node3", 535 relation: "depends-on", 536 label: "prefix1 non-empty", 537 }) 538 Expect(el.verifyDirDepthBounds()).To(BeNil()) 539 540 elOver := el.makeOverlay() 541 Expect(elOver).ToNot(BeNil()) 542 Expect(elOver.underlay).To(Equal(el)) 543 Expect(el.overlay).To(Equal(elOver)) 544 Expect(elOver.verifyDirDepthBounds()).To(BeNil()) 545 546 elOver.addEdge(edge{ // "prefix2/node3" -> "prefix2/node3" 547 targetKey: "prefix2/node3", 548 isPrefix: false, 549 sourceNode: "prefix2/node3", 550 relation: "myself", 551 label: "edge to itself", 552 }) 553 Expect(elOver.verifyDirDepthBounds()).To(BeNil()) 554 555 elOver.addEdge(edge{ 556 targetKey: "", // "prefix1/node1" -> * 557 isPrefix: true, 558 sourceNode: "prefix1/node1", 559 relation: "all", 560 label: "all", 561 }) 562 Expect(elOver.verifyDirDepthBounds()).To(BeNil()) 563 564 Expect(el.edgeData).To(HaveLen(3)) 565 Expect(elOver.edgeOffset).To(HaveLen(5)) 566 567 // check overlay 568 elOver.iterSources("prefix1/node1", mi.visitEdge) 569 Expect(mi.visitedEdges).To(HaveLen(3)) 570 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 571 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 572 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 573 mi.reset() 574 elOver.iterSources("prefix1/node2", mi.visitEdge) 575 Expect(mi.visitedEdges).To(HaveLen(2)) 576 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 577 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 578 mi.reset() 579 elOver.iterSources("prefix2/node3", mi.visitEdge) 580 Expect(mi.visitedEdges).To(HaveLen(3)) 581 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node3 exists"})) 582 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 583 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 584 mi.reset() 585 586 // check underlay 587 el.iterSources("prefix1/node1", mi.visitEdge) 588 Expect(mi.visitedEdges).To(HaveLen(2)) 589 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 590 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 591 mi.reset() 592 el.iterSources("prefix1/node2", mi.visitEdge) 593 Expect(mi.visitedEdges).To(HaveLen(1)) 594 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 595 mi.reset() 596 el.iterSources("prefix2/node3", mi.visitEdge) 597 Expect(mi.visitedEdges).To(HaveLen(1)) 598 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node3 exists"})) 599 mi.reset() 600 601 // save and re-check underlay 602 elOver.saveOverlay() 603 Expect(el.verifyDirDepthBounds()).To(BeNil()) 604 el.iterSources("prefix1/node1", mi.visitEdge) 605 Expect(mi.visitedEdges).To(HaveLen(3)) 606 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 607 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 608 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 609 mi.reset() 610 el.iterSources("prefix1/node2", mi.visitEdge) 611 Expect(mi.visitedEdges).To(HaveLen(2)) 612 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 613 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 614 mi.reset() 615 el.iterSources("prefix2/node3", mi.visitEdge) 616 Expect(mi.visitedEdges).To(HaveLen(3)) 617 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node3 exists"})) 618 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 619 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 620 mi.reset() 621 622 // delete 2 edges in overlay 623 elOver.delEdge(edge{ // "prefix1/node2" -> "prefix2/node3" 624 targetKey: "prefix2/node3", 625 isPrefix: false, 626 sourceNode: "prefix1/node2", 627 relation: "depends-on", 628 label: "node3 exists", 629 }) 630 Expect(elOver.verifyDirDepthBounds()).To(BeNil()) 631 elOver.delEdge(edge{ 632 targetKey: "", // "prefix1/node1" -> * 633 isPrefix: true, 634 sourceNode: "prefix1/node1", 635 relation: "all", 636 label: "all", 637 }) 638 Expect(elOver.verifyDirDepthBounds()).To(BeNil()) 639 Expect(elOver.edgeData).To(HaveLen(5)) // not gc-ed yet 640 Expect(elOver.edgeOffset).To(HaveLen(5)) // not gc-ed yet 641 642 // check overlay 643 elOver.iterSources("prefix1/node1", mi.visitEdge) 644 Expect(mi.visitedEdges).To(HaveLen(2)) 645 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 646 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 647 mi.reset() 648 elOver.iterSources("prefix1/node2", mi.visitEdge) 649 Expect(mi.visitedEdges).To(HaveLen(1)) 650 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 651 mi.reset() 652 elOver.iterSources("prefix2/node3", mi.visitEdge) 653 Expect(mi.visitedEdges).To(HaveLen(1)) 654 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 655 mi.reset() 656 657 // underlay before save 658 el.iterSources("prefix1/node1", mi.visitEdge) 659 Expect(mi.visitedEdges).To(HaveLen(3)) 660 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 661 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 662 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 663 mi.reset() 664 el.iterSources("prefix1/node2", mi.visitEdge) 665 Expect(mi.visitedEdges).To(HaveLen(2)) 666 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 667 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 668 mi.reset() 669 el.iterSources("prefix2/node3", mi.visitEdge) 670 Expect(mi.visitedEdges).To(HaveLen(3)) 671 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node3 exists"})) 672 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 673 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node1", "all", "all"})) 674 mi.reset() 675 676 // save and re-check underlay 677 elOver.saveOverlay() 678 Expect(el.verifyDirDepthBounds()).To(BeNil()) 679 el.iterSources("prefix1/node1", mi.visitEdge) 680 Expect(mi.visitedEdges).To(HaveLen(2)) 681 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 682 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 683 mi.reset() 684 el.iterSources("prefix1/node2", mi.visitEdge) 685 Expect(mi.visitedEdges).To(HaveLen(1)) 686 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 687 mi.reset() 688 el.iterSources("prefix2/node3", mi.visitEdge) 689 Expect(mi.visitedEdges).To(HaveLen(1)) 690 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 691 mi.reset() 692 693 // delete another edge, but do not save 694 elOver.delEdge(edge{ // "prefix2/node3" -> "prefix1/*" 695 targetKey: "prefix1/", 696 isPrefix: true, 697 sourceNode: "prefix2/node3", 698 relation: "depends-on", 699 label: "prefix1 non-empty", 700 }) 701 Expect(elOver.verifyDirDepthBounds()).To(BeNil()) 702 Expect(elOver.edgeData).To(HaveLen(2)) // gc-ed 703 Expect(elOver.edgeOffset).To(HaveLen(2)) // gc-ed 704 Expect(el.edgeOffset).To(HaveLen(5)) 705 Expect(el.edgeData).To(HaveLen(5)) 706 707 // check overlay 708 elOver.iterSources("prefix1/node1", mi.visitEdge) 709 Expect(mi.visitedEdges).To(HaveLen(1)) 710 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 711 mi.reset() 712 elOver.iterSources("prefix1/node2", mi.visitEdge) 713 Expect(mi.visitedEdges).To(BeEmpty()) 714 mi.reset() 715 elOver.iterSources("prefix2/node3", mi.visitEdge) 716 Expect(mi.visitedEdges).To(HaveLen(1)) 717 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 718 mi.reset() 719 720 // throw away changes 721 Expect(el.makeOverlay()).To(Equal(elOver)) 722 elOver = el.makeOverlay() 723 elOver.saveOverlay() 724 Expect(el.verifyDirDepthBounds()).To(BeNil()) 725 726 // check underlay 727 el.iterSources("prefix1/node1", mi.visitEdge) 728 Expect(mi.visitedEdges).To(HaveLen(2)) 729 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix1/node2", "depends-on", "node1 exists"})) 730 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 731 mi.reset() 732 el.iterSources("prefix1/node2", mi.visitEdge) 733 Expect(mi.visitedEdges).To(HaveLen(1)) 734 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "depends-on", "prefix1 non-empty"})) 735 mi.reset() 736 el.iterSources("prefix2/node3", mi.visitEdge) 737 Expect(mi.visitedEdges).To(HaveLen(1)) 738 Expect(mi.visitedEdges).To(HaveKey(visitedEdge{"prefix2/node3", "myself", "edge to itself"})) 739 mi.reset() 740 }