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.
		
		
		
		
		
			
		
			
				
					
					
						
							448 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							448 lines
						
					
					
						
							12 KiB
						
					
					
				
								package fuse_test
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"bytes"
							 | 
						|
									"crypto/rand"
							 | 
						|
									"fmt"
							 | 
						|
									"os"
							 | 
						|
									"path/filepath"
							 | 
						|
									"sync"
							 | 
						|
									"testing"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/stretchr/testify/assert"
							 | 
						|
									"github.com/stretchr/testify/require"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// TestConcurrentFileOperations tests concurrent file operations
							 | 
						|
								func TestConcurrentFileOperations(t *testing.T) {
							 | 
						|
									framework := NewFuseTestFramework(t, DefaultTestConfig())
							 | 
						|
									defer framework.Cleanup()
							 | 
						|
								
							 | 
						|
									require.NoError(t, framework.Setup(DefaultTestConfig()))
							 | 
						|
								
							 | 
						|
									t.Run("ConcurrentFileWrites", func(t *testing.T) {
							 | 
						|
										testConcurrentFileWrites(t, framework)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("ConcurrentFileReads", func(t *testing.T) {
							 | 
						|
										testConcurrentFileReads(t, framework)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("ConcurrentReadWrite", func(t *testing.T) {
							 | 
						|
										testConcurrentReadWrite(t, framework)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("ConcurrentDirectoryOperations", func(t *testing.T) {
							 | 
						|
										testConcurrentDirectoryOperations(t, framework)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("ConcurrentFileCreation", func(t *testing.T) {
							 | 
						|
										testConcurrentFileCreation(t, framework)
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// testConcurrentFileWrites tests multiple goroutines writing to different files
							 | 
						|
								func testConcurrentFileWrites(t *testing.T, framework *FuseTestFramework) {
							 | 
						|
									numWorkers := 10
							 | 
						|
									filesPerWorker := 5
							 | 
						|
									var wg sync.WaitGroup
							 | 
						|
									var mutex sync.Mutex
							 | 
						|
									errors := make([]error, 0)
							 | 
						|
								
							 | 
						|
									// Function to collect errors safely
							 | 
						|
									addError := func(err error) {
							 | 
						|
										mutex.Lock()
							 | 
						|
										defer mutex.Unlock()
							 | 
						|
										errors = append(errors, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Start concurrent workers
							 | 
						|
									for worker := 0; worker < numWorkers; worker++ {
							 | 
						|
										wg.Add(1)
							 | 
						|
										go func(workerID int) {
							 | 
						|
											defer wg.Done()
							 | 
						|
								
							 | 
						|
											for file := 0; file < filesPerWorker; file++ {
							 | 
						|
												filename := fmt.Sprintf("worker_%d_file_%d.txt", workerID, file)
							 | 
						|
												content := []byte(fmt.Sprintf("Worker %d, File %d - %s", workerID, file, time.Now().String()))
							 | 
						|
								
							 | 
						|
												mountPath := filepath.Join(framework.GetMountPoint(), filename)
							 | 
						|
												if err := os.WriteFile(mountPath, content, 0644); err != nil {
							 | 
						|
													addError(fmt.Errorf("worker %d file %d: %v", workerID, file, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												// Verify file was written correctly
							 | 
						|
												readContent, err := os.ReadFile(mountPath)
							 | 
						|
												if err != nil {
							 | 
						|
													addError(fmt.Errorf("worker %d file %d read: %v", workerID, file, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												if !bytes.Equal(content, readContent) {
							 | 
						|
													addError(fmt.Errorf("worker %d file %d: content mismatch", workerID, file))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}(worker)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									wg.Wait()
							 | 
						|
								
							 | 
						|
									// Check for errors
							 | 
						|
									require.Empty(t, errors, "Concurrent writes failed: %v", errors)
							 | 
						|
								
							 | 
						|
									// Verify all files exist and have correct content
							 | 
						|
									for worker := 0; worker < numWorkers; worker++ {
							 | 
						|
										for file := 0; file < filesPerWorker; file++ {
							 | 
						|
											filename := fmt.Sprintf("worker_%d_file_%d.txt", worker, file)
							 | 
						|
											framework.AssertFileExists(filename)
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// testConcurrentFileReads tests multiple goroutines reading from the same file
							 | 
						|
								func testConcurrentFileReads(t *testing.T, framework *FuseTestFramework) {
							 | 
						|
									// Create a test file
							 | 
						|
									filename := "concurrent_read_test.txt"
							 | 
						|
									testData := make([]byte, 1024*1024) // 1MB
							 | 
						|
									_, err := rand.Read(testData)
							 | 
						|
									require.NoError(t, err)
							 | 
						|
								
							 | 
						|
									framework.CreateTestFile(filename, testData)
							 | 
						|
								
							 | 
						|
									numReaders := 20
							 | 
						|
									var wg sync.WaitGroup
							 | 
						|
									var mutex sync.Mutex
							 | 
						|
									errors := make([]error, 0)
							 | 
						|
								
							 | 
						|
									addError := func(err error) {
							 | 
						|
										mutex.Lock()
							 | 
						|
										defer mutex.Unlock()
							 | 
						|
										errors = append(errors, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Start concurrent readers
							 | 
						|
									for reader := 0; reader < numReaders; reader++ {
							 | 
						|
										wg.Add(1)
							 | 
						|
										go func(readerID int) {
							 | 
						|
											defer wg.Done()
							 | 
						|
								
							 | 
						|
											mountPath := filepath.Join(framework.GetMountPoint(), filename)
							 | 
						|
								
							 | 
						|
											// Read multiple times
							 | 
						|
											for i := 0; i < 3; i++ {
							 | 
						|
												readData, err := os.ReadFile(mountPath)
							 | 
						|
												if err != nil {
							 | 
						|
													addError(fmt.Errorf("reader %d iteration %d: %v", readerID, i, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												if !bytes.Equal(testData, readData) {
							 | 
						|
													addError(fmt.Errorf("reader %d iteration %d: data mismatch", readerID, i))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}(reader)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									wg.Wait()
							 | 
						|
									require.Empty(t, errors, "Concurrent reads failed: %v", errors)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// testConcurrentReadWrite tests simultaneous read and write operations
							 | 
						|
								func testConcurrentReadWrite(t *testing.T, framework *FuseTestFramework) {
							 | 
						|
									filename := "concurrent_rw_test.txt"
							 | 
						|
									initialData := bytes.Repeat([]byte("INITIAL"), 1000)
							 | 
						|
									framework.CreateTestFile(filename, initialData)
							 | 
						|
								
							 | 
						|
									var wg sync.WaitGroup
							 | 
						|
									var mutex sync.Mutex
							 | 
						|
									errors := make([]error, 0)
							 | 
						|
								
							 | 
						|
									addError := func(err error) {
							 | 
						|
										mutex.Lock()
							 | 
						|
										defer mutex.Unlock()
							 | 
						|
										errors = append(errors, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									mountPath := filepath.Join(framework.GetMountPoint(), filename)
							 | 
						|
								
							 | 
						|
									// Start readers
							 | 
						|
									numReaders := 5
							 | 
						|
									for i := 0; i < numReaders; i++ {
							 | 
						|
										wg.Add(1)
							 | 
						|
										go func(readerID int) {
							 | 
						|
											defer wg.Done()
							 | 
						|
								
							 | 
						|
											for j := 0; j < 10; j++ {
							 | 
						|
												_, err := os.ReadFile(mountPath)
							 | 
						|
												if err != nil {
							 | 
						|
													addError(fmt.Errorf("reader %d: %v", readerID, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
												time.Sleep(10 * time.Millisecond)
							 | 
						|
											}
							 | 
						|
										}(i)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Start writers
							 | 
						|
									numWriters := 2
							 | 
						|
									for i := 0; i < numWriters; i++ {
							 | 
						|
										wg.Add(1)
							 | 
						|
										go func(writerID int) {
							 | 
						|
											defer wg.Done()
							 | 
						|
								
							 | 
						|
											for j := 0; j < 5; j++ {
							 | 
						|
												newData := bytes.Repeat([]byte(fmt.Sprintf("WRITER%d", writerID)), 1000)
							 | 
						|
												err := os.WriteFile(mountPath, newData, 0644)
							 | 
						|
												if err != nil {
							 | 
						|
													addError(fmt.Errorf("writer %d: %v", writerID, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
												time.Sleep(50 * time.Millisecond)
							 | 
						|
											}
							 | 
						|
										}(i)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									wg.Wait()
							 | 
						|
									require.Empty(t, errors, "Concurrent read/write failed: %v", errors)
							 | 
						|
								
							 | 
						|
									// Verify file still exists and is readable
							 | 
						|
									framework.AssertFileExists(filename)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// testConcurrentDirectoryOperations tests concurrent directory operations
							 | 
						|
								func testConcurrentDirectoryOperations(t *testing.T, framework *FuseTestFramework) {
							 | 
						|
									numWorkers := 8
							 | 
						|
									var wg sync.WaitGroup
							 | 
						|
									var mutex sync.Mutex
							 | 
						|
									errors := make([]error, 0)
							 | 
						|
								
							 | 
						|
									addError := func(err error) {
							 | 
						|
										mutex.Lock()
							 | 
						|
										defer mutex.Unlock()
							 | 
						|
										errors = append(errors, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Each worker creates a directory tree
							 | 
						|
									for worker := 0; worker < numWorkers; worker++ {
							 | 
						|
										wg.Add(1)
							 | 
						|
										go func(workerID int) {
							 | 
						|
											defer wg.Done()
							 | 
						|
								
							 | 
						|
											// Create worker directory
							 | 
						|
											workerDir := fmt.Sprintf("worker_%d", workerID)
							 | 
						|
											mountPath := filepath.Join(framework.GetMountPoint(), workerDir)
							 | 
						|
								
							 | 
						|
											if err := os.Mkdir(mountPath, 0755); err != nil {
							 | 
						|
												addError(fmt.Errorf("worker %d mkdir: %v", workerID, err))
							 | 
						|
												return
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// Create subdirectories and files
							 | 
						|
											for i := 0; i < 5; i++ {
							 | 
						|
												subDir := filepath.Join(mountPath, fmt.Sprintf("subdir_%d", i))
							 | 
						|
												if err := os.Mkdir(subDir, 0755); err != nil {
							 | 
						|
													addError(fmt.Errorf("worker %d subdir %d: %v", workerID, i, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												// Create file in subdirectory
							 | 
						|
												testFile := filepath.Join(subDir, "test.txt")
							 | 
						|
												content := []byte(fmt.Sprintf("Worker %d, Subdir %d", workerID, i))
							 | 
						|
												if err := os.WriteFile(testFile, content, 0644); err != nil {
							 | 
						|
													addError(fmt.Errorf("worker %d file %d: %v", workerID, i, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}(worker)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									wg.Wait()
							 | 
						|
									require.Empty(t, errors, "Concurrent directory operations failed: %v", errors)
							 | 
						|
								
							 | 
						|
									// Verify all structures were created
							 | 
						|
									for worker := 0; worker < numWorkers; worker++ {
							 | 
						|
										workerDir := fmt.Sprintf("worker_%d", worker)
							 | 
						|
										mountPath := filepath.Join(framework.GetMountPoint(), workerDir)
							 | 
						|
								
							 | 
						|
										info, err := os.Stat(mountPath)
							 | 
						|
										require.NoError(t, err)
							 | 
						|
										assert.True(t, info.IsDir())
							 | 
						|
								
							 | 
						|
										// Check subdirectories
							 | 
						|
										for i := 0; i < 5; i++ {
							 | 
						|
											subDir := filepath.Join(mountPath, fmt.Sprintf("subdir_%d", i))
							 | 
						|
											info, err := os.Stat(subDir)
							 | 
						|
											require.NoError(t, err)
							 | 
						|
											assert.True(t, info.IsDir())
							 | 
						|
								
							 | 
						|
											testFile := filepath.Join(subDir, "test.txt")
							 | 
						|
											expectedContent := []byte(fmt.Sprintf("Worker %d, Subdir %d", worker, i))
							 | 
						|
											actualContent, err := os.ReadFile(testFile)
							 | 
						|
											require.NoError(t, err)
							 | 
						|
											assert.Equal(t, expectedContent, actualContent)
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// testConcurrentFileCreation tests concurrent creation of files in same directory
							 | 
						|
								func testConcurrentFileCreation(t *testing.T, framework *FuseTestFramework) {
							 | 
						|
									// Create test directory
							 | 
						|
									testDir := "concurrent_creation"
							 | 
						|
									framework.CreateTestDir(testDir)
							 | 
						|
								
							 | 
						|
									numWorkers := 15
							 | 
						|
									filesPerWorker := 10
							 | 
						|
									var wg sync.WaitGroup
							 | 
						|
									var mutex sync.Mutex
							 | 
						|
									errors := make([]error, 0)
							 | 
						|
									createdFiles := make(map[string]bool)
							 | 
						|
								
							 | 
						|
									addError := func(err error) {
							 | 
						|
										mutex.Lock()
							 | 
						|
										defer mutex.Unlock()
							 | 
						|
										errors = append(errors, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									addFile := func(filename string) {
							 | 
						|
										mutex.Lock()
							 | 
						|
										defer mutex.Unlock()
							 | 
						|
										createdFiles[filename] = true
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Create files concurrently
							 | 
						|
									for worker := 0; worker < numWorkers; worker++ {
							 | 
						|
										wg.Add(1)
							 | 
						|
										go func(workerID int) {
							 | 
						|
											defer wg.Done()
							 | 
						|
								
							 | 
						|
											for file := 0; file < filesPerWorker; file++ {
							 | 
						|
												filename := fmt.Sprintf("file_%d_%d.txt", workerID, file)
							 | 
						|
												relativePath := filepath.Join(testDir, filename)
							 | 
						|
												mountPath := filepath.Join(framework.GetMountPoint(), relativePath)
							 | 
						|
								
							 | 
						|
												content := []byte(fmt.Sprintf("Worker %d, File %d, Time: %s",
							 | 
						|
													workerID, file, time.Now().Format(time.RFC3339Nano)))
							 | 
						|
								
							 | 
						|
												if err := os.WriteFile(mountPath, content, 0644); err != nil {
							 | 
						|
													addError(fmt.Errorf("worker %d file %d: %v", workerID, file, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												addFile(filename)
							 | 
						|
											}
							 | 
						|
										}(worker)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									wg.Wait()
							 | 
						|
									require.Empty(t, errors, "Concurrent file creation failed: %v", errors)
							 | 
						|
								
							 | 
						|
									// Verify all files were created
							 | 
						|
									expectedCount := numWorkers * filesPerWorker
							 | 
						|
									assert.Equal(t, expectedCount, len(createdFiles))
							 | 
						|
								
							 | 
						|
									// Read directory and verify count
							 | 
						|
									mountPath := filepath.Join(framework.GetMountPoint(), testDir)
							 | 
						|
									entries, err := os.ReadDir(mountPath)
							 | 
						|
									require.NoError(t, err)
							 | 
						|
									assert.Equal(t, expectedCount, len(entries))
							 | 
						|
								
							 | 
						|
									// Verify each file exists and has content
							 | 
						|
									for filename := range createdFiles {
							 | 
						|
										relativePath := filepath.Join(testDir, filename)
							 | 
						|
										framework.AssertFileExists(relativePath)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestStressOperations tests high-load scenarios
							 | 
						|
								func TestStressOperations(t *testing.T) {
							 | 
						|
									framework := NewFuseTestFramework(t, DefaultTestConfig())
							 | 
						|
									defer framework.Cleanup()
							 | 
						|
								
							 | 
						|
									require.NoError(t, framework.Setup(DefaultTestConfig()))
							 | 
						|
								
							 | 
						|
									t.Run("HighFrequencySmallWrites", func(t *testing.T) {
							 | 
						|
										testHighFrequencySmallWrites(t, framework)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("ManySmallFiles", func(t *testing.T) {
							 | 
						|
										testManySmallFiles(t, framework)
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// testHighFrequencySmallWrites tests many small writes to the same file
							 | 
						|
								func testHighFrequencySmallWrites(t *testing.T, framework *FuseTestFramework) {
							 | 
						|
									filename := "high_freq_writes.txt"
							 | 
						|
									mountPath := filepath.Join(framework.GetMountPoint(), filename)
							 | 
						|
								
							 | 
						|
									// Open file for writing
							 | 
						|
									file, err := os.OpenFile(mountPath, os.O_CREATE|os.O_WRONLY, 0644)
							 | 
						|
									require.NoError(t, err)
							 | 
						|
									defer file.Close()
							 | 
						|
								
							 | 
						|
									// Perform many small writes
							 | 
						|
									numWrites := 1000
							 | 
						|
									writeSize := 100
							 | 
						|
								
							 | 
						|
									for i := 0; i < numWrites; i++ {
							 | 
						|
										data := []byte(fmt.Sprintf("Write %04d: %s\n", i, bytes.Repeat([]byte("x"), writeSize-20)))
							 | 
						|
										_, err := file.Write(data)
							 | 
						|
										require.NoError(t, err)
							 | 
						|
									}
							 | 
						|
									file.Close()
							 | 
						|
								
							 | 
						|
									// Verify file size
							 | 
						|
									info, err := os.Stat(mountPath)
							 | 
						|
									require.NoError(t, err)
							 | 
						|
									assert.Equal(t, totalSize, info.Size())
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// testManySmallFiles tests creating many small files
							 | 
						|
								func testManySmallFiles(t *testing.T, framework *FuseTestFramework) {
							 | 
						|
									testDir := "many_small_files"
							 | 
						|
									framework.CreateTestDir(testDir)
							 | 
						|
								
							 | 
						|
									numFiles := 500
							 | 
						|
									var wg sync.WaitGroup
							 | 
						|
									var mutex sync.Mutex
							 | 
						|
									errors := make([]error, 0)
							 | 
						|
								
							 | 
						|
									addError := func(err error) {
							 | 
						|
										mutex.Lock()
							 | 
						|
										defer mutex.Unlock()
							 | 
						|
										errors = append(errors, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Create files in batches
							 | 
						|
									batchSize := 50
							 | 
						|
									for batch := 0; batch < numFiles/batchSize; batch++ {
							 | 
						|
										wg.Add(1)
							 | 
						|
										go func(batchID int) {
							 | 
						|
											defer wg.Done()
							 | 
						|
								
							 | 
						|
											for i := 0; i < batchSize; i++ {
							 | 
						|
												fileNum := batchID*batchSize + i
							 | 
						|
												filename := filepath.Join(testDir, fmt.Sprintf("small_file_%04d.txt", fileNum))
							 | 
						|
												content := []byte(fmt.Sprintf("File %d content", fileNum))
							 | 
						|
								
							 | 
						|
												mountPath := filepath.Join(framework.GetMountPoint(), filename)
							 | 
						|
												if err := os.WriteFile(mountPath, content, 0644); err != nil {
							 | 
						|
													addError(fmt.Errorf("file %d: %v", fileNum, err))
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}(batch)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									wg.Wait()
							 | 
						|
									require.Empty(t, errors, "Many small files creation failed: %v", errors)
							 | 
						|
								
							 | 
						|
									// Verify directory listing
							 | 
						|
									mountPath := filepath.Join(framework.GetMountPoint(), testDir)
							 | 
						|
									entries, err := os.ReadDir(mountPath)
							 | 
						|
									require.NoError(t, err)
							 | 
						|
									assert.Equal(t, numFiles, len(entries))
							 | 
						|
								}
							 |