| // Copyright 2012 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package syscall_test | |
| import ( | |
| "fmt" | |
| "internal/testenv" | |
| "os" | |
| "os/exec" | |
| "path/filepath" | |
| "strings" | |
| "syscall" | |
| "testing" | |
| ) | |
| func TestOpen(t *testing.T) { | |
| t.Parallel() | |
| dir := t.TempDir() | |
| file := filepath.Join(dir, "a") | |
| f, err := os.Create(file) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| f.Close() | |
| tests := []struct { | |
| path string | |
| flag int | |
| err error | |
| }{ | |
| {dir, syscall.O_RDONLY, nil}, | |
| {dir, syscall.O_CREAT, nil}, | |
| {dir, syscall.O_RDONLY | syscall.O_CREAT, nil}, | |
| {file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE, nil}, | |
| {file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE | os.O_TRUNC, nil}, | |
| {file, syscall.O_WRONLY | syscall.O_RDWR, nil}, | |
| {dir, syscall.O_WRONLY | syscall.O_RDWR, nil}, | |
| {dir, syscall.O_RDONLY | syscall.O_TRUNC, syscall.ERROR_ACCESS_DENIED}, | |
| {dir, syscall.O_WRONLY, syscall.EISDIR}, | |
| {dir, syscall.O_RDWR, syscall.EISDIR}, | |
| } | |
| for i, tt := range tests { | |
| h, err := syscall.Open(tt.path, tt.flag, 0o660) | |
| if err == nil { | |
| syscall.CloseHandle(h) | |
| } | |
| if err != tt.err { | |
| t.Errorf("%d: Open got %q, want %q", i, err, tt.err) | |
| } | |
| } | |
| } | |
| func TestComputerName(t *testing.T) { | |
| name, err := syscall.ComputerName() | |
| if err != nil { | |
| t.Fatalf("ComputerName failed: %v", err) | |
| } | |
| if len(name) == 0 { | |
| t.Error("ComputerName returned empty string") | |
| } | |
| } | |
| func TestWin32finddata(t *testing.T) { | |
| dir := t.TempDir() | |
| path := filepath.Join(dir, "long_name.and_extension") | |
| f, err := os.Create(path) | |
| if err != nil { | |
| t.Fatalf("failed to create %v: %v", path, err) | |
| } | |
| f.Close() | |
| type X struct { | |
| fd syscall.Win32finddata | |
| got byte | |
| pad [10]byte // to protect ourselves | |
| } | |
| var want byte = 2 // it is unlikely to have this character in the filename | |
| x := X{got: want} | |
| pathp, _ := syscall.UTF16PtrFromString(path) | |
| h, err := syscall.FindFirstFile(pathp, &(x.fd)) | |
| if err != nil { | |
| t.Fatalf("FindFirstFile failed: %v", err) | |
| } | |
| err = syscall.FindClose(h) | |
| if err != nil { | |
| t.Fatalf("FindClose failed: %v", err) | |
| } | |
| if x.got != want { | |
| t.Fatalf("memory corruption: want=%d got=%d", want, x.got) | |
| } | |
| } | |
| func abort(funcname string, err error) { | |
| panic(funcname + " failed: " + err.Error()) | |
| } | |
| func ExampleLoadLibrary() { | |
| h, err := syscall.LoadLibrary("kernel32.dll") | |
| if err != nil { | |
| abort("LoadLibrary", err) | |
| } | |
| defer syscall.FreeLibrary(h) | |
| proc, err := syscall.GetProcAddress(h, "GetVersion") | |
| if err != nil { | |
| abort("GetProcAddress", err) | |
| } | |
| r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0) | |
| major := byte(r) | |
| minor := uint8(r >> 8) | |
| build := uint16(r >> 16) | |
| print("windows version ", major, ".", minor, " (Build ", build, ")\n") | |
| } | |
| func TestTOKEN_ALL_ACCESS(t *testing.T) { | |
| if syscall.TOKEN_ALL_ACCESS != 0xF01FF { | |
| t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", syscall.TOKEN_ALL_ACCESS) | |
| } | |
| } | |
| func TestStdioAreInheritable(t *testing.T) { | |
| testenv.MustHaveGoBuild(t) | |
| testenv.MustHaveCGO(t) | |
| testenv.MustHaveExecPath(t, "gcc") | |
| tmpdir := t.TempDir() | |
| // build go dll | |
| const dlltext = ` | |
| package main | |
| import "C" | |
| import ( | |
| "fmt" | |
| ) | |
| //export HelloWorld | |
| func HelloWorld() { | |
| fmt.Println("Hello World") | |
| } | |
| func main() {} | |
| ` | |
| dllsrc := filepath.Join(tmpdir, "helloworld.go") | |
| err := os.WriteFile(dllsrc, []byte(dlltext), 0644) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| dll := filepath.Join(tmpdir, "helloworld.dll") | |
| cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", dllsrc) | |
| out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() | |
| if err != nil { | |
| t.Fatalf("failed to build go library: %s\n%s", err, out) | |
| } | |
| // build c exe | |
| const exetext = ` | |
| #include <stdlib.h> | |
| #include <windows.h> | |
| int main(int argc, char *argv[]) | |
| { | |
| system("hostname"); | |
| ((void(*)(void))GetProcAddress(LoadLibraryA(%q), "HelloWorld"))(); | |
| system("hostname"); | |
| return 0; | |
| } | |
| ` | |
| exe := filepath.Join(tmpdir, "helloworld.exe") | |
| cmd = exec.Command("gcc", "-o", exe, "-xc", "-") | |
| cmd.Stdin = strings.NewReader(fmt.Sprintf(exetext, dll)) | |
| out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() | |
| if err != nil { | |
| t.Fatalf("failed to build c executable: %s\n%s", err, out) | |
| } | |
| out, err = exec.Command(exe).Output() | |
| if err != nil { | |
| t.Fatalf("c program execution failed: %v: %v", err, string(out)) | |
| } | |
| hostname, err := os.Hostname() | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| have := strings.ReplaceAll(string(out), "\n", "") | |
| have = strings.ReplaceAll(have, "\r", "") | |
| want := fmt.Sprintf("%sHello World%s", hostname, hostname) | |
| if have != want { | |
| t.Fatalf("c program output is wrong: got %q, want %q", have, want) | |
| } | |
| } | |
| func TestGetwd_DoesNotPanicWhenPathIsLong(t *testing.T) { | |
| // Regression test for https://github.com/golang/go/issues/60051. | |
| tmp := t.TempDir() | |
| t.Chdir(tmp) | |
| // The length of a filename is also limited, so we can't reproduce the | |
| // crash by creating a single directory with a very long name; we need two | |
| // layers. | |
| a200 := strings.Repeat("a", 200) | |
| dirname := filepath.Join(tmp, a200, a200) | |
| err := os.MkdirAll(dirname, 0o700) | |
| if err != nil { | |
| t.Skipf("MkdirAll failed: %v", err) | |
| } | |
| err = os.Chdir(dirname) | |
| if err != nil { | |
| t.Skipf("Chdir failed: %v", err) | |
| } | |
| syscall.Getwd() | |
| } | |
| func TestGetStartupInfo(t *testing.T) { | |
| var si syscall.StartupInfo | |
| err := syscall.GetStartupInfo(&si) | |
| if err != nil { | |
| // see https://go.dev/issue/31316 | |
| t.Fatalf("GetStartupInfo: got error %v, want nil", err) | |
| } | |
| } | |
| func TestSyscallAllocations(t *testing.T) { | |
| testenv.SkipIfOptimizationOff(t) | |
| // Test that syscall.SyscallN arguments do not escape. | |
| // The function used (in this case GetVersion) doesn't matter | |
| // as long as it is always available and doesn't panic. | |
| h, err := syscall.LoadLibrary("kernel32.dll") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer syscall.FreeLibrary(h) | |
| proc, err := syscall.GetProcAddress(h, "GetVersion") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| testAllocs := func(t *testing.T, name string, fn func() error) { | |
| t.Run(name, func(t *testing.T) { | |
| n := int(testing.AllocsPerRun(10, func() { | |
| if err := fn(); err != nil { | |
| t.Fatalf("%s: %v", name, err) | |
| } | |
| })) | |
| if n > 0 { | |
| t.Errorf("allocs = %d, want 0", n) | |
| } | |
| }) | |
| } | |
| testAllocs(t, "SyscallN", func() error { | |
| r0, _, e1 := syscall.SyscallN(proc, 0, 0, 0) | |
| if r0 == 0 { | |
| return syscall.Errno(e1) | |
| } | |
| return nil | |
| }) | |
| testAllocs(t, "Syscall", func() error { | |
| r0, _, e1 := syscall.Syscall(proc, 3, 0, 0, 0) | |
| if r0 == 0 { | |
| return syscall.Errno(e1) | |
| } | |
| return nil | |
| }) | |
| } | |
| func FuzzUTF16FromString(f *testing.F) { | |
| f.Add("hi") // ASCII | |
| f.Add("รข") // latin1 | |
| f.Add("ใญใ") // plane 0 | |
| f.Add("๐") // extra Plane 0 | |
| f.Add("\x90") // invalid byte | |
| f.Add("\xe3\x81") // truncated | |
| f.Add("\xe3\xc1\x81") // invalid middle byte | |
| f.Fuzz(func(t *testing.T, tst string) { | |
| res, err := syscall.UTF16FromString(tst) | |
| if err != nil { | |
| if strings.Contains(tst, "\x00") { | |
| t.Skipf("input %q contains a NUL byte", tst) | |
| } | |
| t.Fatalf("UTF16FromString(%q): %v", tst, err) | |
| } | |
| t.Logf("UTF16FromString(%q) = %04x", tst, res) | |
| if len(res) < 1 || res[len(res)-1] != 0 { | |
| t.Fatalf("missing NUL terminator") | |
| } | |
| if len(res) > len(tst)+1 { | |
| t.Fatalf("len(%04x) > len(%q)+1", res, tst) | |
| } | |
| }) | |
| } | |
| func BenchmarkErrnoString(b *testing.B) { | |
| b.ReportAllocs() | |
| for b.Loop() { | |
| _ = syscall.Errno(2).Error() | |
| } | |
| } |