blob: 7021226a65090b4cc3db50a81153a979f43597d7 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"testing"
"github.com/golang/protobuf/jsonpb"
. "github.com/smartystreets/goconvey/convey"
ds "go.chromium.org/luci/gae/service/datastore"
tq "go.chromium.org/luci/gae/service/taskqueue"
admin "infra/tricium/api/admin/v1"
tricium "infra/tricium/api/v1"
"infra/tricium/appengine/common"
gc "infra/tricium/appengine/common/gerrit"
"infra/tricium/appengine/common/track"
"infra/tricium/appengine/common/triciumtest"
)
func TestReportResultsRequest(t *testing.T) {
Convey("Test Environment", t, func() {
ctx := triciumtest.Context()
// Add request -> run -> function -> worker to datastore.
functionName := "MyLinter"
request := &track.AnalyzeRequest{
GitURL: "https://chromium-review.googlesource.com",
GitRef: "refs/changes/88/508788/1",
Files: []tricium.Data_File{
{Path: "dir/file.txt"},
},
}
So(ds.Put(ctx, request), ShouldBeNil)
requestKey := ds.KeyForObj(ctx, request)
run := &track.WorkflowRun{ID: 1, Parent: requestKey}
So(ds.Put(ctx, run), ShouldBeNil)
runKey := ds.KeyForObj(ctx, run)
So(ds.Put(ctx, &track.FunctionRun{
ID: "MyLinter",
Parent: runKey,
}), ShouldBeNil)
analyzerKey := ds.NewKey(ctx, "FunctionRun", "MyLinter", 0, runKey)
So(ds.Put(ctx, &track.FunctionRunResult{
ID: 1,
Parent: analyzerKey,
Name: "MyLinter",
NumComments: 2,
}), ShouldBeNil)
workerName := "MyLinter_UBUNTU"
So(ds.Put(ctx, &track.WorkerRun{
ID: workerName,
Parent: analyzerKey,
}), ShouldBeNil)
// Add example Comment and associated CommentSelection entities.
workerKey := ds.NewKey(ctx, "WorkerRun", workerName, 0, analyzerKey)
changedLines := gc.ChangedLinesInfo{
"dir/file.txt": {2, 5, 6},
}
dataCommentDeleted := tricium.Data_Comment{
Category: "L",
Message: "Line too long",
Path: "dir/deleted_file.txt",
}
deletedFileCommentJSON, err := (&jsonpb.Marshaler{}).MarshalToString(&dataCommentDeleted)
dataComment := tricium.Data_Comment{
Category: "L",
Message: "Line too short",
Path: "dir/file.txt",
StartLine: 2,
EndLine: 3,
}
inChangeCommentJSON, err := (&jsonpb.Marshaler{}).MarshalToString(&dataComment)
So(err, ShouldBeNil)
comments := []*track.Comment{
{Parent: workerKey, Comment: []byte(deletedFileCommentJSON)},
{Parent: workerKey, Comment: []byte(inChangeCommentJSON)},
{Parent: workerKey, Comment: []byte(inChangeCommentJSON)},
}
So(ds.Put(ctx, comments), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, comments[0]),
Included: true,
}), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1, Parent: ds.KeyForObj(ctx, comments[1]),
Included: true,
}), ShouldBeNil)
// The third comment added is not "included" when reporting
// comments.
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, comments[2]),
Included: false,
}), ShouldBeNil)
Convey("Reports only included comments", func() {
mock := &gc.MockRestAPI{ChangedLines: changedLines}
err = reportResults(ctx, &admin.ReportResultsRequest{
RunId: run.ID,
Analyzer: "MyLinter",
}, mock)
So(err, ShouldBeNil)
// This only includes the two selected comments.
So(len(mock.LastComments), ShouldEqual, len(comments)-1)
Convey("A successful request also sends a row to BQ", func() {
So(len(tq.GetTestable(ctx).GetScheduledTasks()[common.FeedbackEventsQueue]), ShouldEqual, 1)
})
})
Convey("Does not report results when reporting is disabled", func() {
request.GerritReportingDisabled = true
So(ds.Put(ctx, request), ShouldBeNil)
mock := &gc.MockRestAPI{ChangedLines: changedLines}
err = reportResults(ctx, &admin.ReportResultsRequest{
RunId: run.ID,
Analyzer: functionName,
}, mock)
So(err, ShouldBeNil)
So(len(mock.LastComments), ShouldEqual, 0)
Convey("When no comments are posted, no rows are sent to BQ", func() {
So(len(tq.GetTestable(ctx).GetScheduledTasks()[common.FeedbackEventsQueue]), ShouldEqual, 0)
})
})
// Put more comments in until the number of included comments
// has reached the maximum comment number threshold.
for len(comments) < maxComments+1 {
comment := &track.Comment{Parent: workerKey, Comment: []byte(deletedFileCommentJSON)}
comments = append(comments, comment)
So(ds.Put(ctx, comment), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, comment),
Included: true,
}), ShouldBeNil)
}
So(len(comments), ShouldEqual, maxComments+1)
Convey("Reports when number of comments is at maximum", func() {
mock := &gc.MockRestAPI{ChangedLines: changedLines}
err = reportResults(ctx, &admin.ReportResultsRequest{
RunId: run.ID,
Analyzer: functionName,
}, mock)
So(err, ShouldBeNil)
So(len(mock.LastComments), ShouldEqual, maxComments)
})
// This comment is not on a changed line.
dataOutside := tricium.Data_Comment{
Category: "L",
Message: "Line too short",
Path: "dir/file.txt",
StartLine: 3,
EndLine: 3,
}
outsideChangeCommentJSON, err := (&jsonpb.Marshaler{}).MarshalToString(&dataOutside)
// Put the new comment with line numbers in.
outsideChangeComment := &track.Comment{Parent: workerKey, Comment: []byte(outsideChangeCommentJSON)}
comments = append(comments, outsideChangeComment)
So(ds.Put(ctx, outsideChangeComment), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, outsideChangeComment),
Included: gc.CommentIsInChangedLines(ctx, &dataOutside, changedLines, 0),
}), ShouldBeNil)
So(len(comments), ShouldEqual, maxComments+2)
Convey("Does not report comments that are not on changed lines", func() {
mock := &gc.MockRestAPI{ChangedLines: changedLines}
err := reportResults(ctx, &admin.ReportResultsRequest{
RunId: run.ID,
Analyzer: functionName,
}, mock)
So(err, ShouldBeNil)
So(len(mock.LastComments), ShouldEqual, maxComments)
})
// Put one more comment in;
comment := &track.Comment{Parent: workerKey, Comment: []byte(deletedFileCommentJSON)}
comments = append(comments, comment)
So(ds.Put(ctx, comment), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, comment),
Included: gc.CommentIsInChangedLines(ctx, &dataCommentDeleted, changedLines, 0),
}), ShouldBeNil)
So(len(comments), ShouldEqual, maxComments+3)
Convey("Does not report when number of comments exceeds maximum", func() {
mock := &gc.MockRestAPI{ChangedLines: changedLines}
err := reportResults(ctx, &admin.ReportResultsRequest{
RunId: run.ID,
Analyzer: functionName,
}, mock)
So(err, ShouldBeNil)
So(len(mock.LastComments), ShouldEqual, 0)
})
})
}
func TestReportResultsRequestWithRenamedOrCopiedFiles(t *testing.T) {
Convey("Test Environment", t, func() {
ctx := triciumtest.Context()
// Add request -> run -> function -> worker to datastore.
request := &track.AnalyzeRequest{
GitURL: "https://chromium-review.googlesource.com",
GitRef: "refs/changes/88/508788/1",
Files: []tricium.Data_File{
{Path: "dir/file.txt"},
{Path: "dir/renamed_file.txt", Status: tricium.Data_RENAMED},
{Path: "dir/copied_file.txt", Status: tricium.Data_COPIED},
},
}
So(ds.Put(ctx, request), ShouldBeNil)
requestKey := ds.KeyForObj(ctx, request)
run := &track.WorkflowRun{ID: 1, Parent: requestKey}
So(ds.Put(ctx, run), ShouldBeNil)
runKey := ds.KeyForObj(ctx, run)
So(ds.Put(ctx, &track.FunctionRun{
ID: "MyLinter",
Parent: runKey,
}), ShouldBeNil)
analyzerKey := ds.NewKey(ctx, "FunctionRun", "MyLinter", 0, runKey)
So(ds.Put(ctx, &track.FunctionRunResult{
ID: 1,
Parent: analyzerKey,
Name: "MyLinter",
NumComments: 2,
}), ShouldBeNil)
workerName := "MyLinter_UBUNTU"
So(ds.Put(ctx, &track.WorkerRun{
ID: workerName,
Parent: analyzerKey,
}), ShouldBeNil)
// Add example Comment and associated CommentSelection entities.
workerKey := ds.NewKey(ctx, "WorkerRun", workerName, 0, analyzerKey)
changedLines := gc.ChangedLinesInfo{
"dir/file.txt": {2, 5, 6},
"dir/renamed_file.txt": {1, 2, 3, 4, 5, 6, 7},
"dir/copied_file.txt": {1, 2, 3, 4, 5, 6, 7},
}
gc.FilterRequestChangedLines(request, &changedLines)
dataCommentChanged := tricium.Data_Comment{
Category: "L",
Message: "Line too short",
Path: "dir/file.txt",
StartLine: 2,
EndLine: 3,
}
inChangeCommentJSON, err := (&jsonpb.Marshaler{}).MarshalToString(&dataCommentChanged)
dataCommentRenamed := tricium.Data_Comment{
Category: "L",
Message: "Line doesn't look right",
Path: "dir/renamed_file.txt",
StartLine: 2,
EndLine: 3,
}
inRenamedFileCommentJSON, err := (&jsonpb.Marshaler{}).MarshalToString(&dataCommentRenamed)
dataCommentCopied := tricium.Data_Comment{
Category: "L",
Message: "Line doesn't look right",
Path: "dir/copied_file.txt",
StartLine: 2,
EndLine: 3,
}
inCopiedFileCommentJSON, err := (&jsonpb.Marshaler{}).MarshalToString(&dataCommentCopied)
So(err, ShouldBeNil)
comments := []*track.Comment{
{Parent: workerKey, Comment: []byte(inChangeCommentJSON)},
{Parent: workerKey, Comment: []byte(inRenamedFileCommentJSON)},
{Parent: workerKey, Comment: []byte(inCopiedFileCommentJSON)},
}
So(ds.Put(ctx, comments), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, comments[0]),
Included: gc.CommentIsInChangedLines(ctx, &dataCommentChanged, changedLines, 0),
}), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, comments[1]),
Included: gc.CommentIsInChangedLines(ctx, &dataCommentRenamed, changedLines, 0),
}), ShouldBeNil)
So(ds.Put(ctx, &track.CommentSelection{
ID: 1,
Parent: ds.KeyForObj(ctx, comments[2]),
Included: gc.CommentIsInChangedLines(ctx, &dataCommentCopied, changedLines, 0),
}), ShouldBeNil)
Convey("Does not report comments in renamed or copied files", func() {
mock := &gc.MockRestAPI{ChangedLines: changedLines}
err := reportResults(ctx, &admin.ReportResultsRequest{
RunId: run.ID,
Analyzer: "MyLinter",
}, mock)
So(err, ShouldBeNil)
So(len(mock.LastComments), ShouldEqual, 1)
})
})
}