dump_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package garbagecollector
  14. import (
  15. "sort"
  16. "testing"
  17. "github.com/davecgh/go-spew/spew"
  18. "gonum.org/v1/gonum/graph"
  19. "gonum.org/v1/gonum/graph/simple"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/types"
  22. )
  23. var (
  24. alphaNode = func() *node {
  25. return &node{
  26. identity: objectReference{
  27. OwnerReference: metav1.OwnerReference{
  28. UID: types.UID("alpha"),
  29. },
  30. },
  31. owners: []metav1.OwnerReference{
  32. {UID: types.UID("bravo")},
  33. {UID: types.UID("charlie")},
  34. },
  35. }
  36. }
  37. bravoNode = func() *node {
  38. return &node{
  39. identity: objectReference{
  40. OwnerReference: metav1.OwnerReference{
  41. UID: types.UID("bravo"),
  42. },
  43. },
  44. dependents: map[*node]struct{}{
  45. alphaNode(): {},
  46. },
  47. }
  48. }
  49. charlieNode = func() *node {
  50. return &node{
  51. identity: objectReference{
  52. OwnerReference: metav1.OwnerReference{
  53. UID: types.UID("charlie"),
  54. },
  55. },
  56. dependents: map[*node]struct{}{
  57. alphaNode(): {},
  58. },
  59. }
  60. }
  61. deltaNode = func() *node {
  62. return &node{
  63. identity: objectReference{
  64. OwnerReference: metav1.OwnerReference{
  65. UID: types.UID("delta"),
  66. },
  67. },
  68. owners: []metav1.OwnerReference{
  69. {UID: types.UID("foxtrot")},
  70. },
  71. }
  72. }
  73. echoNode = func() *node {
  74. return &node{
  75. identity: objectReference{
  76. OwnerReference: metav1.OwnerReference{
  77. UID: types.UID("echo"),
  78. },
  79. },
  80. }
  81. }
  82. foxtrotNode = func() *node {
  83. return &node{
  84. identity: objectReference{
  85. OwnerReference: metav1.OwnerReference{
  86. UID: types.UID("foxtrot"),
  87. },
  88. },
  89. owners: []metav1.OwnerReference{
  90. {UID: types.UID("golf")},
  91. },
  92. dependents: map[*node]struct{}{
  93. deltaNode(): {},
  94. },
  95. }
  96. }
  97. golfNode = func() *node {
  98. return &node{
  99. identity: objectReference{
  100. OwnerReference: metav1.OwnerReference{
  101. UID: types.UID("golf"),
  102. },
  103. },
  104. dependents: map[*node]struct{}{
  105. foxtrotNode(): {},
  106. },
  107. }
  108. }
  109. )
  110. func TestToGonumGraph(t *testing.T) {
  111. tests := []struct {
  112. name string
  113. uidToNode map[types.UID]*node
  114. expect graph.Directed
  115. }{
  116. {
  117. name: "simple",
  118. uidToNode: map[types.UID]*node{
  119. types.UID("alpha"): alphaNode(),
  120. types.UID("bravo"): bravoNode(),
  121. types.UID("charlie"): charlieNode(),
  122. },
  123. expect: func() graph.Directed {
  124. graphBuilder := simple.NewDirectedGraph()
  125. alphaVertex := NewGonumVertex(alphaNode(), graphBuilder.NewNode().ID())
  126. graphBuilder.AddNode(alphaVertex)
  127. bravoVertex := NewGonumVertex(bravoNode(), graphBuilder.NewNode().ID())
  128. graphBuilder.AddNode(bravoVertex)
  129. charlieVertex := NewGonumVertex(charlieNode(), graphBuilder.NewNode().ID())
  130. graphBuilder.AddNode(charlieVertex)
  131. graphBuilder.SetEdge(simple.Edge{
  132. F: alphaVertex,
  133. T: bravoVertex,
  134. })
  135. graphBuilder.SetEdge(simple.Edge{
  136. F: alphaVertex,
  137. T: charlieVertex,
  138. })
  139. return graphBuilder
  140. }(),
  141. },
  142. {
  143. name: "missing", // synthetic vertex created
  144. uidToNode: map[types.UID]*node{
  145. types.UID("alpha"): alphaNode(),
  146. types.UID("charlie"): charlieNode(),
  147. },
  148. expect: func() graph.Directed {
  149. graphBuilder := simple.NewDirectedGraph()
  150. alphaVertex := NewGonumVertex(alphaNode(), graphBuilder.NewNode().ID())
  151. graphBuilder.AddNode(alphaVertex)
  152. bravoVertex := NewGonumVertex(bravoNode(), graphBuilder.NewNode().ID())
  153. graphBuilder.AddNode(bravoVertex)
  154. charlieVertex := NewGonumVertex(charlieNode(), graphBuilder.NewNode().ID())
  155. graphBuilder.AddNode(charlieVertex)
  156. graphBuilder.SetEdge(simple.Edge{
  157. F: alphaVertex,
  158. T: bravoVertex,
  159. })
  160. graphBuilder.SetEdge(simple.Edge{
  161. F: alphaVertex,
  162. T: charlieVertex,
  163. })
  164. return graphBuilder
  165. }(),
  166. },
  167. {
  168. name: "drop-no-ref",
  169. uidToNode: map[types.UID]*node{
  170. types.UID("alpha"): alphaNode(),
  171. types.UID("bravo"): bravoNode(),
  172. types.UID("charlie"): charlieNode(),
  173. types.UID("echo"): echoNode(),
  174. },
  175. expect: func() graph.Directed {
  176. graphBuilder := simple.NewDirectedGraph()
  177. alphaVertex := NewGonumVertex(alphaNode(), graphBuilder.NewNode().ID())
  178. graphBuilder.AddNode(alphaVertex)
  179. bravoVertex := NewGonumVertex(bravoNode(), graphBuilder.NewNode().ID())
  180. graphBuilder.AddNode(bravoVertex)
  181. charlieVertex := NewGonumVertex(charlieNode(), graphBuilder.NewNode().ID())
  182. graphBuilder.AddNode(charlieVertex)
  183. graphBuilder.SetEdge(simple.Edge{
  184. F: alphaVertex,
  185. T: bravoVertex,
  186. })
  187. graphBuilder.SetEdge(simple.Edge{
  188. F: alphaVertex,
  189. T: charlieVertex,
  190. })
  191. return graphBuilder
  192. }(),
  193. },
  194. {
  195. name: "two-chains",
  196. uidToNode: map[types.UID]*node{
  197. types.UID("alpha"): alphaNode(),
  198. types.UID("bravo"): bravoNode(),
  199. types.UID("charlie"): charlieNode(),
  200. types.UID("delta"): deltaNode(),
  201. types.UID("foxtrot"): foxtrotNode(),
  202. types.UID("golf"): golfNode(),
  203. },
  204. expect: func() graph.Directed {
  205. graphBuilder := simple.NewDirectedGraph()
  206. alphaVertex := NewGonumVertex(alphaNode(), graphBuilder.NewNode().ID())
  207. graphBuilder.AddNode(alphaVertex)
  208. bravoVertex := NewGonumVertex(bravoNode(), graphBuilder.NewNode().ID())
  209. graphBuilder.AddNode(bravoVertex)
  210. charlieVertex := NewGonumVertex(charlieNode(), graphBuilder.NewNode().ID())
  211. graphBuilder.AddNode(charlieVertex)
  212. graphBuilder.SetEdge(simple.Edge{
  213. F: alphaVertex,
  214. T: bravoVertex,
  215. })
  216. graphBuilder.SetEdge(simple.Edge{
  217. F: alphaVertex,
  218. T: charlieVertex,
  219. })
  220. deltaVertex := NewGonumVertex(deltaNode(), graphBuilder.NewNode().ID())
  221. graphBuilder.AddNode(deltaVertex)
  222. foxtrotVertex := NewGonumVertex(foxtrotNode(), graphBuilder.NewNode().ID())
  223. graphBuilder.AddNode(foxtrotVertex)
  224. golfVertex := NewGonumVertex(golfNode(), graphBuilder.NewNode().ID())
  225. graphBuilder.AddNode(golfVertex)
  226. graphBuilder.SetEdge(simple.Edge{
  227. F: deltaVertex,
  228. T: foxtrotVertex,
  229. })
  230. graphBuilder.SetEdge(simple.Edge{
  231. F: foxtrotVertex,
  232. T: golfVertex,
  233. })
  234. return graphBuilder
  235. }(),
  236. },
  237. }
  238. for _, test := range tests {
  239. t.Run(test.name, func(t *testing.T) {
  240. actual := toGonumGraph(test.uidToNode)
  241. compareGraphs(test.expect, actual, t)
  242. })
  243. }
  244. }
  245. func TestToGonumGraphObj(t *testing.T) {
  246. tests := []struct {
  247. name string
  248. uidToNode map[types.UID]*node
  249. uids []types.UID
  250. expect graph.Directed
  251. }{
  252. {
  253. name: "simple",
  254. uidToNode: map[types.UID]*node{
  255. types.UID("alpha"): alphaNode(),
  256. types.UID("bravo"): bravoNode(),
  257. types.UID("charlie"): charlieNode(),
  258. },
  259. uids: []types.UID{types.UID("bravo")},
  260. expect: func() graph.Directed {
  261. graphBuilder := simple.NewDirectedGraph()
  262. alphaVertex := NewGonumVertex(alphaNode(), graphBuilder.NewNode().ID())
  263. graphBuilder.AddNode(alphaVertex)
  264. bravoVertex := NewGonumVertex(bravoNode(), graphBuilder.NewNode().ID())
  265. graphBuilder.AddNode(bravoVertex)
  266. charlieVertex := NewGonumVertex(charlieNode(), graphBuilder.NewNode().ID())
  267. graphBuilder.AddNode(charlieVertex)
  268. graphBuilder.SetEdge(simple.Edge{
  269. F: alphaVertex,
  270. T: bravoVertex,
  271. })
  272. graphBuilder.SetEdge(simple.Edge{
  273. F: alphaVertex,
  274. T: charlieVertex,
  275. })
  276. return graphBuilder
  277. }(),
  278. },
  279. {
  280. name: "missing", // synthetic vertex created
  281. uidToNode: map[types.UID]*node{
  282. types.UID("alpha"): alphaNode(),
  283. types.UID("charlie"): charlieNode(),
  284. },
  285. uids: []types.UID{types.UID("bravo")},
  286. expect: func() graph.Directed {
  287. graphBuilder := simple.NewDirectedGraph()
  288. return graphBuilder
  289. }(),
  290. },
  291. {
  292. name: "drop-no-ref",
  293. uidToNode: map[types.UID]*node{
  294. types.UID("alpha"): alphaNode(),
  295. types.UID("bravo"): bravoNode(),
  296. types.UID("charlie"): charlieNode(),
  297. types.UID("echo"): echoNode(),
  298. },
  299. uids: []types.UID{types.UID("echo")},
  300. expect: func() graph.Directed {
  301. graphBuilder := simple.NewDirectedGraph()
  302. return graphBuilder
  303. }(),
  304. },
  305. {
  306. name: "two-chains-from-owner",
  307. uidToNode: map[types.UID]*node{
  308. types.UID("alpha"): alphaNode(),
  309. types.UID("bravo"): bravoNode(),
  310. types.UID("charlie"): charlieNode(),
  311. types.UID("delta"): deltaNode(),
  312. types.UID("foxtrot"): foxtrotNode(),
  313. types.UID("golf"): golfNode(),
  314. },
  315. uids: []types.UID{types.UID("golf")},
  316. expect: func() graph.Directed {
  317. graphBuilder := simple.NewDirectedGraph()
  318. deltaVertex := NewGonumVertex(deltaNode(), graphBuilder.NewNode().ID())
  319. graphBuilder.AddNode(deltaVertex)
  320. foxtrotVertex := NewGonumVertex(foxtrotNode(), graphBuilder.NewNode().ID())
  321. graphBuilder.AddNode(foxtrotVertex)
  322. golfVertex := NewGonumVertex(golfNode(), graphBuilder.NewNode().ID())
  323. graphBuilder.AddNode(golfVertex)
  324. graphBuilder.SetEdge(simple.Edge{
  325. F: deltaVertex,
  326. T: foxtrotVertex,
  327. })
  328. graphBuilder.SetEdge(simple.Edge{
  329. F: foxtrotVertex,
  330. T: golfVertex,
  331. })
  332. return graphBuilder
  333. }(),
  334. },
  335. {
  336. name: "two-chains-from-child",
  337. uidToNode: map[types.UID]*node{
  338. types.UID("alpha"): alphaNode(),
  339. types.UID("bravo"): bravoNode(),
  340. types.UID("charlie"): charlieNode(),
  341. types.UID("delta"): deltaNode(),
  342. types.UID("foxtrot"): foxtrotNode(),
  343. types.UID("golf"): golfNode(),
  344. },
  345. uids: []types.UID{types.UID("delta")},
  346. expect: func() graph.Directed {
  347. graphBuilder := simple.NewDirectedGraph()
  348. deltaVertex := NewGonumVertex(deltaNode(), graphBuilder.NewNode().ID())
  349. graphBuilder.AddNode(deltaVertex)
  350. foxtrotVertex := NewGonumVertex(foxtrotNode(), graphBuilder.NewNode().ID())
  351. graphBuilder.AddNode(foxtrotVertex)
  352. golfVertex := NewGonumVertex(golfNode(), graphBuilder.NewNode().ID())
  353. graphBuilder.AddNode(golfVertex)
  354. graphBuilder.SetEdge(simple.Edge{
  355. F: deltaVertex,
  356. T: foxtrotVertex,
  357. })
  358. graphBuilder.SetEdge(simple.Edge{
  359. F: foxtrotVertex,
  360. T: golfVertex,
  361. })
  362. return graphBuilder
  363. }(),
  364. },
  365. {
  366. name: "two-chains-choose-both",
  367. uidToNode: map[types.UID]*node{
  368. types.UID("alpha"): alphaNode(),
  369. types.UID("bravo"): bravoNode(),
  370. types.UID("charlie"): charlieNode(),
  371. types.UID("delta"): deltaNode(),
  372. types.UID("foxtrot"): foxtrotNode(),
  373. types.UID("golf"): golfNode(),
  374. },
  375. uids: []types.UID{types.UID("delta"), types.UID("charlie")},
  376. expect: func() graph.Directed {
  377. graphBuilder := simple.NewDirectedGraph()
  378. alphaVertex := NewGonumVertex(alphaNode(), graphBuilder.NewNode().ID())
  379. graphBuilder.AddNode(alphaVertex)
  380. bravoVertex := NewGonumVertex(bravoNode(), graphBuilder.NewNode().ID())
  381. graphBuilder.AddNode(bravoVertex)
  382. charlieVertex := NewGonumVertex(charlieNode(), graphBuilder.NewNode().ID())
  383. graphBuilder.AddNode(charlieVertex)
  384. graphBuilder.SetEdge(simple.Edge{
  385. F: alphaVertex,
  386. T: bravoVertex,
  387. })
  388. graphBuilder.SetEdge(simple.Edge{
  389. F: alphaVertex,
  390. T: charlieVertex,
  391. })
  392. deltaVertex := NewGonumVertex(deltaNode(), graphBuilder.NewNode().ID())
  393. graphBuilder.AddNode(deltaVertex)
  394. foxtrotVertex := NewGonumVertex(foxtrotNode(), graphBuilder.NewNode().ID())
  395. graphBuilder.AddNode(foxtrotVertex)
  396. golfVertex := NewGonumVertex(golfNode(), graphBuilder.NewNode().ID())
  397. graphBuilder.AddNode(golfVertex)
  398. graphBuilder.SetEdge(simple.Edge{
  399. F: deltaVertex,
  400. T: foxtrotVertex,
  401. })
  402. graphBuilder.SetEdge(simple.Edge{
  403. F: foxtrotVertex,
  404. T: golfVertex,
  405. })
  406. return graphBuilder
  407. }(),
  408. },
  409. }
  410. for _, test := range tests {
  411. t.Run(test.name, func(t *testing.T) {
  412. actual := toGonumGraphForObj(test.uidToNode, test.uids...)
  413. compareGraphs(test.expect, actual, t)
  414. })
  415. }
  416. }
  417. func compareGraphs(expected, actual graph.Directed, t *testing.T) {
  418. // sort the edges by from ID, then to ID
  419. // (the slices we get back are from map iteration, where order is not guaranteed)
  420. expectedNodes := expected.Nodes().(graph.NodeSlicer).NodeSlice()
  421. actualNodes := actual.Nodes().(graph.NodeSlicer).NodeSlice()
  422. sort.Sort(gonumByUID(expectedNodes))
  423. sort.Sort(gonumByUID(actualNodes))
  424. if len(expectedNodes) != len(actualNodes) {
  425. t.Fatal(spew.Sdump(actual))
  426. }
  427. for i := range expectedNodes {
  428. currExpected := *expectedNodes[i].(*gonumVertex)
  429. currActual := *actualNodes[i].(*gonumVertex)
  430. if currExpected.uid != currActual.uid {
  431. t.Errorf("expected %v, got %v", spew.Sdump(currExpected), spew.Sdump(currActual))
  432. }
  433. expectedFrom := append([]graph.Node{}, expected.From(expectedNodes[i].ID()).(graph.NodeSlicer).NodeSlice()...)
  434. actualFrom := append([]graph.Node{}, actual.From(actualNodes[i].ID()).(graph.NodeSlicer).NodeSlice()...)
  435. sort.Sort(gonumByUID(expectedFrom))
  436. sort.Sort(gonumByUID(actualFrom))
  437. if len(expectedFrom) != len(actualFrom) {
  438. t.Errorf("%q: expected %v, got %v", currExpected.uid, spew.Sdump(expectedFrom), spew.Sdump(actualFrom))
  439. }
  440. for i := range expectedFrom {
  441. currExpectedFrom := *expectedFrom[i].(*gonumVertex)
  442. currActualFrom := *actualFrom[i].(*gonumVertex)
  443. if currExpectedFrom.uid != currActualFrom.uid {
  444. t.Errorf("expected %v, got %v", spew.Sdump(currExpectedFrom), spew.Sdump(currActualFrom))
  445. }
  446. }
  447. }
  448. }
  449. type gonumByUID []graph.Node
  450. func (s gonumByUID) Len() int { return len(s) }
  451. func (s gonumByUID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  452. func (s gonumByUID) Less(i, j int) bool {
  453. lhs := s[i].(*gonumVertex)
  454. lhsUID := string(lhs.uid)
  455. rhs := s[j].(*gonumVertex)
  456. rhsUID := string(rhs.uid)
  457. return lhsUID < rhsUID
  458. }