sorter_test.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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 http://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()
  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()
  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 LocalVersion 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. Modified: rand.Int63(),
  85. LocalVersion: rnd,
  86. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: uint64(rand.Int63())}}},
  87. Blocks: []protocol.BlockInfo{{
  88. Size: int32(rand.Intn(128 << 10)),
  89. Hash: []byte(rand.String(32)),
  90. }},
  91. }
  92. s.Append(f)
  93. }
  94. }
  95. // verifySorted checks that the files are returned sorted by LocalVersion.
  96. func verifySorted(t *testing.T, s IndexSorter, expected int) {
  97. prevLocalVer := int64(-1)
  98. seen := 0
  99. s.Sorted(func(f protocol.FileInfo) bool {
  100. if f.LocalVersion <= prevLocalVer {
  101. t.Fatalf("Unsorted LocalVer, %d <= %d", f.LocalVersion, prevLocalVer)
  102. }
  103. prevLocalVer = f.LocalVersion
  104. seen++
  105. return true
  106. })
  107. if seen != expected {
  108. t.Fatalf("expected %d files returned, got %d", expected, seen)
  109. }
  110. }
  111. // verifyBreak checks that the Sorter stops iteration once we return false.
  112. func verifyBreak(t *testing.T, s IndexSorter, expected int) {
  113. prevLocalVer := int64(-1)
  114. seen := 0
  115. s.Sorted(func(f protocol.FileInfo) bool {
  116. if f.LocalVersion <= prevLocalVer {
  117. t.Fatalf("Unsorted LocalVer, %d <= %d", f.LocalVersion, prevLocalVer)
  118. }
  119. if len(f.Blocks) != 1 {
  120. t.Fatalf("incorrect number of blocks %d != 1", len(f.Blocks))
  121. }
  122. if len(f.Version.Counters) != 1 {
  123. t.Fatalf("incorrect number of version counters %d != 1", len(f.Version.Counters))
  124. }
  125. prevLocalVer = f.LocalVersion
  126. seen++
  127. return seen < expected/2
  128. })
  129. if seen != expected/2 {
  130. t.Fatalf("expected %d files iterated over, got %d", expected, seen)
  131. }
  132. }