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