Files
docker-cli/cli/command/formatter/image_test.go
Sebastiaan van Stijn dace8fdc75 formatter: reduce minimum width for columns in table-view
The tabwriter was configured to have a min-width for columns of 20 positions.
This seemed quite wide, and caused smaller columns to be printed with a large
gap between.

Before:

    docker container stats

    CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
    29184b3ae391        amazing_shirley     0.00%               800KiB / 1.944GiB     0.04%               1.44kB / 0B         0B / 0B             1
    403c101bad56        agitated_swartz     0.15%               34.31MiB / 1.944GiB   1.72%               10.2MB / 206kB      0B / 0B             51
    0dc4b7f6c6be        container2          0.00%               1.012MiB / 1.944GiB   0.05%               12.9kB / 0B         0B / 0B             5
    2d99abcc6f62        container99         0.00%               972KiB / 1.944GiB     0.05%               13kB / 0B           0B / 0B             5
    9f9aa90173ac        foo                 0.00%               820KiB / 1.944GiB     0.04%               13kB / 0B           0B / 0B             5

    docker container ls

    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    29184b3ae391        docker-cli-dev      "ash"                    4 hours ago         Up 4 hours                              amazing_shirley
    403c101bad56        docker-dev:master   "hack/dind bash"         3 days ago          Up 3 days                               agitated_swartz
    0dc4b7f6c6be        nginx:alpine        "/docker-entrypoint.…"   4 days ago          Up 4 days           80/tcp              container2
    2d99abcc6f62        nginx:alpine        "/docker-entrypoint.…"   4 days ago          Up 4 days           80/tcp              container99
    9f9aa90173ac        nginx:alpine        "/docker-entrypoint.…"   4 days ago          Up 4 days           80/tcp              foo

    docker image ls

    REPOSITORY          TAG                    IMAGE ID            CREATED             SIZE
    docker-cli-dev      latest                 5f603caa04aa        4 hours ago         610MB
    docker-cli-native   latest                 9dd29f8d387b        4 hours ago         519MB
    docker-dev          master                 8132bf7a199e        3 days ago          2.02GB
    docker-dev          improve-build-errors   69e208994b3f        11 days ago         2.01GB
    docker-dev          refactor-idtools       69e208994b3f        11 days ago         2.01GB

After:

    docker container stats

    CONTAINER ID   NAME              CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O   PIDS
    29184b3ae391   amazing_shirley   0.14%     5.703MiB / 1.944GiB   0.29%     1.44kB / 0B      0B / 0B     10
    403c101bad56   agitated_swartz   0.15%     56.97MiB / 1.944GiB   2.86%     10.2MB / 206kB   0B / 0B     51
    0dc4b7f6c6be   container2        0.00%     1016KiB / 1.944GiB    0.05%     12.9kB / 0B      0B / 0B     5
    2d99abcc6f62   container99       0.00%     956KiB / 1.944GiB     0.05%     13kB / 0B        0B / 0B     5
    9f9aa90173ac   foo               0.00%     980KiB / 1.944GiB     0.05%     13kB / 0B        0B / 0B     5

    docker container ls

    CONTAINER ID   IMAGE               COMMAND                  CREATED          STATUS          PORTS     NAMES
    29184b3ae391   docker-cli-dev      "ash"                    12 minutes ago   Up 12 minutes             amazing_shirley
    403c101bad56   docker-dev:master   "hack/dind bash"         3 days ago       Up 3 days                 agitated_swartz
    0dc4b7f6c6be   nginx:alpine        "/docker-entrypoint.…"   4 days ago       Up 4 days       80/tcp    container2
    2d99abcc6f62   nginx:alpine        "/docker-entrypoint.…"   4 days ago       Up 4 days       80/tcp    container99
    9f9aa90173ac   nginx:alpine        "/docker-entrypoint.…"   4 days ago       Up 4 days       80/tcp    foo

    docker image ls

    REPOSITORY          TAG                    IMAGE ID       CREATED         SIZE
    docker-cli-dev      latest                 5f603caa04aa   4 hours ago     610MB
    docker-cli-native   latest                 9dd29f8d387b   4 hours ago     519MB
    docker-dev          master                 8132bf7a199e   3 days ago      2.02GB
    docker-dev          improve-build-errors   69e208994b3f   11 days ago     2.01GB
    docker-dev          refactor-idtools       69e208994b3f   11 days ago     2.01GB

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-31 16:31:15 +02:00

380 lines
8.6 KiB
Go

