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// +build openbsd
6
7// This, on the face of it, bizarre testing mechanism is necessary because
8// the only reliable way to gauge whether or not a pledge(2) call has succeeded
9// is that the program has been killed as a result of breaking its pledge.
10
11package unix_test
12
13import (
14	"flag"
15	"fmt"
16	"io/ioutil"
17	"os"
18	"os/exec"
19	"path/filepath"
20	"testing"
21
22	"golang.org/x/sys/unix"
23)
24
25type testProc struct {
26	fn      func()       // should always exit instead of returning
27	cleanup func() error // for instance, delete coredumps from testing pledge
28	success bool         // whether zero-exit means success or failure
29}
30
31var (
32	testProcs = map[string]testProc{}
33	procName  = ""
34)
35
36const (
37	optName = "sys-unix-internal-procname"
38)
39
40func init() {
41	flag.StringVar(&procName, optName, "", "internal use only")
42}
43
44// testCmd generates a proper command that, when executed, runs the test
45// corresponding to the given key.
46func testCmd(procName string) (*exec.Cmd, error) {
47	exe, err := filepath.Abs(os.Args[0])
48	if err != nil {
49		return nil, err
50	}
51	cmd := exec.Command(exe, "-"+optName+"="+procName)
52	cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
53	return cmd, nil
54}
55
56// ExitsCorrectly is a comprehensive, one-line-of-use wrapper for testing
57// a testProc with a key.
58func ExitsCorrectly(procName string, t *testing.T) {
59	s := testProcs[procName]
60	c, err := testCmd(procName)
61	defer func() {
62		if s.cleanup() != nil {
63			t.Fatalf("Failed to run cleanup for %s", procName)
64		}
65	}()
66	if err != nil {
67		t.Fatalf("Failed to construct command for %s", procName)
68	}
69	if (c.Run() == nil) != s.success {
70		result := "succeed"
71		if !s.success {
72			result = "fail"
73		}
74		t.Fatalf("Process did not %s when it was supposed to", result)
75	}
76}
77
78func TestMain(m *testing.M) {
79	flag.Parse()
80	if procName != "" {
81		testProcs[procName].fn()
82	}
83	os.Exit(m.Run())
84}
85
86// For example, add a test for pledge.
87func init() {
88	testProcs["pledge"] = testProc{
89		func() {
90			fmt.Println(unix.Pledge("", ""))
91			os.Exit(0)
92		},
93		func() error {
94			files, err := ioutil.ReadDir(".")
95			if err != nil {
96				return err
97			}
98			for _, file := range files {
99				if filepath.Ext(file.Name()) == ".core" {
100					if err := os.Remove(file.Name()); err != nil {
101						return err
102					}
103				}
104			}
105			return nil
106		},
107		false,
108	}
109}
110
111func TestPledge(t *testing.T) {
112	ExitsCorrectly("pledge", t)
113}
114