github.com/rawahars/moby@v24.0.4+incompatible/libnetwork/bitseq/sequence_test.go (about) 1 package bitseq 2 3 import ( 4 "fmt" 5 "math/rand" 6 "os" 7 "path/filepath" 8 "testing" 9 "time" 10 11 "github.com/docker/docker/libnetwork/datastore" 12 "github.com/docker/libkv/store" 13 "github.com/docker/libkv/store/boltdb" 14 ) 15 16 var ( 17 defaultPrefix = filepath.Join(os.TempDir(), "libnetwork", "test", "bitseq") 18 ) 19 20 func init() { 21 boltdb.Register() 22 } 23 24 func randomLocalStore() (datastore.DataStore, error) { 25 tmp, err := os.CreateTemp("", "libnetwork-") 26 if err != nil { 27 return nil, fmt.Errorf("Error creating temp file: %v", err) 28 } 29 if err := tmp.Close(); err != nil { 30 return nil, fmt.Errorf("Error closing temp file: %v", err) 31 } 32 return datastore.NewDataStore(datastore.ScopeCfg{ 33 Client: datastore.ScopeClientCfg{ 34 Provider: "boltdb", 35 Address: filepath.Join(defaultPrefix, filepath.Base(tmp.Name())), 36 Config: &store.Config{ 37 Bucket: "libnetwork", 38 ConnectionTimeout: 3 * time.Second, 39 }, 40 }, 41 }) 42 } 43 44 const blockLen = 32 45 46 // This one tests an allocation pattern which unveiled an issue in pushReservation 47 // Specifically a failure in detecting when we are in the (B) case (the bit to set 48 // belongs to the last block of the current sequence). Because of a bug, code 49 // was assuming the bit belonged to a block in the middle of the current sequence. 50 // Which in turn caused an incorrect allocation when requesting a bit which is not 51 // in the first or last sequence block. 52 func TestSetAnyInRange(t *testing.T) { 53 numBits := uint64(8 * blockLen) 54 hnd, err := NewHandle("", nil, "", numBits) 55 if err != nil { 56 t.Fatal(err) 57 } 58 59 if err := hnd.Set(0); err != nil { 60 t.Fatal(err) 61 } 62 63 if err := hnd.Set(255); err != nil { 64 t.Fatal(err) 65 } 66 67 o, err := hnd.SetAnyInRange(128, 255, false) 68 if err != nil { 69 t.Fatal(err) 70 } 71 if o != 128 { 72 t.Fatalf("Unexpected ordinal: %d", o) 73 } 74 75 o, err = hnd.SetAnyInRange(128, 255, false) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 if o != 129 { 81 t.Fatalf("Unexpected ordinal: %d", o) 82 } 83 84 o, err = hnd.SetAnyInRange(246, 255, false) 85 if err != nil { 86 t.Fatal(err) 87 } 88 if o != 246 { 89 t.Fatalf("Unexpected ordinal: %d", o) 90 } 91 92 o, err = hnd.SetAnyInRange(246, 255, false) 93 if err != nil { 94 t.Fatal(err) 95 } 96 if o != 247 { 97 t.Fatalf("Unexpected ordinal: %d", o) 98 } 99 } 100 101 func TestRandomAllocateDeallocate(t *testing.T) { 102 ds, err := randomLocalStore() 103 if err != nil { 104 t.Fatal(err) 105 } 106 107 numBits := int(16 * blockLen) 108 hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits)) 109 if err != nil { 110 t.Fatal(err) 111 } 112 defer func() { 113 if err := hnd.Destroy(); err != nil { 114 t.Fatal(err) 115 } 116 }() 117 118 seed := time.Now().Unix() 119 rng := rand.New(rand.NewSource(seed)) 120 121 // Allocate all bits using a random pattern 122 pattern := rng.Perm(numBits) 123 for _, bit := range pattern { 124 err := hnd.Set(uint64(bit)) 125 if err != nil { 126 t.Errorf("Unexpected failure on allocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd) 127 } 128 } 129 if unselected := hnd.Unselected(); unselected != 0 { 130 t.Errorf("Expected full sequence. Instead found %d free bits. Seed: %d.\n%s", unselected, seed, hnd) 131 } 132 133 // Deallocate all bits using a random pattern 134 pattern = rng.Perm(numBits) 135 for _, bit := range pattern { 136 err := hnd.Unset(uint64(bit)) 137 if err != nil { 138 t.Errorf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd) 139 } 140 } 141 if unselected := hnd.Unselected(); unselected != uint64(numBits) { 142 t.Errorf("Expected full sequence. Instead found %d free bits. Seed: %d.\n%s", unselected, seed, hnd) 143 } 144 } 145 146 func TestRetrieveFromStore(t *testing.T) { 147 ds, err := randomLocalStore() 148 if err != nil { 149 t.Fatal(err) 150 } 151 152 numBits := int(8 * blockLen) 153 hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits)) 154 if err != nil { 155 t.Fatal(err) 156 } 157 158 // Allocate first half of the bits 159 for i := 0; i < numBits/2; i++ { 160 _, err := hnd.SetAny(false) 161 if err != nil { 162 t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd) 163 } 164 } 165 hnd0 := hnd.String() 166 167 // Retrieve same handle 168 hnd, err = NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits)) 169 if err != nil { 170 t.Fatal(err) 171 } 172 hnd1 := hnd.String() 173 174 if hnd1 != hnd0 { 175 t.Fatalf("%v\n%v", hnd0, hnd1) 176 } 177 178 err = hnd.Destroy() 179 if err != nil { 180 t.Fatal(err) 181 } 182 } 183 184 func testSetRollover(t *testing.T, serial bool) { 185 ds, err := randomLocalStore() 186 if err != nil { 187 t.Fatal(err) 188 } 189 190 numBlocks := uint32(8) 191 numBits := int(numBlocks * blockLen) 192 hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits)) 193 if err != nil { 194 t.Fatal(err) 195 } 196 197 // Allocate first half of the bits 198 for i := 0; i < numBits/2; i++ { 199 _, err := hnd.SetAny(serial) 200 if err != nil { 201 t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd) 202 } 203 } 204 205 if unselected := hnd.Unselected(); unselected != uint64(numBits/2) { 206 t.Fatalf("Expected full sequence. Instead found %d free bits. %s", unselected, hnd) 207 } 208 209 seed := time.Now().Unix() 210 rng := rand.New(rand.NewSource(seed)) 211 212 // Deallocate half of the allocated bits following a random pattern 213 pattern := rng.Perm(numBits / 2) 214 for i := 0; i < numBits/4; i++ { 215 bit := pattern[i] 216 err := hnd.Unset(uint64(bit)) 217 if err != nil { 218 t.Fatalf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd) 219 } 220 } 221 if unselected := hnd.Unselected(); unselected != uint64(3*numBits/4) { 222 t.Fatalf("Unexpected free bits: found %d free bits.\nSeed: %d.\n%s", unselected, seed, hnd) 223 } 224 225 //request to allocate for remaining half of the bits 226 for i := 0; i < numBits/2; i++ { 227 _, err := hnd.SetAny(serial) 228 if err != nil { 229 t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd) 230 } 231 } 232 233 //At this point all the bits must be allocated except the randomly unallocated bits 234 //which were unallocated in the first half of the bit sequence 235 if unselected := hnd.Unselected(); unselected != uint64(numBits/4) { 236 t.Fatalf("Unexpected number of unselected bits %d, Expected %d", unselected, numBits/4) 237 } 238 239 for i := 0; i < numBits/4; i++ { 240 _, err := hnd.SetAny(serial) 241 if err != nil { 242 t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd) 243 } 244 } 245 //Now requesting to allocate the unallocated random bits (qurter of the number of bits) should 246 //leave no more bits that can be allocated. 247 if hnd.Unselected() != 0 { 248 t.Fatalf("Unexpected number of unselected bits %d, Expected %d", hnd.Unselected(), 0) 249 } 250 251 err = hnd.Destroy() 252 if err != nil { 253 t.Fatal(err) 254 } 255 } 256 257 func TestSetRollover(t *testing.T) { 258 testSetRollover(t, false) 259 } 260 261 func TestSetRolloverSerial(t *testing.T) { 262 testSetRollover(t, true) 263 } 264 265 func TestMarshalJSON(t *testing.T) { 266 const expectedID = "my-bitseq" 267 expected := []byte("hello libnetwork") 268 hnd, err := NewHandle("", nil, expectedID, uint64(len(expected)*8)) 269 if err != nil { 270 t.Fatal(err) 271 } 272 273 for i, c := range expected { 274 for j := 0; j < 8; j++ { 275 if c&(1<<j) == 0 { 276 continue 277 } 278 if err := hnd.Set(uint64(i*8 + j)); err != nil { 279 t.Fatal(err) 280 } 281 } 282 } 283 284 hstr := hnd.String() 285 t.Log(hstr) 286 marshaled, err := hnd.MarshalJSON() 287 if err != nil { 288 t.Fatalf("MarshalJSON() err = %v", err) 289 } 290 t.Logf("%s", marshaled) 291 292 // Serializations of hnd as would be marshaled by versions of the code 293 // found in the wild. We need to support unmarshaling old versions to 294 // maintain backwards compatibility with sequences persisted on disk. 295 const ( 296 goldenV0 = `{"id":"my-bitseq","sequence":"AAAAAAAAAIAAAAAAAAAAPRamNjYAAAAAAAAAAfYENpYAAAAAAAAAAUZ2pi4AAAAAAAAAAe72TtYAAAAAAAAAAQ=="}` 297 ) 298 299 if string(marshaled) != goldenV0 { 300 t.Errorf("MarshalJSON() output differs from golden. Please add a new golden case to this test.") 301 } 302 303 for _, tt := range []struct { 304 name string 305 data []byte 306 }{ 307 {name: "Live", data: marshaled}, 308 {name: "Golden-v0", data: []byte(goldenV0)}, 309 } { 310 tt := tt 311 t.Run("UnmarshalJSON="+tt.name, func(t *testing.T) { 312 hnd2, err := NewHandle("", nil, "", 0) 313 if err != nil { 314 t.Fatal(err) 315 } 316 if err := hnd2.UnmarshalJSON(tt.data); err != nil { 317 t.Errorf("UnmarshalJSON() err = %v", err) 318 } 319 320 h2str := hnd2.String() 321 t.Log(h2str) 322 if hstr != h2str { 323 t.Errorf("Unmarshaled a different bitseq: want %q, got %q", hstr, h2str) 324 } 325 }) 326 } 327 }