Skip to content

Commit b7b0a9a

Browse files
committed
feature: create discard console
Signed-off-by: Terry Howe <[email protected]>
1 parent 2933b63 commit b7b0a9a

File tree

8 files changed

+212
-111
lines changed

8 files changed

+212
-111
lines changed

cmd/oras/internal/display/status/console/console.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ limitations under the License.
1616
package console
1717

1818
import (
19-
"os"
20-
21-
"github.com/containerd/console"
19+
containerd "github.com/containerd/console"
2220
"github.com/morikuni/aec"
23-
"oras.land/oras/internal/testutils"
21+
"os"
2422
)
2523

2624
const (
@@ -34,63 +32,78 @@ const (
3432
Restore = "\0338"
3533
)
3634

37-
// Console is a wrapper around containerd's console.Console and ANSI escape
38-
// codes.
39-
type Console struct {
40-
console.Console
35+
// Console is a wrapper around containerd's Console and ANSI escape codes.
36+
type Console interface {
37+
containerd.Console
38+
GetHeightWidth() (height, width int)
39+
Save()
40+
NewRow()
41+
OutputTo(upCnt uint, str string)
42+
Restore()
43+
}
44+
45+
type console struct {
46+
containerd.Console
47+
}
48+
49+
// NewConsole generates a console from a file.
50+
func NewConsole(f *os.File) (Console, error) {
51+
if f != nil && f.Name() == os.DevNull {
52+
return NewDiscardConsole(f), nil
53+
}
54+
c, err := containerd.ConsoleFromFile(f)
55+
if err != nil {
56+
return nil, err
57+
}
58+
return &console{c}, nil
4159
}
4260

4361
// Size returns the width and height of the console.
4462
// If the console size cannot be determined, returns a default value of 80x10.
45-
func (c *Console) Size() (width, height int) {
46-
width = MinWidth
47-
height = MinHeight
48-
size, err := c.Console.Size()
49-
if err == nil {
63+
func (c *console) Size() (size containerd.WinSize, err error) {
64+
size, err = c.Console.Size()
65+
if err != nil {
66+
size.Height = MinHeight
67+
size.Width = MinWidth
68+
} else {
5069
if size.Height > MinHeight {
51-
height = int(size.Height)
70+
size.Height = MinHeight
5271
}
5372
if size.Width > MinWidth {
54-
width = int(size.Width)
73+
size.Width = MinWidth
5574
}
5675
}
5776
return
5877
}
5978

60-
// New generates a Console from a file.
61-
func New(f *os.File) (*Console, error) {
62-
if f != nil && f.Name() == os.DevNull {
63-
return &Console{testutils.NewMockConsole(f)}, nil
64-
}
65-
c, err := console.ConsoleFromFile(f)
66-
if err != nil {
67-
return nil, err
68-
}
69-
return &Console{c}, nil
79+
// GetHeightWidth returns the width and height of the console.
80+
func (c *console) GetHeightWidth() (height, width int) {
81+
windowSize, _ := c.Size()
82+
return int(windowSize.Height), int(windowSize.Width)
7083
}
7184

7285
// Save saves the current cursor position.
73-
func (c *Console) Save() {
86+
func (c *console) Save() {
7487
_, _ = c.Write([]byte(aec.Hide.Apply(Save)))
7588
}
7689

7790
// NewRow allocates a horizontal space to the output area with scroll if needed.
78-
func (c *Console) NewRow() {
91+
func (c *console) NewRow() {
7992
_, _ = c.Write([]byte(Restore))
8093
_, _ = c.Write([]byte("\n"))
8194
_, _ = c.Write([]byte(Save))
8295
}
8396

8497
// OutputTo outputs a string to a specific line.
85-
func (c *Console) OutputTo(upCnt uint, str string) {
98+
func (c *console) OutputTo(upCnt uint, str string) {
8699
_, _ = c.Write([]byte(Restore))
87100
_, _ = c.Write([]byte(aec.PreviousLine(upCnt).Apply(str)))
88101
_, _ = c.Write([]byte("\n"))
89102
_, _ = c.Write([]byte(aec.EraseLine(aec.EraseModes.Tail).String()))
90103
}
91104

92105
// Restore restores the saved cursor position.
93-
func (c *Console) Restore() {
106+
func (c *console) Restore() {
94107
// cannot use aec.Restore since DEC has better compatibility than SCO
95108
_, _ = c.Write([]byte(Restore))
96109
_, _ = c.Write([]byte(aec.Column(0).

cmd/oras/internal/display/status/console/console_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ package console
2020
import (
2121
"testing"
2222

23-
"github.com/containerd/console"
23+
containerd "github.com/containerd/console"
2424
)
2525

2626
func validateSize(t *testing.T, gotWidth, gotHeight, wantWidth, wantHeight int) {
@@ -34,30 +34,30 @@ func validateSize(t *testing.T, gotWidth, gotHeight, wantWidth, wantHeight int)
3434
}
3535

3636
func TestConsole_Size(t *testing.T) {
37-
pty, _, err := console.NewPty()
37+
pty, _, err := containerd.NewPty()
3838
if err != nil {
3939
t.Fatal(err)
4040
}
41-
c := &Console{
41+
c := &console{
4242
Console: pty,
4343
}
4444

4545
// minimal width and height
46-
gotWidth, gotHeight := c.Size()
46+
gotHeight, gotWidth := c.GetHeightWidth()
4747
validateSize(t, gotWidth, gotHeight, MinWidth, MinHeight)
4848

4949
// zero width
50-
_ = pty.Resize(console.WinSize{Width: 0, Height: MinHeight})
51-
gotWidth, gotHeight = c.Size()
50+
_ = pty.Resize(containerd.WinSize{Width: 0, Height: MinHeight})
51+
gotWidth, gotHeight = c.GetHeightWidth()
5252
validateSize(t, gotWidth, gotHeight, MinWidth, MinHeight)
5353

5454
// zero height
55-
_ = pty.Resize(console.WinSize{Width: MinWidth, Height: 0})
56-
gotWidth, gotHeight = c.Size()
55+
_ = pty.Resize(containerd.WinSize{Width: MinWidth, Height: 0})
56+
gotWidth, gotHeight = c.GetHeightWidth()
5757
validateSize(t, gotWidth, gotHeight, MinWidth, MinHeight)
5858

5959
// valid zero and height
60-
_ = pty.Resize(console.WinSize{Width: 200, Height: 100})
61-
gotWidth, gotHeight = c.Size()
60+
_ = pty.Resize(containerd.WinSize{Width: 200, Height: 100})
61+
gotWidth, gotHeight = c.GetHeightWidth()
6262
validateSize(t, gotWidth, gotHeight, 200, 100)
6363
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
Copyright The ORAS Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package console
17+
18+
import (
19+
"os"
20+
21+
containerd "github.com/containerd/console"
22+
)
23+
24+
type discardConsole struct {
25+
*os.File
26+
}
27+
28+
func NewDiscardConsole(f *os.File) Console {
29+
dc := discardConsole{
30+
File: f,
31+
}
32+
return &dc
33+
}
34+
35+
// Fd returns its file descriptor
36+
func (mc *discardConsole) Fd() uintptr {
37+
return os.Stderr.Fd()
38+
}
39+
40+
// Name returns its file name
41+
func (mc *discardConsole) Name() string {
42+
return mc.File.Name()
43+
}
44+
45+
func (mc *discardConsole) Resize(_ containerd.WinSize) error {
46+
return nil
47+
}
48+
49+
func (mc *discardConsole) ResizeFrom(containerd.Console) error {
50+
return nil
51+
}
52+
func (mc *discardConsole) SetRaw() error {
53+
return nil
54+
}
55+
func (mc *discardConsole) DisableEcho() error {
56+
return nil
57+
}
58+
func (mc *discardConsole) Reset() error {
59+
return nil
60+
}
61+
func (mc *discardConsole) Size() (containerd.WinSize, error) {
62+
ws := containerd.WinSize{
63+
Width: 80,
64+
Height: 24,
65+
}
66+
return ws, nil
67+
}
68+
69+
// GetHeightWidth returns the width and height of the console.
70+
func (mc *discardConsole) GetHeightWidth() (height, width int) {
71+
windowSize, _ := mc.Size()
72+
return int(windowSize.Height), int(windowSize.Width)
73+
}
74+
75+
func (mc *discardConsole) Save() {
76+
}
77+
78+
func (mc *discardConsole) NewRow() {
79+
}
80+
81+
func (mc *discardConsole) OutputTo(_ uint, _ string) {
82+
}
83+
84+
func (mc *discardConsole) Restore() {
85+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Copyright The ORAS Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package console
17+
18+
import (
19+
"os"
20+
"testing"
21+
22+
containerd "github.com/containerd/console"
23+
)
24+
25+
func TestConsole_New(t *testing.T) {
26+
mockFile, err := os.OpenFile(os.DevNull, os.O_RDWR, 0666)
27+
if err != nil {
28+
t.Fatalf("Unexpected error %v", err)
29+
}
30+
31+
sut, err := NewConsole(mockFile)
32+
if err != nil {
33+
t.Errorf("Unexpected error %v", err)
34+
}
35+
36+
if err = sut.Resize(containerd.WinSize{}); err != nil {
37+
t.Errorf("Unexpected erro for Resize: %v", err)
38+
}
39+
if err = sut.ResizeFrom(nil); err != nil {
40+
t.Errorf("Unexpected erro for Resize: %v", err)
41+
}
42+
if err = sut.SetRaw(); err != nil {
43+
t.Errorf("Unexpected erro for Resize: %v", err)
44+
}
45+
if err = sut.DisableEcho(); err != nil {
46+
t.Errorf("Unexpected erro for Resize: %v", err)
47+
}
48+
if err = sut.Reset(); err != nil {
49+
t.Errorf("Unexpected erro for Resize: %v", err)
50+
}
51+
windowSize, _ := sut.Size()
52+
if windowSize.Height != 24 {
53+
t.Errorf("Expected size 24 actual %d", windowSize.Height)
54+
}
55+
if windowSize.Width != 80 {
56+
t.Errorf("Expected size 80 actual %d", windowSize.Width)
57+
}
58+
h, w := sut.GetHeightWidth()
59+
if h != 24 {
60+
t.Errorf("Expected size 24 actual %d", h)
61+
}
62+
if w != 80 {
63+
t.Errorf("Expected size 80 actual %d", w)
64+
}
65+
}

cmd/oras/internal/display/status/progress/manager.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ type Manager interface {
4444
type manager struct {
4545
status []*status
4646
statusLock sync.RWMutex
47-
console *console.Console
47+
console console.Console
4848
actionPrompt string
4949
donePrompt string
5050
updating sync.WaitGroup
@@ -54,7 +54,7 @@ type manager struct {
5454

5555
// NewManager initialized a new progress manager.
5656
func NewManager(actionPrompt string, donePrompt string, tty *os.File) (Manager, error) {
57-
c, err := console.New(tty)
57+
c, err := console.NewConsole(tty)
5858
if err != nil {
5959
return nil, err
6060
}
@@ -92,7 +92,7 @@ func (m *manager) render() {
9292
m.statusLock.RLock()
9393
defer m.statusLock.RUnlock()
9494
// todo: update size in another routine
95-
width, height := m.console.Size()
95+
height, width := m.console.GetHeightWidth()
9696
lineCount := len(m.status) * 2
9797
offset := 0
9898
if lineCount > height {

cmd/oras/internal/display/status/progress/manager_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,15 @@ func Test_manager_render(t *testing.T) {
3232
t.Fatal(err)
3333
}
3434
defer device.Close()
35+
sole, err := console.NewConsole(device)
36+
if err != nil {
37+
t.Fatal(err)
38+
}
39+
3540
m := &manager{
36-
console: &console.Console{Console: pty},
41+
console: sole,
3742
}
38-
_, height := m.console.Size()
43+
_, height := m.console.GetHeightWidth()
3944
for i := 0; i < height; i++ {
4045
if _, err := m.Add(); err != nil {
4146
t.Fatal(err)

internal/testutils/console.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build freebsd || linux || netbsd || openbsd || solaris
2-
31
/*
42
Copyright The ORAS Authors.
53
Licensed under the Apache License, Version 2.0 (the "License");

0 commit comments

Comments
 (0)