sequence_test.go 8.1 KB

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