Skip to content

Commit f2066cc

Browse files
committed
refactor: simplify reader with manager and messenger
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
1 parent 952d867 commit f2066cc

File tree

11 files changed

+335
-69
lines changed

11 files changed

+335
-69
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ type console struct {
4949

5050
// NewConsole generates a console from a file.
5151
func NewConsole(f *os.File) (Console, error) {
52+
if f != nil && f.Name() == os.DevNull {
53+
return NewDiscardConsole(f), nil
54+
}
5255
c, err := containerd.ConsoleFromFile(f)
5356
if err != nil {
5457
return nil, err
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
// NewDiscardConsole create a console that does not output.
29+
func NewDiscardConsole(f *os.File) Console {
30+
dc := discardConsole{
31+
File: f,
32+
}
33+
return &dc
34+
}
35+
36+
// Fd returns its file descriptor
37+
func (mc *discardConsole) Fd() uintptr {
38+
return os.Stderr.Fd()
39+
}
40+
41+
// Name returns its file name
42+
func (mc *discardConsole) Name() string {
43+
return mc.File.Name()
44+
}
45+
46+
// Resize ignored
47+
func (mc *discardConsole) Resize(_ containerd.WinSize) error {
48+
return nil
49+
}
50+
51+
// ResizeFrom ignored
52+
func (mc *discardConsole) ResizeFrom(containerd.Console) error {
53+
return nil
54+
}
55+
56+
// SetRaw ignored
57+
func (mc *discardConsole) SetRaw() error {
58+
return nil
59+
}
60+
61+
// DisableEcho ignored
62+
func (mc *discardConsole) DisableEcho() error {
63+
return nil
64+
}
65+
66+
// Reset ignored
67+
func (mc *discardConsole) Reset() error {
68+
return nil
69+
}
70+
71+
// Size return default size
72+
func (mc *discardConsole) Size() (containerd.WinSize, error) {
73+
ws := containerd.WinSize{
74+
Width: 80,
75+
Height: 24,
76+
}
77+
return ws, nil
78+
}
79+
80+
// GetHeightWidth returns the width and height of the console.
81+
func (mc *discardConsole) GetHeightWidth() (height, width int) {
82+
windowSize, _ := mc.Size()
83+
return int(windowSize.Height), int(windowSize.Width)
84+
}
85+
86+
// Save ignored
87+
func (mc *discardConsole) Save() {
88+
}
89+
90+
// NewRow ignored
91+
func (mc *discardConsole) NewRow() {
92+
}
93+
94+
// OutputTo ignored
95+
func (mc *discardConsole) OutputTo(_ uint, _ string) {
96+
}
97+
98+
// Restore ignored
99+
func (mc *discardConsole) Restore() {
100+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
if sut.Fd() != os.Stderr.Fd() {
66+
t.Errorf("Expected size %d actual %d", sut.Fd(), os.Stderr.Fd())
67+
}
68+
if sut.Name() != os.DevNull {
69+
t.Errorf("Expected size %s actual %s", sut.Name(), os.DevNull)
70+
}
71+
sut.OutputTo(0, "ignored")
72+
sut.Restore()
73+
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,23 @@ type manager struct {
4545
status []*status
4646
statusLock sync.RWMutex
4747
console console.Console
48+
actionPrompt string
49+
donePrompt string
4850
updating sync.WaitGroup
4951
renderDone chan struct{}
5052
renderClosed chan struct{}
5153
}
5254

5355
// NewManager initialized a new progress manager.
54-
func NewManager(tty *os.File) (Manager, error) {
56+
func NewManager(actionPrompt string, donePrompt string, tty *os.File) (Manager, error) {
5557
c, err := console.NewConsole(tty)
5658
if err != nil {
5759
return nil, err
5860
}
5961
m := &manager{
6062
console: c,
63+
actionPrompt: actionPrompt,
64+
donePrompt: donePrompt,
6165
renderDone: make(chan struct{}),
6266
renderClosed: make(chan struct{}),
6367
}
@@ -131,15 +135,15 @@ func (m *manager) SendAndStop(desc ocispec.Descriptor, prompt string) error {
131135
}
132136

133137
func (m *manager) statusChan(s *status) *Messenger {
134-
ch := make(chan *status, BufferSize)
138+
messenger := NewMessenger(m.actionPrompt, m.donePrompt)
135139
m.updating.Add(1)
136140
go func() {
137141
defer m.updating.Done()
138-
for newStatus := range ch {
142+
for newStatus := range messenger.ch {
139143
s.update(newStatus)
140144
}
141145
}()
142-
return &Messenger{ch: ch}
146+
return messenger
143147
}
144148

145149
// Close stops all status and waits for updating and rendering.

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package progress
1919

2020
import (
2121
"fmt"
22+
"os"
2223
"testing"
2324

2425
"oras.land/oras/cmd/oras/internal/display/status/console"
@@ -55,3 +56,31 @@ func Test_manager_render(t *testing.T) {
5556
t.Fatal(err)
5657
}
5758
}
59+
60+
func TestNewManager(t *testing.T) {
61+
mockFile, err := os.OpenFile(os.DevNull, os.O_RDWR, 0666)
62+
if err != nil {
63+
t.Fatalf("Unexpected error %v", err)
64+
}
65+
66+
sut, err := NewManager("Action", "Done", mockFile)
67+
if err != nil {
68+
t.Errorf("Unexpected error %v", err)
69+
}
70+
71+
messenger, err := sut.Add()
72+
if err != nil {
73+
t.Errorf("Unexpected error %v", err)
74+
}
75+
if messenger.actionPrompt != "Action" {
76+
t.Errorf("Expected prompt Action actual %v", messenger.actionPrompt)
77+
}
78+
if messenger.donePrompt != "Done" {
79+
t.Errorf("Expected prompt Done actual %v", messenger.donePrompt)
80+
}
81+
82+
_, err = NewManager("Action", "Done", os.Stderr)
83+
if err == nil {
84+
t.Errorf("Expected error when using Stderr as console")
85+
}
86+
}

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,20 @@ import (
2323

2424
// Messenger is progress message channel.
2525
type Messenger struct {
26-
ch chan *status
27-
closed bool
26+
ch chan *status
27+
actionPrompt string
28+
donePrompt string
29+
closed bool
30+
}
31+
32+
// NewMessenger create a new messenger object
33+
func NewMessenger(actionPrompt, donePrompt string) *Messenger {
34+
ch := make(chan *status, BufferSize)
35+
return &Messenger{
36+
ch: ch,
37+
actionPrompt: actionPrompt,
38+
donePrompt: donePrompt,
39+
}
2840
}
2941

3042
// Start initializes the messenger.
@@ -50,7 +62,17 @@ func (sm *Messenger) Send(prompt string, descriptor ocispec.Descriptor, offset i
5062
}
5163
}
5264

53-
// Stop the messenger after sending a end message.
65+
// SendAction send the action status message.
66+
func (sm *Messenger) SendAction(descriptor ocispec.Descriptor, offset int64) {
67+
sm.Send(sm.actionPrompt, descriptor, offset)
68+
}
69+
70+
// SendDone send the done status message.
71+
func (sm *Messenger) SendDone(descriptor ocispec.Descriptor, offset int64) {
72+
sm.Send(sm.donePrompt, descriptor, offset)
73+
}
74+
75+
// Stop the messenger after sending end message.
5476
func (sm *Messenger) Stop() {
5577
if sm.closed {
5678
return

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

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ import (
2222

2323
func Test_Messenger(t *testing.T) {
2424
var msg *status
25-
ch := make(chan *status, BufferSize)
26-
messenger := &Messenger{ch: ch}
25+
messenger := NewMessenger("Action", "Done")
2726

2827
messenger.Start()
2928
select {
30-
case msg = <-ch:
29+
case msg = <-messenger.ch:
3130
if msg.offset != -1 {
3231
t.Errorf("Expected start message with offset -1, got %d", msg.offset)
3332
}
@@ -42,7 +41,7 @@ func Test_Messenger(t *testing.T) {
4241
expected := int64(50)
4342
messenger.Send("Reading", desc, expected)
4443
select {
45-
case msg = <-ch:
44+
case msg = <-messenger.ch:
4645
if msg.offset != expected {
4746
t.Errorf("Expected status message with offset %d, got %d", expected, msg.offset)
4847
}
@@ -56,7 +55,7 @@ func Test_Messenger(t *testing.T) {
5655
messenger.Send("Reading", desc, expected)
5756
messenger.Send("Read", desc, desc.Size)
5857
select {
59-
case msg = <-ch:
58+
case msg = <-messenger.ch:
6059
if msg.offset != desc.Size {
6160
t.Errorf("Expected status message with offset %d, got %d", expected, msg.offset)
6261
}
@@ -67,15 +66,42 @@ func Test_Messenger(t *testing.T) {
6766
t.Error("Expected status message")
6867
}
6968
select {
70-
case msg = <-ch:
69+
case msg = <-messenger.ch:
7170
t.Errorf("Unexpected status message %v", msg)
7271
default:
7372
}
7473

74+
messenger.SendAction(desc, expected)
75+
select {
76+
case msg = <-messenger.ch:
77+
if msg.offset != expected {
78+
t.Errorf("Expected status message with offset %d, got %d", expected, msg.offset)
79+
}
80+
if msg.prompt != "Action" {
81+
t.Errorf("Expected status message prompt Action, got %s", msg.prompt)
82+
}
83+
default:
84+
t.Error("Expected status message")
85+
}
86+
87+
expected += 1
88+
messenger.SendDone(desc, expected)
89+
select {
90+
case msg = <-messenger.ch:
91+
if msg.offset != expected {
92+
t.Errorf("Expected status message with offset %d, got %d", expected, msg.offset)
93+
}
94+
if msg.prompt != "Done" {
95+
t.Errorf("Expected status message prompt Done, got %s", msg.prompt)
96+
}
97+
default:
98+
t.Error("Expected status message")
99+
}
100+
75101
expected = int64(-1)
76102
messenger.Stop()
77103
select {
78-
case msg = <-ch:
104+
case msg = <-messenger.ch:
79105
if msg.offset != expected {
80106
t.Errorf("Expected END status message with offset %d, got %d", expected, msg.offset)
81107
}
@@ -85,7 +111,7 @@ func Test_Messenger(t *testing.T) {
85111

86112
messenger.Stop()
87113
select {
88-
case msg = <-ch:
114+
case msg = <-messenger.ch:
89115
if msg != nil {
90116
t.Errorf("Unexpected status message %v", msg)
91117
}

0 commit comments

Comments
 (0)