github.com/smithx10/nomad@v0.9.1-rc1/scheduler/select_test.go (about) 1 package scheduler 2 3 import ( 4 "testing" 5 6 "github.com/hashicorp/nomad/nomad/mock" 7 "github.com/hashicorp/nomad/nomad/structs" 8 "github.com/stretchr/testify/require" 9 ) 10 11 func TestLimitIterator(t *testing.T) { 12 _, ctx := testContext(t) 13 nodes := []*RankedNode{ 14 { 15 Node: mock.Node(), 16 FinalScore: 1, 17 }, 18 { 19 Node: mock.Node(), 20 FinalScore: 2, 21 }, 22 { 23 Node: mock.Node(), 24 FinalScore: 3, 25 }, 26 } 27 static := NewStaticRankIterator(ctx, nodes) 28 29 limit := NewLimitIterator(ctx, static, 1, 0, 2) 30 limit.SetLimit(2) 31 32 out := collectRanked(limit) 33 if len(out) != 2 { 34 t.Fatalf("bad: %v", out) 35 } 36 if out[0] != nodes[0] && out[1] != nodes[1] { 37 t.Fatalf("bad: %v", out) 38 } 39 40 out = collectRanked(limit) 41 if len(out) != 0 { 42 t.Fatalf("bad: %v", out) 43 } 44 limit.Reset() 45 46 out = collectRanked(limit) 47 if len(out) != 2 { 48 t.Fatalf("bad: %v", out) 49 } 50 if out[0] != nodes[2] && out[1] != nodes[0] { 51 t.Fatalf("bad: %v", out) 52 } 53 } 54 55 func TestLimitIterator_ScoreThreshold(t *testing.T) { 56 _, ctx := testContext(t) 57 type testCase struct { 58 desc string 59 nodes []*RankedNode 60 expectedOut []*RankedNode 61 threshold float64 62 limit int 63 maxSkip int 64 } 65 66 var nodes []*structs.Node 67 for i := 0; i < 5; i++ { 68 nodes = append(nodes, mock.Node()) 69 } 70 71 testCases := []testCase{ 72 { 73 desc: "Skips one low scoring node", 74 nodes: []*RankedNode{ 75 { 76 Node: nodes[0], 77 FinalScore: -1, 78 }, 79 { 80 Node: nodes[1], 81 FinalScore: 2, 82 }, 83 { 84 Node: nodes[2], 85 FinalScore: 3, 86 }, 87 }, 88 expectedOut: []*RankedNode{ 89 { 90 Node: nodes[1], 91 FinalScore: 2, 92 }, 93 { 94 Node: nodes[2], 95 FinalScore: 3, 96 }, 97 }, 98 threshold: -1, 99 limit: 2, 100 maxSkip: 2, 101 }, 102 { 103 desc: "Skips maxSkip scoring nodes", 104 nodes: []*RankedNode{ 105 { 106 Node: nodes[0], 107 FinalScore: -1, 108 }, 109 { 110 Node: nodes[1], 111 FinalScore: -2, 112 }, 113 { 114 Node: nodes[2], 115 FinalScore: 3, 116 }, 117 { 118 Node: nodes[3], 119 FinalScore: 4, 120 }, 121 }, 122 expectedOut: []*RankedNode{ 123 { 124 Node: nodes[2], 125 FinalScore: 3, 126 }, 127 { 128 Node: nodes[3], 129 FinalScore: 4, 130 }, 131 }, 132 threshold: -1, 133 limit: 2, 134 maxSkip: 2, 135 }, 136 { 137 desc: "maxSkip limit reached", 138 nodes: []*RankedNode{ 139 { 140 Node: nodes[0], 141 FinalScore: -1, 142 }, 143 { 144 Node: nodes[1], 145 FinalScore: -6, 146 }, 147 { 148 Node: nodes[2], 149 FinalScore: -3, 150 }, 151 { 152 Node: nodes[3], 153 FinalScore: -4, 154 }, 155 }, 156 expectedOut: []*RankedNode{ 157 { 158 Node: nodes[2], 159 FinalScore: -3, 160 }, 161 { 162 Node: nodes[3], 163 FinalScore: -4, 164 }, 165 }, 166 threshold: -1, 167 limit: 2, 168 maxSkip: 2, 169 }, 170 { 171 desc: "draw both from skipped nodes", 172 nodes: []*RankedNode{ 173 { 174 Node: nodes[0], 175 FinalScore: -1, 176 }, 177 { 178 Node: nodes[1], 179 FinalScore: -6, 180 }, 181 }, 182 expectedOut: []*RankedNode{ 183 { 184 Node: nodes[0], 185 FinalScore: -1, 186 }, 187 { 188 Node: nodes[1], 189 FinalScore: -6, 190 }, 191 }, 192 threshold: -1, 193 limit: 2, 194 maxSkip: 2, 195 }, { 196 desc: "one node above threshold, one skipped node", 197 nodes: []*RankedNode{ 198 { 199 Node: nodes[0], 200 FinalScore: -1, 201 }, 202 { 203 Node: nodes[1], 204 FinalScore: 5, 205 }, 206 }, 207 expectedOut: []*RankedNode{ 208 { 209 Node: nodes[1], 210 FinalScore: 5, 211 }, 212 { 213 Node: nodes[0], 214 FinalScore: -1, 215 }, 216 }, 217 threshold: -1, 218 limit: 2, 219 maxSkip: 2, 220 }, 221 { 222 desc: "low scoring nodes interspersed", 223 nodes: []*RankedNode{ 224 { 225 Node: nodes[0], 226 FinalScore: -1, 227 }, 228 { 229 Node: nodes[1], 230 FinalScore: 5, 231 }, 232 { 233 Node: nodes[2], 234 FinalScore: -2, 235 }, 236 { 237 Node: nodes[3], 238 FinalScore: 2, 239 }, 240 }, 241 expectedOut: []*RankedNode{ 242 { 243 Node: nodes[1], 244 FinalScore: 5, 245 }, 246 { 247 Node: nodes[3], 248 FinalScore: 2, 249 }, 250 }, 251 threshold: -1, 252 limit: 2, 253 maxSkip: 2, 254 }, 255 { 256 desc: "only one node, score below threshold", 257 nodes: []*RankedNode{ 258 { 259 Node: nodes[0], 260 FinalScore: -1, 261 }, 262 }, 263 expectedOut: []*RankedNode{ 264 { 265 Node: nodes[0], 266 FinalScore: -1, 267 }, 268 }, 269 threshold: -1, 270 limit: 2, 271 maxSkip: 2, 272 }, 273 { 274 desc: "maxSkip is more than available nodes", 275 nodes: []*RankedNode{ 276 { 277 Node: nodes[0], 278 FinalScore: -2, 279 }, 280 { 281 Node: nodes[1], 282 FinalScore: 1, 283 }, 284 }, 285 expectedOut: []*RankedNode{ 286 { 287 Node: nodes[1], 288 FinalScore: 1, 289 }, 290 { 291 Node: nodes[0], 292 FinalScore: -2, 293 }, 294 }, 295 threshold: -1, 296 limit: 2, 297 maxSkip: 10, 298 }, 299 } 300 301 for _, tc := range testCases { 302 t.Run(tc.desc, func(t *testing.T) { 303 static := NewStaticRankIterator(ctx, tc.nodes) 304 305 limit := NewLimitIterator(ctx, static, 1, 0, 2) 306 limit.SetLimit(2) 307 out := collectRanked(limit) 308 require := require.New(t) 309 require.Equal(tc.expectedOut, out) 310 311 limit.Reset() 312 require.Equal(0, limit.skippedNodeIndex) 313 require.Equal(0, len(limit.skippedNodes)) 314 }) 315 } 316 317 } 318 319 func TestMaxScoreIterator(t *testing.T) { 320 _, ctx := testContext(t) 321 nodes := []*RankedNode{ 322 { 323 Node: mock.Node(), 324 FinalScore: 1, 325 }, 326 { 327 Node: mock.Node(), 328 FinalScore: 2, 329 }, 330 { 331 Node: mock.Node(), 332 FinalScore: 3, 333 }, 334 } 335 static := NewStaticRankIterator(ctx, nodes) 336 337 max := NewMaxScoreIterator(ctx, static) 338 339 out := collectRanked(max) 340 if len(out) != 1 { 341 t.Fatalf("bad: %v", out) 342 } 343 if out[0] != nodes[2] { 344 t.Fatalf("bad: %v", out) 345 } 346 347 out = collectRanked(max) 348 if len(out) != 0 { 349 t.Fatalf("bad: %v", out) 350 } 351 max.Reset() 352 353 out = collectRanked(max) 354 if len(out) != 1 { 355 t.Fatalf("bad: %v", out) 356 } 357 if out[0] != nodes[2] { 358 t.Fatalf("bad: %v", out) 359 } 360 }