github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/os_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package os_test
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	. "os"
    12  	"runtime"
    13  	"strings"
    14  	"syscall"
    15  	"testing"
    16  )
    17  
    18  // localTmp returns a local temporary directory not on NFS.
    19  func localTmp() string {
    20  	return TempDir()
    21  }
    22  
    23  func newFile(testName string, t *testing.T) (f *File) {
    24  	f, err := CreateTemp("", testName)
    25  	if err != nil {
    26  		t.Fatalf("newFile %s: CreateTemp fails with %s", testName, err)
    27  	}
    28  	return
    29  }
    30  
    31  // Read with length 0 should not return EOF.
    32  func TestRead0(t *testing.T) {
    33  	f := newFile("TestRead0", t)
    34  	defer Remove(f.Name())
    35  	defer f.Close()
    36  
    37  	const data = "hello, world\n"
    38  	io.WriteString(f, data)
    39  	f.Close()
    40  	f, err := Open(f.Name())
    41  	if err != nil {
    42  		t.Errorf("failed to reopen")
    43  	}
    44  
    45  	b := make([]byte, 0)
    46  	n, err := f.Read(b)
    47  	if n != 0 || err != nil {
    48  		t.Errorf("Read(0) = %d, %v, want 0, nil", n, err)
    49  	}
    50  	b = make([]byte, 5)
    51  	n, err = f.Read(b)
    52  	if n <= 0 || err != nil {
    53  		t.Errorf("Read(5) = %d, %v, want >0, nil", n, err)
    54  	}
    55  }
    56  
    57  // ReadAt with length 0 should not return EOF.
    58  func TestReadAt0(t *testing.T) {
    59  	if runtime.GOOS == "windows" {
    60  		t.Log("TODO: implement Pread for Windows")
    61  		return
    62  	}
    63  	f := newFile("TestReadAt0", t)
    64  	defer Remove(f.Name())
    65  	defer f.Close()
    66  
    67  	const data = "hello, world\n"
    68  	io.WriteString(f, data)
    69  
    70  	b := make([]byte, 0)
    71  	n, err := f.ReadAt(b, 0)
    72  	if n != 0 || err != nil {
    73  		t.Errorf("ReadAt(0,0) = %d, %v, want 0, nil", n, err)
    74  	}
    75  	b = make([]byte, 5)
    76  	n, err = f.ReadAt(b, 0)
    77  	if n <= 0 || err != nil {
    78  		t.Errorf("ReadAt(5,0) = %d, %v, want >0, nil", n, err)
    79  	}
    80  }
    81  
    82  func checkMode(t *testing.T, path string, mode FileMode) {
    83  	dir, err := Stat(path)
    84  	if err != nil {
    85  		t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
    86  	}
    87  	if dir.Mode()&ModePerm != mode {
    88  		t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
    89  	}
    90  }
    91  
    92  func TestSeek(t *testing.T) {
    93  	if runtime.GOARCH == "386" || runtime.GOARCH == "arm" {
    94  		t.Log("TODO: implement seek for 386 and arm")
    95  		return
    96  	}
    97  	f := newFile("TestSeek", t)
    98  	if f == nil {
    99  		t.Fatalf("f is nil")
   100  		return // TODO: remove
   101  	}
   102  	defer Remove(f.Name())
   103  	defer f.Close()
   104  
   105  	const data = "hello, world\n"
   106  	io.WriteString(f, data)
   107  
   108  	type test struct {
   109  		in     int64
   110  		whence int
   111  		out    int64
   112  	}
   113  	var tests = []test{
   114  		{0, io.SeekCurrent, int64(len(data))},
   115  		{0, io.SeekStart, 0},
   116  		{5, io.SeekStart, 5},
   117  		{0, io.SeekEnd, int64(len(data))},
   118  		{0, io.SeekStart, 0},
   119  		{-1, io.SeekEnd, int64(len(data)) - 1},
   120  		{1 << 33, io.SeekStart, 1 << 33},
   121  		{1 << 33, io.SeekEnd, 1<<33 + int64(len(data))},
   122  
   123  		// Issue 21681, Windows 4G-1, etc:
   124  		{1<<32 - 1, io.SeekStart, 1<<32 - 1},
   125  		{0, io.SeekCurrent, 1<<32 - 1},
   126  		{2<<32 - 1, io.SeekStart, 2<<32 - 1},
   127  		{0, io.SeekCurrent, 2<<32 - 1},
   128  	}
   129  	for i, tt := range tests {
   130  		off, err := f.Seek(tt.in, tt.whence)
   131  		if off != tt.out || err != nil {
   132  			if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" {
   133  				mounts, _ := os.ReadFile("/proc/mounts")
   134  				if strings.Contains(string(mounts), "reiserfs") {
   135  					// Reiserfs rejects the big seeks.
   136  					t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91")
   137  				}
   138  			}
   139  			t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
   140  		}
   141  	}
   142  }
   143  
   144  func TestReadAt(t *testing.T) {
   145  	if runtime.GOOS == "windows" {
   146  		t.Log("TODO: implement Pread for Windows")
   147  		return
   148  	}
   149  	f := newFile("TestReadAt", t)
   150  	defer Remove(f.Name())
   151  	defer f.Close()
   152  
   153  	const data = "hello, world\n"
   154  	io.WriteString(f, data)
   155  
   156  	b := make([]byte, 5)
   157  	n, err := f.ReadAt(b, 7)
   158  	if err != nil || n != len(b) {
   159  		t.Fatalf("ReadAt 7: %d, %v", n, err)
   160  	}
   161  	if string(b) != "world" {
   162  		t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
   163  	}
   164  }
   165  
   166  // Verify that ReadAt doesn't affect seek offset.
   167  // In the Plan 9 kernel, there used to be a bug in the implementation of
   168  // the pread syscall, where the channel offset was erroneously updated after
   169  // calling pread on a file.
   170  func TestReadAtOffset(t *testing.T) {
   171  	if runtime.GOOS == "windows" {
   172  		t.Log("TODO: implement Pread for Windows")
   173  		return
   174  	}
   175  	f := newFile("TestReadAtOffset", t)
   176  	defer Remove(f.Name())
   177  	defer f.Close()
   178  
   179  	const data = "hello, world\n"
   180  	io.WriteString(f, data)
   181  	f.Close()
   182  	f, err := Open(f.Name())
   183  	if err != nil {
   184  		t.Errorf("failed to reopen")
   185  	}
   186  
   187  	b := make([]byte, 5)
   188  
   189  	n, err := f.ReadAt(b, 7)
   190  	if err != nil || n != len(b) {
   191  		t.Fatalf("ReadAt 7: %d, %v", n, err)
   192  	}
   193  	if string(b) != "world" {
   194  		t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
   195  	}
   196  
   197  	n, err = f.Read(b)
   198  	if err != nil || n != len(b) {
   199  		t.Fatalf("Read: %d, %v", n, err)
   200  	}
   201  	if string(b) != "hello" {
   202  		t.Fatalf("Read: have %q want %q", string(b), "hello")
   203  	}
   204  }
   205  
   206  // Verify that ReadAt doesn't allow negative offset.
   207  func TestReadAtNegativeOffset(t *testing.T) {
   208  	if runtime.GOOS == "windows" {
   209  		t.Log("TODO: implement Pread for Windows")
   210  		return
   211  	}
   212  	f := newFile("TestReadAtNegativeOffset", t)
   213  	defer Remove(f.Name())
   214  	defer f.Close()
   215  
   216  	const data = "hello, world\n"
   217  	io.WriteString(f, data)
   218  	f.Close()
   219  	f, err := Open(f.Name())
   220  	if err != nil {
   221  		t.Errorf("failed to reopen")
   222  	}
   223  
   224  	b := make([]byte, 5)
   225  
   226  	n, err := f.ReadAt(b, -10)
   227  
   228  	const wantsub = "negative offset"
   229  	if !strings.Contains(err.Error(), wantsub) || n != 0 {
   230  		t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
   231  	}
   232  }
   233  
   234  func TestReadAtEOF(t *testing.T) {
   235  	if runtime.GOOS == "windows" {
   236  		t.Log("TODO: implement Pread for Windows")
   237  		return
   238  	}
   239  	f := newFile("TestReadAtEOF", t)
   240  	defer Remove(f.Name())
   241  	defer f.Close()
   242  
   243  	_, err := f.ReadAt(make([]byte, 10), 0)
   244  	switch err {
   245  	case io.EOF:
   246  		// all good
   247  	case nil:
   248  		t.Fatalf("ReadAt succeeded")
   249  	default:
   250  		t.Fatalf("ReadAt failed: %s", err)
   251  	}
   252  }
   253  
   254  func TestWriteAt(t *testing.T) {
   255  	if runtime.GOOS == "windows" {
   256  		t.Log("TODO: implement Pwrite for Windows")
   257  		return
   258  	}
   259  	f := newFile("TestWriteAt", t)
   260  	defer Remove(f.Name())
   261  	defer f.Close()
   262  
   263  	const data = "hello, world\n"
   264  	io.WriteString(f, data)
   265  
   266  	n, err := f.WriteAt([]byte("WORLD"), 7)
   267  	if err != nil || n != 5 {
   268  		t.Fatalf("WriteAt 7: %d, %v", n, err)
   269  	}
   270  
   271  	b, err := os.ReadFile(f.Name())
   272  	if err != nil {
   273  		t.Fatalf("ReadFile %s: %v", f.Name(), err)
   274  	}
   275  	if string(b) != "hello, WORLD\n" {
   276  		t.Fatalf("after write: have %q want %q", string(b), "hello, WORLD\n")
   277  	}
   278  }
   279  
   280  // Verify that WriteAt doesn't allow negative offset.
   281  func TestWriteAtNegativeOffset(t *testing.T) {
   282  	if runtime.GOOS == "windows" {
   283  		t.Log("TODO: implement Pwrite for Windows")
   284  		return
   285  	}
   286  	f := newFile("TestWriteAtNegativeOffset", t)
   287  	defer Remove(f.Name())
   288  	defer f.Close()
   289  
   290  	n, err := f.WriteAt([]byte("WORLD"), -10)
   291  
   292  	const wantsub = "negative offset"
   293  	if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 {
   294  		t.Errorf("WriteAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
   295  	}
   296  }
   297  
   298  // Verify that WriteAt doesn't work in append mode.
   299  func TestWriteAtInAppendMode(t *testing.T) {
   300  	if runtime.GOOS == "windows" {
   301  		t.Log("TODO: implement Pwrite for Windows")
   302  		return
   303  	}
   304  	defer chtmpdir(t)()
   305  	f, err := OpenFile("write_at_in_append_mode.txt", O_APPEND|O_CREATE|O_WRONLY, 0666)
   306  	if err != nil {
   307  		t.Fatalf("OpenFile: %v", err)
   308  	}
   309  	defer f.Close()
   310  
   311  	_, err = f.WriteAt([]byte(""), 1)
   312  	if err != ErrWriteAtInAppendMode {
   313  		t.Fatalf("f.WriteAt returned %v, expected %v", err, ErrWriteAtInAppendMode)
   314  	}
   315  }