]> Cypherpunks.ru repositories - gostls13.git/blob - misc/cgo/testcarchive/carchive_test.go
0f3432650a5e5285e9a2d353e64664232f0ae80b
[gostls13.git] / misc / cgo / testcarchive / carchive_test.go
1 // Copyright 2016 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 carchive_test
6
7 import (
8         "bufio"
9         "fmt"
10         "io/ioutil"
11         "os"
12         "os/exec"
13         "path/filepath"
14         "strings"
15         "syscall"
16         "testing"
17         "time"
18         "unicode"
19 )
20
21 // Program to run.
22 var bin []string
23
24 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
25 var cc []string
26
27 // An environment with GOPATH=$(pwd).
28 var gopathEnv []string
29
30 // ".exe" on Windows.
31 var exeSuffix string
32
33 var GOOS, GOARCH string
34 var libgodir string
35
36 func init() {
37         bin = []string{"./testp"}
38         GOOS = goEnv("GOOS")
39         GOARCH = goEnv("GOARCH")
40         execScript := "go_" + GOOS + "_" + GOARCH + "_exec"
41         if executor, err := exec.LookPath(execScript); err == nil {
42                 bin = []string{executor, "./testp"}
43         }
44
45         ccOut := goEnv("CC")
46         cc = []string{string(ccOut)}
47
48         out := goEnv("GOGCCFLAGS")
49         quote := '\000'
50         start := 0
51         lastSpace := true
52         backslash := false
53         s := string(out)
54         for i, c := range s {
55                 if quote == '\000' && unicode.IsSpace(c) {
56                         if !lastSpace {
57                                 cc = append(cc, s[start:i])
58                                 lastSpace = true
59                         }
60                 } else {
61                         if lastSpace {
62                                 start = i
63                                 lastSpace = false
64                         }
65                         if quote == '\000' && !backslash && (c == '"' || c == '\'') {
66                                 quote = c
67                                 backslash = false
68                         } else if !backslash && quote == c {
69                                 quote = '\000'
70                         } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
71                                 backslash = true
72                         } else {
73                                 backslash = false
74                         }
75                 }
76         }
77         if !lastSpace {
78                 cc = append(cc, s[start:])
79         }
80
81         if GOOS == "darwin" {
82                 cc = append(cc, "-Wl,-no_pie")
83
84                 // For Darwin/ARM.
85                 // TODO(crawshaw): can we do better?
86                 cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
87         }
88         libgodir = GOOS + "_" + GOARCH
89         if GOOS == "darwin" && GOARCH == "arm" {
90                 libgodir = GOOS + "_" + GOARCH + "_shared"
91         }
92         cc = append(cc, "-I", filepath.Join("pkg", libgodir))
93
94         // Build an environment with GOPATH=$(pwd)
95         env := os.Environ()
96         var n []string
97         for _, e := range env {
98                 if !strings.HasPrefix(e, "GOPATH=") {
99                         n = append(n, e)
100                 }
101         }
102         dir, err := os.Getwd()
103         if err != nil {
104                 fmt.Fprintln(os.Stderr, err)
105                 os.Exit(2)
106         }
107         n = append(n, "GOPATH="+dir)
108         gopathEnv = n
109
110         if GOOS == "windows" {
111                 exeSuffix = ".exe"
112         }
113 }
114
115 func goEnv(key string) string {
116         out, err := exec.Command("go", "env", key).Output()
117         if err != nil {
118                 fmt.Fprintf(os.Stderr, "go env %s failed:\n%s", key, err)
119                 fmt.Fprintf(os.Stderr, "%s", err.(*exec.ExitError).Stderr)
120                 os.Exit(2)
121         }
122         return strings.TrimSpace(string(out))
123 }
124
125 func compilemain(t *testing.T, libgo string) {
126         ccArgs := append(cc, "-o", "testp"+exeSuffix, "main.c")
127         if GOOS == "windows" {
128                 ccArgs = append(ccArgs, "main_windows.c", libgo, "-lntdll", "-lws2_32", "-lwinmm")
129         } else {
130                 ccArgs = append(ccArgs, "main_unix.c", libgo)
131         }
132         t.Log(ccArgs)
133
134         if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
135                 t.Logf("%s", out)
136                 t.Fatal(err)
137         }
138 }
139
140 func TestInstall(t *testing.T) {
141         defer func() {
142                 os.Remove("libgo.a")
143                 os.Remove("libgo.h")
144                 os.Remove("testp")
145                 os.RemoveAll("pkg")
146         }()
147
148         cmd := exec.Command("go", "install", "-buildmode=c-archive", "libgo")
149         cmd.Env = gopathEnv
150         if out, err := cmd.CombinedOutput(); err != nil {
151                 t.Logf("%s", out)
152                 t.Fatal(err)
153         }
154
155         compilemain(t, filepath.Join("pkg", libgodir, "libgo.a"))
156
157         binArgs := append(bin, "arg1", "arg2")
158         if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
159                 t.Logf("%s", out)
160                 t.Fatal(err)
161         }
162
163         os.Remove("libgo.a")
164         os.Remove("libgo.h")
165         os.Remove("testp")
166
167         // Test building libgo other than installing it.
168         // Header files are now present.
169         cmd = exec.Command("go", "build", "-buildmode=c-archive", filepath.Join("src", "libgo", "libgo.go"))
170         cmd.Env = gopathEnv
171         if out, err := cmd.CombinedOutput(); err != nil {
172                 t.Logf("%s", out)
173                 t.Fatal(err)
174         }
175
176         compilemain(t, "libgo.a")
177
178         if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
179                 t.Logf("%s", out)
180                 t.Fatal(err)
181         }
182
183         os.Remove("libgo.a")
184         os.Remove("libgo.h")
185         os.Remove("testp")
186
187         cmd = exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
188         cmd.Env = gopathEnv
189         if out, err := cmd.CombinedOutput(); err != nil {
190                 t.Logf("%s", out)
191                 t.Fatal(err)
192         }
193
194         compilemain(t, "libgo.a")
195
196         if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
197                 t.Logf("%s", out)
198                 t.Fatal(err)
199         }
200 }
201
202 func TestEarlySignalHandler(t *testing.T) {
203         switch GOOS {
204         case "darwin":
205                 switch GOARCH {
206                 case "arm", "arm64":
207                         t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
208                 }
209         case "windows":
210                 t.Skip("skipping signal test on Windows")
211         }
212
213         defer func() {
214                 os.Remove("libgo2.a")
215                 os.Remove("libgo2.h")
216                 os.Remove("testp")
217                 os.RemoveAll("pkg")
218         }()
219
220         cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
221         cmd.Env = gopathEnv
222         if out, err := cmd.CombinedOutput(); err != nil {
223                 t.Logf("%s", out)
224                 t.Fatal(err)
225         }
226
227         ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
228         if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
229                 t.Logf("%s", out)
230                 t.Fatal(err)
231         }
232
233         if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
234                 t.Logf("%s", out)
235                 t.Fatal(err)
236         }
237 }
238
239 func TestSignalForwarding(t *testing.T) {
240         switch GOOS {
241         case "darwin":
242                 switch GOARCH {
243                 case "arm", "arm64":
244                         t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
245                 }
246         case "windows":
247                 t.Skip("skipping signal test on Windows")
248         }
249
250         defer func() {
251                 os.Remove("libgo2.a")
252                 os.Remove("libgo2.h")
253                 os.Remove("testp")
254                 os.RemoveAll("pkg")
255         }()
256
257         cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
258         cmd.Env = gopathEnv
259         if out, err := cmd.CombinedOutput(); err != nil {
260                 t.Logf("%s", out)
261                 t.Fatal(err)
262         }
263
264         ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
265         if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
266                 t.Logf("%s", out)
267                 t.Fatal(err)
268         }
269
270         cmd = exec.Command(bin[0], append(bin[1:], "1")...)
271
272         out, err := cmd.CombinedOutput()
273
274         if err == nil {
275                 t.Logf("%s", out)
276                 t.Error("test program succeeded unexpectedly")
277         } else if ee, ok := err.(*exec.ExitError); !ok {
278                 t.Logf("%s", out)
279                 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
280         } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
281                 t.Logf("%s", out)
282                 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
283         } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV {
284                 t.Logf("%s", out)
285                 t.Errorf("got %v; expected SIGSEGV", ee)
286         }
287 }
288
289 func TestSignalForwardingExternal(t *testing.T) {
290         switch GOOS {
291         case "darwin":
292                 switch GOARCH {
293                 case "arm", "arm64":
294                         t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
295                 }
296         case "windows":
297                 t.Skip("skipping signal test on Windows")
298         }
299
300         defer func() {
301                 os.Remove("libgo2.a")
302                 os.Remove("libgo2.h")
303                 os.Remove("testp")
304                 os.RemoveAll("pkg")
305         }()
306
307         cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
308         cmd.Env = gopathEnv
309         if out, err := cmd.CombinedOutput(); err != nil {
310                 t.Logf("%s", out)
311                 t.Fatal(err)
312         }
313
314         ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
315         if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
316                 t.Logf("%s", out)
317                 t.Fatal(err)
318         }
319
320         // We want to send the process a signal and see if it dies.
321         // Normally the signal goes to the C thread, the Go signal
322         // handler picks it up, sees that it is running in a C thread,
323         // and the program dies. Unfortunately, occasionally the
324         // signal is delivered to a Go thread, which winds up
325         // discarding it because it was sent by another program and
326         // there is no Go handler for it. To avoid this, run the
327         // program several times in the hopes that it will eventually
328         // fail.
329         const tries = 20
330         for i := 0; i < tries; i++ {
331                 cmd = exec.Command(bin[0], append(bin[1:], "2")...)
332
333                 stderr, err := cmd.StderrPipe()
334                 if err != nil {
335                         t.Fatal(err)
336                 }
337                 defer stderr.Close()
338
339                 r := bufio.NewReader(stderr)
340
341                 err = cmd.Start()
342
343                 if err != nil {
344                         t.Fatal(err)
345                 }
346
347                 // Wait for trigger to ensure that the process is started.
348                 ok, err := r.ReadString('\n')
349
350                 // Verify trigger.
351                 if err != nil || ok != "OK\n" {
352                         t.Fatalf("Did not receive OK signal")
353                 }
354
355                 // Give the program a chance to enter the sleep function.
356                 time.Sleep(time.Millisecond)
357
358                 cmd.Process.Signal(syscall.SIGSEGV)
359
360                 err = cmd.Wait()
361
362                 if err == nil {
363                         continue
364                 }
365
366                 if ee, ok := err.(*exec.ExitError); !ok {
367                         t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
368                 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
369                         t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
370                 } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV {
371                         t.Errorf("got %v; expected SIGSEGV", ee)
372                 } else {
373                         // We got the error we expected.
374                         return
375                 }
376         }
377
378         t.Errorf("program succeeded unexpectedly %d times", tries)
379 }
380
381 func TestOsSignal(t *testing.T) {
382         switch GOOS {
383         case "windows":
384                 t.Skip("skipping signal test on Windows")
385         }
386
387         defer func() {
388                 os.Remove("libgo3.a")
389                 os.Remove("libgo3.h")
390                 os.Remove("testp")
391                 os.RemoveAll("pkg")
392         }()
393
394         cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "libgo3")
395         cmd.Env = gopathEnv
396         if out, err := cmd.CombinedOutput(); err != nil {
397                 t.Logf("%s", out)
398                 t.Fatal(err)
399         }
400
401         ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
402         if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
403                 t.Logf("%s", out)
404                 t.Fatal(err)
405         }
406
407         if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
408                 t.Logf("%s", out)
409                 t.Fatal(err)
410         }
411 }
412
413 func TestSigaltstack(t *testing.T) {
414         switch GOOS {
415         case "windows":
416                 t.Skip("skipping signal test on Windows")
417         }
418
419         defer func() {
420                 os.Remove("libgo4.a")
421                 os.Remove("libgo4.h")
422                 os.Remove("testp")
423                 os.RemoveAll("pkg")
424         }()
425
426         cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "libgo4")
427         cmd.Env = gopathEnv
428         if out, err := cmd.CombinedOutput(); err != nil {
429                 t.Logf("%s", out)
430                 t.Fatal(err)
431         }
432
433         ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
434         if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
435                 t.Logf("%s", out)
436                 t.Fatal(err)
437         }
438
439         if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
440                 t.Logf("%s", out)
441                 t.Fatal(err)
442         }
443 }
444
445 const testar = `#!/usr/bin/env bash
446 while expr $1 : '[-]' >/dev/null; do
447   shift
448 done
449 echo "testar" > $1
450 echo "testar" > PWD/testar.ran
451 `
452
453 func TestExtar(t *testing.T) {
454         switch GOOS {
455         case "windows":
456                 t.Skip("skipping signal test on Windows")
457         }
458
459         defer func() {
460                 os.Remove("libgo4.a")
461                 os.Remove("libgo4.h")
462                 os.Remove("testar")
463                 os.Remove("testar.ran")
464                 os.RemoveAll("pkg")
465         }()
466
467         os.Remove("testar")
468         dir, err := os.Getwd()
469         if err != nil {
470                 t.Fatal(err)
471         }
472         s := strings.Replace(testar, "PWD", dir, 1)
473         if err := ioutil.WriteFile("testar", []byte(s), 0777); err != nil {
474                 t.Fatal(err)
475         }
476
477         cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "libgo4")
478         cmd.Env = gopathEnv
479         if out, err := cmd.CombinedOutput(); err != nil {
480                 t.Logf("%s", out)
481                 t.Fatal(err)
482         }
483
484         if _, err := os.Stat("testar.ran"); err != nil {
485                 if os.IsNotExist(err) {
486                         t.Error("testar does not exist after go build")
487                 } else {
488                         t.Errorf("error checking testar: %v", err)
489                 }
490         }
491 }