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  }