package formatter
import (
"bytes"
"fmt"
"strings"
"testing"
"time"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/stringid"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestImageContext(t *testing.T) {
imageID := stringid.GenerateRandomID()
unix := time.Now().Unix()
zeroTime := int64(-62135596800)
var ctx imageContext
cases := []struct {
imageCtx imageContext
expValue string
call func() string
}{
{
imageCtx: imageContext{i: types.ImageSummary{ID: imageID}, trunc: true},
expValue: stringid.TruncateID(imageID),
call: ctx.ID,
},
{
imageCtx: imageContext{i: types.ImageSummary{ID: imageID}, trunc: false},
expValue: imageID,
call: ctx.ID,
},
{
imageCtx: imageContext{i: types.ImageSummary{Size: 10, VirtualSize: 10}, trunc: true},
expValue: "10B",
call: ctx.Size,
},
{
imageCtx: imageContext{i: types.ImageSummary{Created: unix}, trunc: true},
expValue: time.Unix(unix, 0).String(), call: ctx.CreatedAt,
},
// FIXME
// {imageContext{
// i: types.ImageSummary{Created: unix},
// trunc: true,
// }, units.HumanDuration(time.Unix(unix, 0)), createdSinceHeader, ctx.CreatedSince},
{
imageCtx: imageContext{i: types.ImageSummary{}, repo: "busybox"},
expValue: "busybox",
call: ctx.Repository,
},
{
imageCtx: imageContext{i: types.ImageSummary{}, tag: "latest"},
expValue: "latest",
call: ctx.Tag,
},
{
imageCtx: imageContext{i: types.ImageSummary{}, digest: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a"},
expValue: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a",
call: ctx.Digest,
},
{
imageCtx: imageContext{i: types.ImageSummary{Containers: 10}},
expValue: "10",
call: ctx.Containers,
},
{
imageCtx: imageContext{i: types.ImageSummary{VirtualSize: 10000}},
expValue: "10kB",
call: ctx.VirtualSize,
},
{
imageCtx: imageContext{i: types.ImageSummary{SharedSize: 10000}},
expValue: "10kB",
call: ctx.SharedSize,
},
{
imageCtx: imageContext{i: types.ImageSummary{SharedSize: 5000, VirtualSize: 20000}},
expValue: "15kB",
call: ctx.UniqueSize,
},
{
imageCtx: imageContext{i: types.ImageSummary{Created: zeroTime}},
expValue: "",
call: ctx.CreatedSince,
},
}
for _, c := range cases {
ctx = c.imageCtx
v := c.call()
if strings.Contains(v, ",") {
test.CompareMultipleValues(t, v, c.expValue)
} else {
assert.Check(t, is.Equal(c.expValue, v))
}
}
}
func TestImageContextWrite(t *testing.T) {
unixTime := time.Now().AddDate(0, 0, -1).Unix()
zeroTime := int64(-62135596800)
expectedTime := time.Unix(unixTime, 0).String()
expectedZeroTime := time.Unix(zeroTime, 0).String()
cases := []struct {
context ImageContext
expected string
}{
// Errors
{
ImageContext{
Context: Context{
Format: "{{InvalidFunction}}",
},
},
`Template parsing error: template: :1: function "InvalidFunction" not defined
`,
},
{
ImageContext{
Context: Context{
Format: "{{nil}}",
},
},
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
`,
},
// Table Format
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", false, false),
},
},
`REPOSITORY TAG IMAGE ID CREATED SIZE
image tag1 imageID1 24 hours ago 0B
image tag2 imageID2 N/A 0B
<none> <none> imageID3 24 hours ago 0B
`,
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, false),
},
},
"REPOSITORY\nimage\nimage\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, true),
},
Digest: true,
},
`REPOSITORY DIGEST
image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image <none>
<none> <none>
`,
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", true, false),
},
},
"REPOSITORY\nimage\nimage\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Digest}}", true, false),
},
},
"DIGEST\nsha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf\n<none>\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", true, false),
},
},
"imageID1\nimageID2\nimageID3\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", false, true),
},
Digest: true,
},
`REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
image tag1 sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0B
image tag2 <none> imageID2 N/A 0B
<none> <none> <none> imageID3 24 hours ago 0B
`,
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", true, true),
},
Digest: true,
},
"imageID1\nimageID2\nimageID3\n",
},
// Raw Format
{
ImageContext{
Context: Context{
Format: NewImageFormat("raw", false, false),
},
},
fmt.Sprintf(`repository: image
tag: tag1
image_id: imageID1
created_at: %s
virtual_size: 0B
repository: image
tag: tag2
image_id: imageID2
created_at: %s
virtual_size: 0B
repository: <none>
tag: <none>
image_id: imageID3
created_at: %s
virtual_size: 0B
`, expectedTime, expectedZeroTime, expectedTime),
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("raw", false, true),
},
Digest: true,
},
fmt.Sprintf(`repository: image
tag: tag1
digest: sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image_id: imageID1
created_at: %s
virtual_size: 0B
repository: image
tag: tag2
digest: <none>
image_id: imageID2
created_at: %s
virtual_size: 0B
repository: <none>
tag: <none>
digest: <none>
image_id: imageID3
created_at: %s
virtual_size: 0B
`, expectedTime, expectedZeroTime, expectedTime),
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("raw", true, false),
},
},
`image_id: imageID1
image_id: imageID2
image_id: imageID3
`,
},
// Custom Format
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, false),
},
},
"image\nimage\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, true),
},
Digest: true,
},
"image\nimage\n<none>\n",
},
}
images := []types.ImageSummary{
{ID: "imageID1", RepoTags: []string{"image:tag1"}, RepoDigests: []string{"image@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"}, Created: unixTime},
{ID: "imageID2", RepoTags: []string{"image:tag2"}, Created: zeroTime},
{ID: "imageID3", RepoTags: []string{"<none>:<none>"}, RepoDigests: []string{"<none>@<none>"}, Created: unixTime},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := ImageWrite(tc.context, images)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}
func TestImageContextWriteWithNoImage(t *testing.T) {
out := bytes.NewBufferString("")
images := []types.ImageSummary{}
cases := []struct {
context ImageContext
expected string
}{
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, false),
Output: out,
},
},
"",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, false),
Output: out,
},
},
"REPOSITORY\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, true),
Output: out,
},
},
"",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, true),
Output: out,
},
},
"REPOSITORY DIGEST\n",
},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := ImageWrite(tc.context, images)
assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
// Clean buffer
out.Reset()
})
}
}