Browse code

Cleanup pkg/jsonmessage progress tests

Signed-off-by: Daniel Nephin <dnephin@docker.com>

Daniel Nephin authored on 2017/12/30 05:13:13
Showing 3 changed files
... ...
@@ -40,21 +40,17 @@ type JSONProgress struct {
40 40
 	// If true, don't show xB/yB
41 41
 	HideCounts bool   `json:"hidecounts,omitempty"`
42 42
 	Units      string `json:"units,omitempty"`
43
+	nowFunc    func() time.Time
44
+	winSize    int
43 45
 }
44 46
 
45 47
 func (p *JSONProgress) String() string {
46 48
 	var (
47
-		width       = 200
49
+		width       = p.width()
48 50
 		pbBox       string
49 51
 		numbersBox  string
50 52
 		timeLeftBox string
51 53
 	)
52
-
53
-	ws, err := term.GetWinsize(p.terminalFd)
54
-	if err == nil {
55
-		width = int(ws.Width)
56
-	}
57
-
58 54
 	if p.Current <= 0 && p.Total <= 0 {
59 55
 		return ""
60 56
 	}
... ...
@@ -103,7 +99,7 @@ func (p *JSONProgress) String() string {
103 103
 	}
104 104
 
105 105
 	if p.Current > 0 && p.Start > 0 && percentage < 50 {
106
-		fromStart := time.Now().UTC().Sub(time.Unix(p.Start, 0))
106
+		fromStart := p.now().Sub(time.Unix(p.Start, 0))
107 107
 		perEntry := fromStart / time.Duration(p.Current)
108 108
 		left := time.Duration(p.Total-p.Current) * perEntry
109 109
 		left = (left / time.Second) * time.Second
... ...
@@ -115,6 +111,28 @@ func (p *JSONProgress) String() string {
115 115
 	return pbBox + numbersBox + timeLeftBox
116 116
 }
117 117
 
118
+// shim for testing
119
+func (p *JSONProgress) now() time.Time {
120
+	if p.nowFunc == nil {
121
+		p.nowFunc = func() time.Time {
122
+			return time.Now().UTC()
123
+		}
124
+	}
125
+	return p.nowFunc()
126
+}
127
+
128
+// shim for testing
129
+func (p *JSONProgress) width() int {
130
+	if p.winSize != 0 {
131
+		return p.winSize
132
+	}
133
+	ws, err := term.GetWinsize(p.terminalFd)
134
+	if err == nil {
135
+		return int(ws.Width)
136
+	}
137
+	return 200
138
+}
139
+
118 140
 // JSONMessage defines a message struct. It describes
119 141
 // the created time, where it from, status, ID of the
120 142
 // message. It's used for docker events.
... ...
@@ -15,84 +15,102 @@ import (
15 15
 
16 16
 func TestError(t *testing.T) {
17 17
 	je := JSONError{404, "Not found"}
18
-	if je.Error() != "Not found" {
19
-		t.Fatalf("Expected 'Not found' got '%s'", je.Error())
20
-	}
18
+	assert.Assert(t, is.Error(&je, "Not found"))
21 19
 }
22 20
 
23
-func TestProgress(t *testing.T) {
24
-	termsz, err := term.GetWinsize(0)
25
-	if err != nil {
26
-		// we can safely ignore the err here
27
-		termsz = nil
28
-	}
29
-	jp := JSONProgress{}
30
-	if jp.String() != "" {
31
-		t.Fatalf("Expected empty string, got '%s'", jp.String())
32
-	}
33
-
34
-	expected := "      1B"
35
-	jp2 := JSONProgress{Current: 1}
36
-	if jp2.String() != expected {
37
-		t.Fatalf("Expected %q, got %q", expected, jp2.String())
21
+func TestProgressString(t *testing.T) {
22
+	type expected struct {
23
+		short string
24
+		long  string
38 25
 	}
39 26
 
40
-	expectedStart := "[==========>                                        ]      20B/100B"
41
-	if termsz != nil && termsz.Width <= 110 {
42
-		expectedStart = "    20B/100B"
43
-	}
44
-	jp3 := JSONProgress{Current: 20, Total: 100, Start: time.Now().Unix()}
45
-	// Just look at the start of the string
46
-	// (the remaining time is really hard to test -_-)
47
-	if jp3.String()[:len(expectedStart)] != expectedStart {
48
-		t.Fatalf("Expected to start with %q, got %q", expectedStart, jp3.String())
49
-	}
50
-
51
-	expected = "[=========================>                         ]      50B/100B"
52
-	if termsz != nil && termsz.Width <= 110 {
53
-		expected = "    50B/100B"
54
-	}
55
-	jp4 := JSONProgress{Current: 50, Total: 100}
56
-	if jp4.String() != expected {
57
-		t.Fatalf("Expected %q, got %q", expected, jp4.String())
27
+	shortAndLong := func(short, long string) expected {
28
+		return expected{short: short, long: long}
58 29
 	}
59 30
 
60
-	// this number can't be negative gh#7136
61
-	expected = "[==================================================>]      50B"
62
-	if termsz != nil && termsz.Width <= 110 {
63
-		expected = "    50B"
64
-	}
65
-	jp5 := JSONProgress{Current: 50, Total: 40}
66
-	if jp5.String() != expected {
67
-		t.Fatalf("Expected %q, got %q", expected, jp5.String())
31
+	start := time.Date(2017, 12, 3, 15, 10, 1, 0, time.UTC)
32
+	timeAfter := func(delta time.Duration) func() time.Time {
33
+		return func() time.Time {
34
+			return start.Add(delta)
35
+		}
68 36
 	}
69 37
 
70
-	expected = "[=========================>                         ] 50/100 units"
71
-	if termsz != nil && termsz.Width <= 110 {
72
-		expected = "    50/100 units"
73
-	}
74
-	jp6 := JSONProgress{Current: 50, Total: 100, Units: "units"}
75
-	if jp6.String() != expected {
76
-		t.Fatalf("Expected %q, got %q", expected, jp6.String())
38
+	var testcases = []struct {
39
+		name     string
40
+		progress JSONProgress
41
+		expected expected
42
+	}{
43
+		{
44
+			name: "no progress",
45
+		},
46
+		{
47
+			name:     "progress 1",
48
+			progress: JSONProgress{Current: 1},
49
+			expected: shortAndLong("      1B", "      1B"),
50
+		},
51
+		{
52
+			name: "some progress with a start time",
53
+			progress: JSONProgress{
54
+				Current: 20,
55
+				Total:   100,
56
+				Start:   start.Unix(),
57
+				nowFunc: timeAfter(time.Second),
58
+			},
59
+			expected: shortAndLong(
60
+				"     20B/100B 4s",
61
+				"[==========>                                        ]      20B/100B 4s",
62
+			),
63
+		},
64
+		{
65
+			name:     "some progress without a start time",
66
+			progress: JSONProgress{Current: 50, Total: 100},
67
+			expected: shortAndLong(
68
+				"     50B/100B",
69
+				"[=========================>                         ]      50B/100B",
70
+			),
71
+		},
72
+		{
73
+			name:     "current more than total is not negative gh#7136",
74
+			progress: JSONProgress{Current: 50, Total: 40},
75
+			expected: shortAndLong(
76
+				"     50B",
77
+				"[==================================================>]      50B",
78
+			),
79
+		},
80
+		{
81
+			name:     "with units",
82
+			progress: JSONProgress{Current: 50, Total: 100, Units: "units"},
83
+			expected: shortAndLong(
84
+				"50/100 units",
85
+				"[=========================>                         ] 50/100 units",
86
+			),
87
+		},
88
+		{
89
+			name:     "current more than total with units is not negative ",
90
+			progress: JSONProgress{Current: 50, Total: 40, Units: "units"},
91
+			expected: shortAndLong(
92
+				"50 units",
93
+				"[==================================================>] 50 units",
94
+			),
95
+		},
96
+		{
97
+			name:     "hide counts",
98
+			progress: JSONProgress{Current: 50, Total: 100, HideCounts: true},
99
+			expected: shortAndLong(
100
+				"",
101
+				"[=========================>                         ] ",
102
+			),
103
+		},
77 104
 	}
78 105
 
79
-	// this number can't be negative
80
-	expected = "[==================================================>] 50 units"
81
-	if termsz != nil && termsz.Width <= 110 {
82
-		expected = "    50 units"
83
-	}
84
-	jp7 := JSONProgress{Current: 50, Total: 40, Units: "units"}
85
-	if jp7.String() != expected {
86
-		t.Fatalf("Expected %q, got %q", expected, jp7.String())
87
-	}
106
+	for _, testcase := range testcases {
107
+		t.Run(testcase.name, func(t *testing.T) {
108
+			testcase.progress.winSize = 100
109
+			assert.Equal(t, testcase.progress.String(), testcase.expected.short)
88 110
 
89
-	expected = "[=========================>                         ] "
90
-	if termsz != nil && termsz.Width <= 110 {
91
-		expected = ""
92
-	}
93
-	jp8 := JSONProgress{Current: 50, Total: 100, HideCounts: true}
94
-	if jp8.String() != expected {
95
-		t.Fatalf("Expected %q, got %q", expected, jp8.String())
111
+			testcase.progress.winSize = 200
112
+			assert.Equal(t, testcase.progress.String(), testcase.expected.long)
113
+		})
96 114
 	}
97 115
 }
98 116
 
... ...
@@ -8,6 +8,8 @@ import (
8 8
 	"testing"
9 9
 
10 10
 	"github.com/docker/docker/pkg/jsonmessage"
11
+	"github.com/google/go-cmp/cmp"
12
+	"github.com/google/go-cmp/cmp/cmpopts"
11 13
 	"github.com/gotestyourself/gotestyourself/assert"
12 14
 	is "github.com/gotestyourself/gotestyourself/assert/cmp"
13 15
 )
... ...
@@ -58,30 +60,31 @@ func TestJsonProgressFormatterFormatProgress(t *testing.T) {
58 58
 		Total:   30,
59 59
 		Start:   1,
60 60
 	}
61
-	res := sf.formatProgress("id", "action", jsonProgress, &AuxFormatter{Writer: &bytes.Buffer{}})
61
+	aux := "aux message"
62
+	res := sf.formatProgress("id", "action", jsonProgress, aux)
62 63
 	msg := &jsonmessage.JSONMessage{}
63 64
 
64 65
 	assert.NilError(t, json.Unmarshal(res, msg))
65
-	assert.Check(t, is.Equal("id", msg.ID))
66
-	assert.Check(t, is.Equal("action", msg.Status))
67 66
 
68
-	// jsonProgress will always be in the format of:
69
-	// [=========================>                         ]      15B/30B 412910h51m30s
70
-	// The last entry '404933h7m11s' is the timeLeftBox.
71
-	// However, the timeLeftBox field may change as jsonProgress.String() depends on time.Now().
72
-	// Therefore, we have to strip the timeLeftBox from the strings to do the comparison.
73
-
74
-	// Compare the jsonProgress strings before the timeLeftBox
75
-	expectedProgress := "[=========================>                         ]      15B/30B"
76
-	// if terminal column is <= 110, expectedProgressShort is expected.
77
-	expectedProgressShort := "      15B/30B"
78
-	if !(strings.HasPrefix(msg.ProgressMessage, expectedProgress) ||
79
-		strings.HasPrefix(msg.ProgressMessage, expectedProgressShort)) {
80
-		t.Fatalf("ProgressMessage without the timeLeftBox must be %s or %s, got: %s",
81
-			expectedProgress, expectedProgressShort, msg.ProgressMessage)
67
+	rawAux := json.RawMessage(`"` + aux + `"`)
68
+	expected := &jsonmessage.JSONMessage{
69
+		ID:       "id",
70
+		Status:   "action",
71
+		Aux:      &rawAux,
72
+		Progress: jsonProgress,
82 73
 	}
74
+	assert.DeepEqual(t, msg, expected, cmpJSONMessageOpt())
75
+}
83 76
 
84
-	assert.Check(t, is.DeepEqual(jsonProgress, msg.Progress))
77
+func cmpJSONMessageOpt() cmp.Option {
78
+	progressMessagePath := func(path cmp.Path) bool {
79
+		return path.String() == "ProgressMessage"
80
+	}
81
+	return cmp.Options{
82
+		cmpopts.IgnoreUnexported(jsonmessage.JSONProgress{}),
83
+		// Ignore deprecated property that is a derivative of Progress
84
+		cmp.FilterPath(progressMessagePath, cmp.Ignore()),
85
+	}
85 86
 }
86 87
 
87 88
 func TestJsonProgressFormatterFormatStatus(t *testing.T) {