sorter_test.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // Copyright (C) 2016 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package model
  7. import (
  8. "fmt"
  9. "os"
  10. "testing"
  11. "github.com/syncthing/syncthing/lib/protocol"
  12. "github.com/syncthing/syncthing/lib/rand"
  13. )
  14. func TestInMemoryIndexSorter(t *testing.T) {
  15. // An inMemorySorter should be able to absorb a few files in unsorted
  16. // order, and return them sorted.
  17. s := newInMemoryIndexSorter()
  18. addFiles(50, s)
  19. verifySorted(t, s, 50)
  20. verifyBreak(t, s, 50)
  21. s.Close()
  22. }
  23. func TestOnDiskIndexSorter(t *testing.T) {
  24. // An onDiskSorter should be able to absorb a few files in unsorted
  25. // order, and return them sorted.
  26. s := newOnDiskIndexSorter("testdata")
  27. addFiles(50, s)
  28. verifySorted(t, s, 50)
  29. verifyBreak(t, s, 50)
  30. // The temporary database should exist on disk. When Close()d, it should
  31. // be removed.
  32. info, err := os.Stat(s.dir)
  33. if err != nil {
  34. t.Fatal("temp database should exist on disk:", err)
  35. }
  36. if !info.IsDir() {
  37. t.Fatal("temp database should be a directory")
  38. }
  39. s.Close()
  40. _, err = os.Stat(s.dir)
  41. if !os.IsNotExist(err) {
  42. t.Fatal("temp database should have been removed")
  43. }
  44. }
  45. func TestIndexSorter(t *testing.T) {
  46. // An default IndexSorter should be able to absorb files, have them in
  47. // memory, and at some point switch to an on disk database.
  48. s := NewIndexSorter("testdata")
  49. defer s.Close()
  50. // We should start out as an in memory store.
  51. nFiles := 1
  52. addFiles(1, s)
  53. verifySorted(t, s, nFiles)
  54. as := s.(*autoSwitchingIndexSorter)
  55. if _, ok := as.internalIndexSorter.(*inMemoryIndexSorter); !ok {
  56. t.Fatalf("the sorter should be in memory after only one file")
  57. }
  58. // At some point, for sure with less than maxBytesInMemory files, we
  59. // should switch over to an on disk sorter.
  60. for i := 0; i < maxBytesInMemory; i++ {
  61. addFiles(1, s)
  62. nFiles++
  63. if _, ok := as.internalIndexSorter.(*onDiskIndexSorter); ok {
  64. break
  65. }
  66. }
  67. if _, ok := as.internalIndexSorter.(*onDiskIndexSorter); !ok {
  68. t.Fatalf("the sorter should be on disk after %d files", nFiles)
  69. }
  70. verifySorted(t, s, nFiles)
  71. // For test coverage, as some methods are called on the onDiskSorter
  72. // only after switching to it.
  73. addFiles(1, s)
  74. verifySorted(t, s, nFiles+1)
  75. }
  76. // addFiles adds files with random Sequence to the Sorter.
  77. func addFiles(n int, s IndexSorter) {
  78. for i := 0; i < n; i++ {
  79. rnd := rand.Int63()
  80. f := protocol.FileInfo{
  81. Name: fmt.Sprintf("file-%d", rnd),
  82. Size: rand.Int63(),
  83. Permissions: uint32(rand.Intn(0777)),
  84. ModifiedS: rand.Int63(),
  85. ModifiedNs: int32(rand.Int63()),
  86. Sequence: rnd,
  87. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: uint64(rand.Int63())}}},
  88. Blocks: []protocol.BlockInfo{{
  89. Size: int32(rand.Intn(128 << 10)),
  90. Hash: []byte(rand.String(32)),
  91. }},
  92. }
  93. s.Append(f)
  94. }
  95. }
  96. // verifySorted checks that the files are returned sorted by Sequence.
  97. func verifySorted(t *testing.T, s IndexSorter, expected int) {
  98. prevSequence := int64(-1)
  99. seen := 0
  100. s.Sorted(func(f protocol.FileInfo) bool {
  101. if f.Sequence <= prevSequence {
  102. t.Fatalf("Unsorted Sequence, %d <= %d", f.Sequence, prevSequence)
  103. }
  104. prevSequence = f.Sequence
  105. seen++
  106. return true
  107. })
  108. if seen != expected {
  109. t.Fatalf("expected %d files returned, got %d", expected, seen)
  110. }
  111. }
  112. // verifyBreak checks that the Sorter stops iteration once we return false.
  113. func verifyBreak(t *testing.T, s IndexSorter, expected int) {
  114. prevSequence := int64(-1)
  115. seen := 0
  116. s.Sorted(func(f protocol.FileInfo) bool {
  117. if f.Sequence <= prevSequence {
  118. t.Fatalf("Unsorted Sequence, %d <= %d", f.Sequence, prevSequence)
  119. }
  120. if len(f.Blocks) != 1 {
  121. t.Fatalf("incorrect number of blocks %d != 1", len(f.Blocks))
  122. }
  123. if len(f.Version.Counters) != 1 {
  124. t.Fatalf("incorrect number of version counters %d != 1", len(f.Version.Counters))
  125. }
  126. prevSequence = f.Sequence
  127. seen++
  128. return seen < expected/2
  129. })
  130. if seen != expected/2 {
  131. t.Fatalf("expected %d files iterated over, got %d", expected, seen)
  132. }
  133. }