You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.7 KiB

  1. package tempfile
  2. // Need access to internal variables, so can't use _test package
  3. import (
  4. "bytes"
  5. "fmt"
  6. "io/ioutil"
  7. mrand "math/rand"
  8. "os"
  9. testing "testing"
  10. "github.com/stretchr/testify/require"
  11. tmrand "github.com/tendermint/tendermint/libs/rand"
  12. )
  13. func TestWriteFileAtomic(t *testing.T) {
  14. var (
  15. data = []byte(tmrand.Str(mrand.Intn(2048)))
  16. old = tmrand.Bytes(mrand.Intn(2048))
  17. perm os.FileMode = 0600
  18. )
  19. f, err := ioutil.TempFile("/tmp", "write-atomic-test-")
  20. if err != nil {
  21. t.Fatal(err)
  22. }
  23. defer os.Remove(f.Name())
  24. if err = ioutil.WriteFile(f.Name(), old, 0600); err != nil {
  25. t.Fatal(err)
  26. }
  27. if err = WriteFileAtomic(f.Name(), data, perm); err != nil {
  28. t.Fatal(err)
  29. }
  30. rData, err := ioutil.ReadFile(f.Name())
  31. if err != nil {
  32. t.Fatal(err)
  33. }
  34. if !bytes.Equal(data, rData) {
  35. t.Fatalf("data mismatch: %v != %v", data, rData)
  36. }
  37. stat, err := os.Stat(f.Name())
  38. if err != nil {
  39. t.Fatal(err)
  40. }
  41. if have, want := stat.Mode().Perm(), perm; have != want {
  42. t.Errorf("have %v, want %v", have, want)
  43. }
  44. }
  45. // This tests atomic write file when there is a single duplicate file.
  46. // Expected behavior is for a new file to be created, and the original write file to be unaltered.
  47. func TestWriteFileAtomicDuplicateFile(t *testing.T) {
  48. var (
  49. defaultSeed uint64 = 1
  50. testString = "This is a glorious test string"
  51. expectedString = "Did the test file's string appear here?"
  52. fileToWrite = "/tmp/TestWriteFileAtomicDuplicateFile-test.txt"
  53. )
  54. // Create a file at the seed, and reset the seed.
  55. atomicWriteFileRand = defaultSeed
  56. firstFileRand := randWriteFileSuffix()
  57. atomicWriteFileRand = defaultSeed
  58. fname := "/tmp/" + atomicWriteFilePrefix + firstFileRand
  59. f, err := os.OpenFile(fname, atomicWriteFileFlag, 0777)
  60. defer os.Remove(fname)
  61. // Defer here, in case there is a panic in WriteFileAtomic.
  62. defer os.Remove(fileToWrite)
  63. require.NoError(t, err)
  64. _, err = f.WriteString(testString)
  65. require.NoError(t, err)
  66. err = WriteFileAtomic(fileToWrite, []byte(expectedString), 0777)
  67. require.NoError(t, err)
  68. // Check that the first atomic file was untouched
  69. firstAtomicFileBytes, err := ioutil.ReadFile(fname)
  70. require.NoError(t, err, "Error reading first atomic file")
  71. require.Equal(t, []byte(testString), firstAtomicFileBytes, "First atomic file was overwritten")
  72. // Check that the resultant file is correct
  73. resultantFileBytes, err := ioutil.ReadFile(fileToWrite)
  74. require.NoError(t, err, "Error reading resultant file")
  75. require.Equal(t, []byte(expectedString), resultantFileBytes, "Written file had incorrect bytes")
  76. // Check that the intermediate write file was deleted
  77. // Get the second write files' randomness
  78. atomicWriteFileRand = defaultSeed
  79. _ = randWriteFileSuffix()
  80. secondFileRand := randWriteFileSuffix()
  81. _, err = os.Stat("/tmp/" + atomicWriteFilePrefix + secondFileRand)
  82. require.True(t, os.IsNotExist(err), "Intermittent atomic write file not deleted")
  83. }
  84. // This tests atomic write file when there are many duplicate files.
  85. // Expected behavior is for a new file to be created under a completely new seed,
  86. // and the original write files to be unaltered.
  87. func TestWriteFileAtomicManyDuplicates(t *testing.T) {
  88. var (
  89. defaultSeed uint64 = 2
  90. testString = "This is a glorious test string, from file %d"
  91. expectedString = "Did any of the test file's string appear here?"
  92. fileToWrite = "/tmp/TestWriteFileAtomicDuplicateFile-test.txt"
  93. )
  94. // Initialize all of the atomic write files
  95. atomicWriteFileRand = defaultSeed
  96. for i := 0; i < atomicWriteFileMaxNumConflicts+2; i++ {
  97. fileRand := randWriteFileSuffix()
  98. fname := "/tmp/" + atomicWriteFilePrefix + fileRand
  99. f, err := os.OpenFile(fname, atomicWriteFileFlag, 0777)
  100. require.Nil(t, err)
  101. _, err = f.WriteString(fmt.Sprintf(testString, i))
  102. require.NoError(t, err)
  103. defer os.Remove(fname)
  104. }
  105. atomicWriteFileRand = defaultSeed
  106. // Defer here, in case there is a panic in WriteFileAtomic.
  107. defer os.Remove(fileToWrite)
  108. err := WriteFileAtomic(fileToWrite, []byte(expectedString), 0777)
  109. require.NoError(t, err)
  110. // Check that all intermittent atomic file were untouched
  111. atomicWriteFileRand = defaultSeed
  112. for i := 0; i < atomicWriteFileMaxNumConflicts+2; i++ {
  113. fileRand := randWriteFileSuffix()
  114. fname := "/tmp/" + atomicWriteFilePrefix + fileRand
  115. firstAtomicFileBytes, err := ioutil.ReadFile(fname)
  116. require.Nil(t, err, "Error reading first atomic file")
  117. require.Equal(t, []byte(fmt.Sprintf(testString, i)), firstAtomicFileBytes,
  118. "atomic write file %d was overwritten", i)
  119. }
  120. // Check that the resultant file is correct
  121. resultantFileBytes, err := ioutil.ReadFile(fileToWrite)
  122. require.Nil(t, err, "Error reading resultant file")
  123. require.Equal(t, []byte(expectedString), resultantFileBytes, "Written file had incorrect bytes")
  124. }