blob: bc2526d415091d2575c5a41802037b50a3fff292 [file] [log] [blame]
// Copyright 2019 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"bytes"
"regexp"
"strings"
"testing"
"time"
"github.com/golang/protobuf/proto"
"github.com/mgutz/ansi"
"google.golang.org/protobuf/encoding/protojson"
"go.chromium.org/luci/common/clock/testclock"
pb "go.chromium.org/luci/buildbucket/proto"
. "github.com/smartystreets/goconvey/convey"
)
var buildJSON = `
{
"id": "8917899588926498064",
"builder": {
"project": "chromium",
"bucket": "try",
"builder": "linux-rel"
},
"number": 1,
"createdBy": "user:5071639625-1lppvbtck1morgivc6sq4dul7klu27sd@developer.gserviceaccount.com",
"createTime": "2019-03-26T18:33:47.958975Z",
"startTime": "2019-03-26T18:33:52.447816Z",
"endTime": "2019-03-26T18:37:13.892433Z",
"updateTime": "2019-03-26T18:37:14.629154Z",
"status": "SUCCESS",
"summaryMarkdown": "it was ok",
"canary": true,
"input": {
"experimental": true,
"experiments": ["luci.buildbucket.canary_software", "luci.non_production"],
"properties": {
"$recipe_engine/cq": {
"dry_run": true
},
"$recipe_engine/runtime": {
"is_experimental": false
},
"blamelist": [
"hinoka@chromium.org"
],
"buildername": "Luci-go Mac Tester",
"category": "cq"
},
"gitilesCommit": {
"host": "chromium.googlesource.com",
"project": "infra/luci/luci-go",
"ref": "refs/heads/x",
"id": "deadbeef"
},
"gerritChanges": [
{
"host": "chromium-review.googlesource.com",
"project": "infra/luci/luci-go",
"change": "1539021",
"patchset": "1"
}
]
},
"output": {
"properties": {
"$recipe_engine/cq": {
"dry_run": true
},
"bot_id": "build234-m9"
}
},
"steps": [
{
"name": "first",
"startTime": "2019-03-26T18:34:01.896053Z",
"endTime": "2019-03-26T18:34:01.954986Z",
"status": "SUCCESS",
"logs": [
{
"name": "stdout",
"viewUrl": "https://logs.example.com/first/stdout"
},
{
"name": "stderr",
"viewUrl": "https://logs.example.com/first/stderr"
}
],
"summaryMarkdown": "first summary markdown\nsummary second line"
},
{
"name": "second",
"startTime": "2019-03-26T18:34:01.896053Z",
"endTime": "2019-03-26T18:34:01.954986Z",
"status": "SUCCESS",
"logs": [
{
"name": "stdout",
"viewUrl": "https://logs.example.com/second/stdout"
},
{
"name": "stderr",
"viewUrl": "https://logs.example.com/second/stderr"
}
]
}
],
"infra": {
"buildbucket": {
"serviceConfigRevision": "80aefcf1d2f6c25894a968312eca86ba0f80737c"
},
"swarming": {
"hostname": "chromium-swarm.appspot.com",
"taskId": "43d4192e94d9ff10",
"taskServiceAccount": "infra-try-builder@chops-service-accounts.iam.gserviceaccount.com"
},
"logdog": {
"hostname": "logs.chromium.org",
"project": "infra",
"prefix": "buildbucket/cr-buildbucket.appspot.com/8917899588926498064"
}
},
"tags": [
{
"key": "buildset",
"value": "patch/gerrit/chromium-review.googlesource.com/1539021/1"
},
{
"key": "cq_experimental",
"value": "false"
},
{
"key": "user_agent",
"value": "cq"
}
]
}`
const expectedBuildPrintedTemplate = `<white+b><white+u><green+h>http://ci.chromium.org/b/8917899588926498064<reset><white+b><green+h> SUCCESS 'chromium/try/linux-rel/1'<reset>
<white+b>Summary<reset>: it was ok
<white+b>Experimental<reset> <white+b>Canary<reset>
<white+b>Created<reset> on 2019-03-26 at 18:33:47, <white+b>waited<reset> 4.5s, <white+b>started<reset> at 18:33:52, <white+b>ran<reset> for 3m21s, <white+b>ended<reset> at 18:37:13
<white+b>By<reset>: user:5071639625-1lppvbtck1morgivc6sq4dul7klu27sd@developer.gserviceaccount.com
<white+b>Commit<reset>: <white+u>https://crrev.com/deadbeef<reset> on refs/heads/x
<white+b>CL<reset>: <white+u>https://crrev.com/c/1539021/1<reset>
<white+b>Tag<reset>: buildset:patch/gerrit/chromium-review.googlesource.com/1539021/1
<white+b>Tag<reset>: cq_experimental:false
<white+b>Tag<reset>: user_agent:cq
<white+b>Input properties<reset>: {
"$recipe_engine/cq": {
"dry_run": true
},
"$recipe_engine/runtime": {
"is_experimental": false
},
"blamelist": [
"hinoka@chromium.org"
],
"buildername": "Luci-go Mac Tester",
"category": "cq"
}
<white+b>Output properties<reset>: {
"$recipe_engine/cq": {
"dry_run": true
},
"bot_id": "build234-m9"
}
<white+b>Experiments<reset>:
luci.buildbucket.canary_software
luci.non_production
<green+h>Step "first" SUCCESS 59ms Logs: "stdout", "stderr"<reset>
first summary markdown
summary second line
<green+h>Step "second" SUCCESS 59ms Logs: "stdout", "stderr"<reset>
`
func ansifyTemplate(template string) string {
return regexp.MustCompile("<[^>]+>").ReplaceAllStringFunc(
template,
func(style string) string {
style = strings.TrimPrefix(style, "<")
style = strings.TrimSuffix(style, ">")
return ansi.ColorCode(style)
})
}
func TestPrint(t *testing.T) {
Convey("Print", t, func() {
buf := &bytes.Buffer{}
p := newPrinter(buf, false, func() time.Time {
return testclock.TestRecentTimeUTC
})
Convey("Build", func() {
build := &pb.Build{}
So(protojson.Unmarshal([]byte(buildJSON), build), ShouldBeNil)
expectedBuildPrinted := ansifyTemplate(expectedBuildPrintedTemplate)
p.Build(build)
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, expectedBuildPrinted)
})
Convey("Partial build", func() {
// Only Id, Status and Builder are available
build := &pb.Build{
Id: 8917899588926498064,
Status: pb.Status_STARTED,
Builder: &pb.BuilderID{
Project: "chromium",
Bucket: "try",
Builder: "linux-rel",
},
}
expectedBuildPrinted := ansifyTemplate("<white+b><white+u><yellow+h>http://ci.chromium.org/b/8917899588926498064<reset><white+b><yellow+h> STARTED 'chromium/try/linux-rel'<reset>\n")
p.Build(build)
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, expectedBuildPrinted)
})
Convey("Build error when any of required fields is missing", func() {
validBuild := &pb.Build{
Id: 8917899588926498064,
Status: pb.Status_STARTED,
Builder: &pb.BuilderID{
Project: "chromium",
Bucket: "try",
Builder: "linux-rel",
},
}
// Make sure Build can be printed
p.Build(validBuild)
So(p.Err, ShouldBeNil)
buildWithoutID := proto.Clone(validBuild).(*pb.Build)
buildWithoutID.Id = 0
So(func() { p.Build(buildWithoutID) }, ShouldPanic)
buildWithoutStatus := proto.Clone(validBuild).(*pb.Build)
buildWithoutStatus.Status = pb.Status_STATUS_UNSPECIFIED
So(func() { p.Build(buildWithoutStatus) }, ShouldPanic)
buildWithoutBuilder := proto.Clone(validBuild).(*pb.Build)
buildWithoutBuilder.Builder = nil
So(func() { p.Build(buildWithoutBuilder) }, ShouldPanic)
})
Convey("Chromium commit", func() {
p.commit(&pb.GitilesCommit{
Host: "chromium.googlesource.com",
Project: "chromium/src",
Ref: "refs/heads/x",
Id: "deadbeef",
})
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, ansi.Color("https://crrev.com/deadbeef", "white+u")+" on refs/heads/x")
})
Convey("Chromium commit without id", func() {
p.commit(&pb.GitilesCommit{
Host: "chromium.googlesource.com",
Project: "infra/luci/luci-go",
Ref: "refs/heads/x",
})
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, ansi.Color("https://chromium.googlesource.com/infra/luci/luci-go/+/refs/heads/x", "white+u"))
})
Convey("Arbitrary commit", func() {
p.commit(&pb.GitilesCommit{
Host: "fuchsia.googlesource.com",
Project: "infra",
Ref: "refs/heads/x",
Id: "deadbeef",
})
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, ansi.Color("https://fuchsia.googlesource.com/infra/+/deadbeef", "white+u")+" on refs/heads/x")
})
Convey("Arbitrary commit without id", func() {
p.commit(&pb.GitilesCommit{
Host: "fuchsia.googlesource.com",
Project: "infra",
Ref: "refs/heads/x",
})
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, ansi.Color("https://fuchsia.googlesource.com/infra/+/refs/heads/x", "white+u"))
})
Convey("Chromium CL", func() {
p.change(&pb.GerritChange{
Host: "chromium-review.googlesource.com",
Project: "infra/luci/luci-go",
Change: 123,
Patchset: 4,
})
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, ansi.Color("https://crrev.com/c/123/4", "white+u"))
})
Convey("Chrome CL", func() {
p.change(&pb.GerritChange{
Host: "chrome-internal-review.googlesource.com",
Project: "secret",
Change: 123,
Patchset: 4,
})
So(p.Err, ShouldBeNil)
So(buf.String(), ShouldEqual, ansi.Color("https://crrev.com/i/123/4", "white+u"))
})
})
}