Skip to content

Commit b27b544

Browse files
authored
Merge pull request containerd#3561 from apostasie/remove-unbuffer
Add pseudo tty support for tests
2 parents 506d875 + 97919c2 commit b27b544

File tree

7 files changed

+170
-12
lines changed

7 files changed

+170
-12
lines changed

cmd/nerdctl/issues/main_linux_test.go

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,26 @@ func TestMain(m *testing.M) {
3333
func TestIssue108(t *testing.T) {
3434
testCase := nerdtest.Setup()
3535

36+
testCase.Require = test.Linux
37+
3638
testCase.SubTests = []*test.Case{
3739
{
3840
Description: "-it --net=host",
39-
Require: test.Binary("unbuffer"),
4041
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
41-
cmd := helpers.
42-
Command("run", "-it", "--rm", "--net=host", testutil.AlpineImage, "echo", "this was always working")
43-
cmd.WithWrapper("unbuffer")
42+
cmd := helpers.Command("run", "-it", "--rm", "--net=host", testutil.AlpineImage, "echo", "this was always working")
43+
cmd.WithPseudoTTY()
4444
return cmd
4545
},
46-
// Note: unbuffer will merge stdout and stderr, preventing exact match here
47-
Expected: test.Expects(0, nil, test.Contains("this was always working")),
46+
Expected: test.Expects(0, nil, test.Equals("this was always working\r\n")),
4847
},
4948
{
5049
Description: "--net=host -it",
51-
Require: test.Binary("unbuffer"),
5250
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
53-
cmd := helpers.
54-
Command("run", "--rm", "--net=host", "-it", testutil.AlpineImage, "echo", "this was not working due to issue #108")
55-
cmd.WithWrapper("unbuffer")
51+
cmd := helpers.Command("run", "--rm", "--net=host", "-it", testutil.AlpineImage, "echo", "this was not working due to issue #108")
52+
cmd.WithPseudoTTY()
5653
return cmd
5754
},
58-
// Note: unbuffer will merge stdout and stderr, preventing exact match here
59-
Expected: test.Expects(0, nil, test.Contains("this was not working due to issue #108")),
55+
Expected: test.Expects(0, nil, test.Equals("this was not working due to issue #108\r\n")),
6056
},
6157
}
6258

pkg/testutil/test/command.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type GenericCommand struct {
4545
envBlackList []string
4646
stdin io.Reader
4747
async bool
48+
pty bool
4849
timeout time.Duration
4950
workingDir string
5051

@@ -65,6 +66,10 @@ func (gc *GenericCommand) WithWrapper(binary string, args ...string) {
6566
gc.helperArgs = args
6667
}
6768

69+
func (gc *GenericCommand) WithPseudoTTY() {
70+
gc.pty = true
71+
}
72+
6873
func (gc *GenericCommand) WithStdin(r io.Reader) {
6974
gc.stdin = r
7075
}
@@ -77,6 +82,7 @@ func (gc *GenericCommand) WithCwd(path string) {
7782
// Primitives (gc.timeout) is here, it is just a matter of exposing a WithTimeout method
7883
// - UX to be decided
7984
// - validate use case: would we ever need this?
85+
8086
func (gc *GenericCommand) Run(expect *Expected) {
8187
if gc.t != nil {
8288
gc.t.Helper()
@@ -90,6 +96,16 @@ func (gc *GenericCommand) Run(expect *Expected) {
9096
} else {
9197
iCmdCmd := gc.boot()
9298
env = iCmdCmd.Env
99+
100+
if gc.pty {
101+
pty, tty, _ := Open()
102+
iCmdCmd.Stdin = tty
103+
iCmdCmd.Stdout = tty
104+
iCmdCmd.Stderr = tty
105+
defer pty.Close()
106+
defer tty.Close()
107+
}
108+
93109
// Run it
94110
result = icmd.RunCmd(iCmdCmd)
95111
}

pkg/testutil/test/pty.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package test
18+
19+
import "errors"
20+
21+
var ErrPTYFailure = errors.New("pty failure")
22+
var ErrPTYUnsupportedPlatform = errors.New("pty not supported on this platform")

pkg/testutil/test/pty_freebsd.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package test
18+
19+
import (
20+
"os"
21+
)
22+
23+
func Open() (pty, tty *os.File, err error) {
24+
return nil, nil, ErrPTYUnsupportedPlatform
25+
}

pkg/testutil/test/pty_linux.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package test
18+
19+
import (
20+
"errors"
21+
"os"
22+
"strconv"
23+
"syscall"
24+
"unsafe"
25+
)
26+
27+
// Inspiration from https://github.com/creack/pty/tree/2cde18bfb702199728dd43bf10a6c15c7336da0a
28+
29+
func Open() (pty, tty *os.File, err error) {
30+
defer func() {
31+
if err != nil && pty != nil {
32+
err = errors.Join(pty.Close(), err)
33+
}
34+
if err != nil {
35+
err = errors.Join(ErrPTYFailure, err)
36+
}
37+
}()
38+
39+
pty, err = os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
40+
if err != nil {
41+
return nil, nil, err
42+
}
43+
44+
var n uint32
45+
err = ioctl(pty, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
46+
if err != nil {
47+
return nil, nil, err
48+
}
49+
50+
sname := "/dev/pts/" + strconv.Itoa(int(n))
51+
52+
var u int32
53+
err = ioctl(pty, syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
54+
if err != nil {
55+
return nil, nil, err
56+
}
57+
58+
tty, err = os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
59+
if err != nil {
60+
return nil, nil, err
61+
}
62+
63+
return pty, tty, nil
64+
}
65+
66+
func ioctl(f *os.File, cmd, ptr uintptr) error {
67+
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), cmd, ptr)
68+
if e != 0 {
69+
return e
70+
}
71+
return nil
72+
}

pkg/testutil/test/pty_windows.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package test
18+
19+
import (
20+
"os"
21+
)
22+
23+
func Open() (pty, tty *os.File, err error) {
24+
return nil, nil, ErrPTYUnsupportedPlatform
25+
}

pkg/testutil/test/test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ type TestableCommand interface {
9797
WithArgs(args ...string)
9898
// WithWrapper allows wrapping a command with another command (for example: `time`, `unbuffer`)
9999
WithWrapper(binary string, args ...string)
100+
// WithPseudoTTY
101+
WithPseudoTTY()
100102
// WithStdin allows passing a reader to be used for stdin for the command
101103
WithStdin(r io.Reader)
102104
// WithCwd allows specifying the working directory for the command

0 commit comments

Comments
 (0)