sequence_test.go 8.0 KB

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