blob: b99323d469a49639c9cbf6069a6ebeb59a148c8a [file] [log] [blame]
// Copyright 2023 The Shac 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 reporting
import (
"bytes"
"context"
"errors"
"os"
"strconv"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/mattn/go-colorable"
"go.fuchsia.dev/shac-project/shac/internal/engine"
)
func TestGet(t *testing.T) {
r, err := Get(context.Background())
if err != nil {
t.Fatal(err)
}
if err := r.Close(); err != nil {
t.Fatal(err)
}
}
func TestBasic(t *testing.T) {
buf := bytes.Buffer{}
r := basic{out: &buf}
ctx := context.Background()
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message1", "", "", engine.Span{}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message2", "", "testdata/file.txt", engine.Span{}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message3", "", "testdata/file.txt", engine.Span{Start: engine.Cursor{Line: 10}}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitArtifact(ctx, "mycheck", "", "testdata/file.txt", []byte("content")); err == nil {
t.Fatal("expected failure")
}
start := time.Now()
r.CheckCompleted(ctx, "mycheck", start, time.Millisecond, engine.Notice, nil)
r.CheckCompleted(ctx, "badcheck", start, time.Millisecond, engine.Notice, errors.New("bad"))
r.Print(ctx, "src.star", 12, "debugmsg")
if err := r.Close(); err != nil {
t.Fatal(err)
}
want := "[mycheck/notice] message1\n" +
"[mycheck/notice] testdata/file.txt: message2\n" +
"[mycheck/notice] testdata/file.txt(10): message3\n" +
"- mycheck (Success in 1ms)\n" +
"- badcheck (Success in 1ms): bad\n" +
"[src.star:12] debugmsg\n"
if diff := cmp.Diff(want, buf.String()); diff != "" {
t.Fatalf("mismatch (-want +got):\n%s", diff)
}
}
func TestGitHub(t *testing.T) {
buf := bytes.Buffer{}
r := github{out: &buf}
ctx := context.Background()
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message1", "", "", engine.Span{}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message2", "", "testdata/file.txt", engine.Span{}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message3", "", "testdata/file.txt", engine.Span{Start: engine.Cursor{Line: 10}}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message4", "", "testdata/file.txt", engine.Span{Start: engine.Cursor{Line: 10, Col: 1}}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message5", "", "testdata/file.txt", engine.Span{Start: engine.Cursor{Line: 10}, End: engine.Cursor{Line: 12}}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitAnnotation(ctx, "mycheck", engine.Notice, "message6", "", "testdata/file.txt", engine.Span{Start: engine.Cursor{Line: 10, Col: 1}, End: engine.Cursor{Line: 12, Col: 2}}, nil); err != nil {
t.Fatal(err)
}
if err := r.EmitArtifact(ctx, "mycheck", "", "testdata/file.txt", []byte("content")); err == nil {
t.Fatal("expected failure")
}
start := time.Now()
r.CheckCompleted(ctx, "mycheck", start, time.Millisecond, engine.Notice, nil)
r.CheckCompleted(ctx, "badcheck", start, time.Millisecond, engine.Notice, errors.New("bad"))
r.Print(ctx, "src.star", 12, "debugmsg")
if err := r.Close(); err != nil {
t.Fatal(err)
}
want := "::notice ::title=mycheck::message1\n" +
"::notice ::file=testdata/file.txttitle=mycheck::message2\n" +
"::notice ::file=testdata/file.txt,line=10,title=mycheck::message3\n" +
"::notice ::file=testdata/file.txt,line=10,col=1,title=mycheck::message4\n" +
"::notice ::file=testdata/file.txt,line=10,endLine=12,title=mycheck::message5\n" +
"::notice ::file=testdata/file.txt,line=10,col=1,endLine=12,endCol=2,title=mycheck::message6\n" +
"::debug::[src.star:12] debugmsg\n"
if diff := cmp.Diff(want, buf.String()); diff != "" {
t.Fatalf("mismatch (-want +got):\n%s", diff)
}
}
func TestInteractive_Annotation(t *testing.T) {
t.Parallel()
data := []struct {
l engine.Level
filepath string
span engine.Span
want string
}{
{
engine.Notice,
"",
engine.Span{},
"<R>[<Hc>mycheck<R>/<G>notice<R>] message1\n",
},
{
engine.Warning,
"",
engine.Span{},
"<R>[<Hc>mycheck<R>/<Y>warning<R>] message1\n",
},
{
engine.Error,
"",
engine.Span{},
"<R>[<Hc>mycheck<R>/<Re>error<R>] message1\n",
},
{
engine.Notice,
"file.txt",
engine.Span{},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt: message1\n",
},
{
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 1}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(1): message1\n" +
"\n" +
" <G>This<R>\n" +
" File\n" +
"\n",
},
{
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 3}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(3): message1\n" +
"\n" +
" File\n" +
" <G>Has<R>\n" +
" A\n" +
"\n",
},
{
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 9}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(9): message1\n" +
"\n" +
" Some\n" +
" <G>Content<R>\n" +
" \n" +
"\n",
},
{
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 3}, End: engine.Cursor{Line: 3}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(3): message1\n" +
"\n" +
" File\n" +
" <G>Has<R>\n" +
" A\n" +
"\n",
},
{
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 3}, End: engine.Cursor{Line: 4}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(3): message1\n" +
"\n" +
" File\n" +
" <G>Has<R>\n" +
" <G>A<R>\n" +
" Little\n" +
"\n",
},
{
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 3}, End: engine.Cursor{Line: 5}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(3): message1\n" +
"\n" +
" File\n" +
" <G>Has<R>\n" +
" <G>A<R>\n" +
" <G>Little<R>\n" +
" Bit\n" +
"\n",
},
{
// Span with columns.
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 1, Col: 2}, End: engine.Cursor{Line: 2, Col: 2}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(1): message1\n" +
"\n" +
" T<G>his<R>\n" +
" <G>Fi<R>le\n" +
" Has\n" +
"\n",
},
{
// Intra-line.
engine.Notice,
"file.txt",
engine.Span{Start: engine.Cursor{Line: 5, Col: 2}, End: engine.Cursor{Line: 5, Col: 3}},
"<R>[<Hc>mycheck<R>/<G>notice<R>] file.txt(5): message1\n" +
"\n" +
" A\n" +
" L<G>it<R>tle\n" +
" Bit\n" +
"\n",
},
}
for i, l := range data {
l := l
t.Run(strconv.Itoa(i), func(t *testing.T) {
t.Parallel()
buf := bytes.Buffer{}
// Strip the ANSI codes for now, otherwise it makes the test fairly messy.
// Note that many of the ANSI code are hacked out in ansi_test.go.
r := interactive{out: colorable.NewNonColorable(&buf)}
ctx := context.Background()
if err := r.EmitAnnotation(ctx, "mycheck", l.l, "message1", "testdata", l.filepath, l.span, nil); err != nil {
t.Fatal(err)
}
if err := r.Close(); err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(l.want, buf.String()); diff != "" {
t.Fatalf("mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestInteractive(t *testing.T) {
t.Parallel()
buf := bytes.Buffer{}
// Strip the ANSI codes for now, otherwise it makes the test fairly messy.
// Note that many of the ANSI code are hacked out in ansi_test.go.
r := interactive{out: colorable.NewNonColorable(&buf)}
ctx := context.Background()
if err := r.EmitArtifact(ctx, "mycheck", "", "testdata/file.txt", []byte("content")); err == nil {
t.Fatal("expected failure")
}
start := time.Now()
r.CheckCompleted(ctx, "mycheck", start, time.Millisecond, engine.Notice, nil)
r.CheckCompleted(ctx, "badcheck", start, time.Millisecond, engine.Notice, errors.New("bad"))
r.Print(ctx, "src.star", 12, "debugmsg")
if err := r.Close(); err != nil {
t.Fatal(err)
}
want := "<R>- <G>mycheck<R> (Success in 1ms)\n" +
"<R>- <G>badcheck<R> (Success in 1ms): bad\n" +
"<R>[src.star:12<R>] <B>debugmsg<R>\n"
if diff := cmp.Diff(want, buf.String()); diff != "" {
t.Fatalf("mismatch (-want +got):\n%s", diff)
}
}
func init() {
// Mutate the running environment to make the test deterministic.
os.Unsetenv("LUCI_CONTEXT")
os.Unsetenv("GITHUB_RUN_ID")
os.Unsetenv("VSCODE_GIT_IPC_HANDLE")
os.Setenv("TERM", "dumb")
}