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.

138 lines
4.5 KiB

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