Source file src/syscall/exec_windows_test.go

     1  // Copyright 2020 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 syscall_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/testenv"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"slices"
    14  	"syscall"
    15  	"testing"
    16  	"time"
    17  )
    18  
    19  func TestEscapeArg(t *testing.T) {
    20  	var tests = []struct {
    21  		input, output string
    22  	}{
    23  		{``, `""`},
    24  		{`a`, `a`},
    25  		{` `, `" "`},
    26  		{`\`, `\`},
    27  		{`"`, `\"`},
    28  		{`\"`, `\\\"`},
    29  		{`\\"`, `\\\\\"`},
    30  		{`\\ `, `"\\ "`},
    31  		{` \\`, `" \\\\"`},
    32  		{`a `, `"a "`},
    33  		{`C:\`, `C:\`},
    34  		{`C:\Program Files (x32)\Common\`, `"C:\Program Files (x32)\Common\\"`},
    35  		{`C:\Users\Игорь\`, `C:\Users\Игорь\`},
    36  		{`Андрей\file`, `Андрей\file`},
    37  		{`C:\Windows\temp`, `C:\Windows\temp`},
    38  		{`c:\temp\newfile`, `c:\temp\newfile`},
    39  		{`\\?\C:\Windows`, `\\?\C:\Windows`},
    40  		{`\\?\`, `\\?\`},
    41  		{`\\.\C:\Windows\`, `\\.\C:\Windows\`},
    42  		{`\\server\share\file`, `\\server\share\file`},
    43  		{`\\newserver\tempshare\really.txt`, `\\newserver\tempshare\really.txt`},
    44  	}
    45  	for _, test := range tests {
    46  		if got := syscall.EscapeArg(test.input); got != test.output {
    47  			t.Errorf("EscapeArg(%#q) = %#q, want %#q", test.input, got, test.output)
    48  		}
    49  	}
    50  }
    51  
    52  func TestEnvBlockSorted(t *testing.T) {
    53  	tests := []struct {
    54  		env  []string
    55  		want []string
    56  	}{
    57  		{},
    58  		{
    59  			env:  []string{"A=1"},
    60  			want: []string{"A=1"},
    61  		},
    62  		{
    63  			env:  []string{"A=1", "B=2", "C=3"},
    64  			want: []string{"A=1", "B=2", "C=3"},
    65  		},
    66  		{
    67  			env:  []string{"C=3", "B=2", "A=1"},
    68  			want: []string{"A=1", "B=2", "C=3"},
    69  		},
    70  		{
    71  			env:  []string{"c=3", "B=2", "a=1"},
    72  			want: []string{"a=1", "B=2", "c=3"},
    73  		},
    74  	}
    75  	for _, tt := range tests {
    76  		got := syscall.EnvSorted(tt.env)
    77  		if !slices.Equal(got, tt.want) {
    78  			t.Errorf("EnvSorted(%q) = %q, want %q", tt.env, got, tt.want)
    79  		}
    80  	}
    81  }
    82  
    83  func TestChangingProcessParent(t *testing.T) {
    84  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "parent" {
    85  		// in parent process
    86  
    87  		// Parent does nothing. It is just used as a parent of a child process.
    88  		time.Sleep(time.Minute)
    89  		os.Exit(0)
    90  	}
    91  
    92  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "child" {
    93  		// in child process
    94  		dumpPath := os.Getenv("GO_WANT_HELPER_PROCESS_FILE")
    95  		if dumpPath == "" {
    96  			fmt.Fprintf(os.Stderr, "Dump file path cannot be blank.")
    97  			os.Exit(1)
    98  		}
    99  		err := os.WriteFile(dumpPath, []byte(fmt.Sprintf("%d", os.Getppid())), 0644)
   100  		if err != nil {
   101  			fmt.Fprintf(os.Stderr, "Error writing dump file: %v", err)
   102  			os.Exit(2)
   103  		}
   104  		os.Exit(0)
   105  	}
   106  
   107  	// run parent process
   108  
   109  	parent := exec.Command(testenv.Executable(t), "-test.run=^TestChangingProcessParent$")
   110  	parent.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=parent")
   111  	err := parent.Start()
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  	defer func() {
   116  		parent.Process.Kill()
   117  		parent.Wait()
   118  	}()
   119  
   120  	// run child process
   121  
   122  	const _PROCESS_CREATE_PROCESS = 0x0080
   123  	const _PROCESS_DUP_HANDLE = 0x0040
   124  	childDumpPath := filepath.Join(t.TempDir(), "ppid.txt")
   125  	ph, err := syscall.OpenProcess(_PROCESS_CREATE_PROCESS|_PROCESS_DUP_HANDLE|syscall.PROCESS_QUERY_INFORMATION,
   126  		false, uint32(parent.Process.Pid))
   127  	if err != nil {
   128  		t.Fatal(err)
   129  	}
   130  	defer syscall.CloseHandle(ph)
   131  
   132  	child := exec.Command(testenv.Executable(t), "-test.run=^TestChangingProcessParent$")
   133  	child.Env = append(os.Environ(),
   134  		"GO_WANT_HELPER_PROCESS=child",
   135  		"GO_WANT_HELPER_PROCESS_FILE="+childDumpPath)
   136  	child.SysProcAttr = &syscall.SysProcAttr{ParentProcess: ph}
   137  	childOutput, err := child.CombinedOutput()
   138  	if err != nil {
   139  		t.Errorf("child failed: %v: %v", err, string(childOutput))
   140  	}
   141  	childOutput, err = os.ReadFile(childDumpPath)
   142  	if err != nil {
   143  		t.Fatalf("reading child output failed: %v", err)
   144  	}
   145  	if got, want := string(childOutput), fmt.Sprintf("%d", parent.Process.Pid); got != want {
   146  		t.Fatalf("child output: want %q, got %q", want, got)
   147  	}
   148  }
   149  

View as plain text