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