8000 Fix GetChanges issue with changes in both sides by dpordomingo · Pull Request #530 · src-d/lookout · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix GetChanges issue with changes in both sides #530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions cmd/server-test/only_pr_changes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// +build integration

package server_test

import (
"testing"
"time"

fixtures "github.com/src-d/lookout-test-fixtures"
"gopkg.in/src-d/lookout-sdk.v0/pb"

"github.com/stretchr/testify/suite"
)

func TestReviewOnlyPrChangesIntegrationSuite(t *testing.T) {
suite.Run(t, new(reviewOnlyPrChangesIntegrationSuite))
}

type reviewOnlyPrChangesIntegrationSuite struct {
IntegrationSuite
}

func (suite *reviewOnlyPrChangesIntegrationSuite) SetupTest() {
suite.ResetDB()

suite.StoppableCtx()
suite.r, suite.w = suite.StartLookoutd(dummyConfigFile)

suite.StartDummy("--files")
suite.GrepTrue(suite.r, `msg="connection state changed to 'READY'" addr="ipv4://localhost:9930" analyzer=Dummy`)
}

func (suite *reviewOnlyPrChangesIntegrationSuite) TearDownTest() {
// TODO: for integration tests with RabbitMQ we wait a bit so the queue
// is depleted. Ideally this would be done with something similar to ResetDB
time.Sleep(5 * time.Second)
suite.Stop()
}

func (suite *reviewOnlyPrChangesIntegrationSuite) TestAnalyzerErr() {
fixtures := fixtures.GetByName("get-changes-from-outdated-pr")
jsonReviewEvent := &jsonReviewEvent{
ReviewEvent: &pb.ReviewEvent{
InternalID: "1",
Number: 1,
CommitRevision: *fixtures.GetCommitRevision(),
},
}

expectedComments := []string{
`{"analyzer-name":"Dummy","file":"javascript.js",`,
`status=success`,
}
notExpectedComments := []string{
`{"analyzer-name":"Dummy","file":"golang.go",`,
}

suite.sendEvent(jsonReviewEvent.String())
suite.GrepAndNotAll(suite.r, expectedComments, notExpectedComments)
}
25 changes: 15 additions & 10 deletions service/git/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

type CommitLoader interface {
LoadCommits(context.Context, ...lookout.ReferencePointer) (
[]*object.Commit, error)
[]*object.Commit, storer.Storer, error)
}

type LibraryCommitLoader struct {
Expand All @@ -30,31 +30,36 @@ func NewLibraryCommitLoader(l ReposCollectionHandler, s Syncer) *LibraryCommitLo

func (l *LibraryCommitLoader) LoadCommits(
ctx context.Context, rps ...lookout.ReferencePointer) (
[]*object.Commit, error) {
[]*object.Commit, storer.Storer, error) {

if len(rps) == 0 {
return nil, nil
return nil, nil, nil
}

frp := rps[0]
for _, orp := range rps[1:] {
if orp.InternalRepositoryURL != frp.InternalRepositoryURL {
return nil, fmt.Errorf(
return nil, nil, fmt.Errorf(
"loading commits from multiple repositories is not supported")
}
}

if err := l.Syncer.Sync(ctx, rps...); err != nil {
return nil, err
return nil, nil, err
}

r, err := l.Library.GetOrInit(ctx, frp.Repository())
if err != nil {
return nil, err
return nil, nil, err
}

storerCl := NewStorerCommitLoader(r.Storer)
return storerCl.LoadCommits(ctx, rps...)
commits, _, err := storerCl.LoadCommits(ctx, rps...)
if err != nil {
return nil, nil, err
}

return commits, storerCl.Storer, nil
}

type StorerCommitLoader struct {
Expand All @@ -68,17 +73,17 @@ func NewStorerCommitLoader(storer storer.Storer) *StorerCommitLoader {
}

func (l *StorerCommitLoader) LoadCommits(ctx context.Context,
rps ...lookout.ReferencePointer) ([]*object.Commit, error) {
rps ...lookout.ReferencePointer) ([]*object.Commit, storer.Storer, error) {

commits := make([]*object.Commit, len(rps))
for i, rp := range rps {
commit, err := object.GetCommit(l.Storer, plumbing.NewHash(rp.Hash))
if err != nil {
return nil, err
return nil, nil, err
}

commits[i] = commit
}

return commits, nil
return commits, l.Storer, nil
}
15 changes: 8 additions & 7 deletions service/git/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,23 @@ import (
"github.com/stretchr/testify/suite"
git "gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
)

type MockCommitLoader struct {
mock.Mock
}

func (m *MockCommitLoader) LoadCommits(ctx context.Context,
rps ...lookout.ReferencePointer) ([]*object.Commit, error) {
rps ...lookout.ReferencePointer) ([]*object.Commit, storer.Storer, error) {

args := m.Called(ctx, rps)
r0 := args.Get(0)
if r0 == nil {
return nil, args.Error(1)
return nil, nil, args.Error(1)
}

return r0.([]*object.Commit), args.Error(1)
return r0.([]*object.Commit), nil, args.Error(1)
}

type MockSyncer struct {
Expand Down Expand Up @@ -103,7 +104,7 @@ func (s *LibraryCommitLoaderTestSuite) TestErrorOnMultiRepos() {

cl := NewLibraryCommitLoader(&Library{}, &LibrarySyncer{})

commits, err := cl.LoadCommits(context.TODO(), rpsDifferentRepos...)
commits, _, err := cl.LoadCommits(context.TODO(), rpsDifferentRepos...)

require.Nil(commits)
require.Errorf(err, "loading commits from multiple repositories is not supported")
Expand All @@ -120,7 +121,7 @@ func (s *LibraryCommitLoaderTestSuite) TestErrorOnSync() {

cl := NewLibraryCommitLoader(&Library{}, ms)

commits, err := cl.LoadCommits(ctx, rpsSameRepos...)
commits, _, err := cl.LoadCommits(ctx, rpsSameRepos...)

require.Nil(commits)
require.EqualError(err, "sync mock error")
Expand All @@ -140,7 +141,7 @@ func (s *LibraryCommitLoaderTestSuite) TestErrorOnGetOrInit() {

cl := NewLibraryCommitLoader(ml, ms)

commits, err := cl.LoadCommits(ctx, rpsSameRepos...)
commits, _, err := cl.LoadCommits(ctx, rpsSameRepos...)

require.Nil(commits)
require.EqualError(err, "get or init mock error")
Expand All @@ -154,7 +155,7 @@ func (s *LibraryCommitLoaderTestSuite) TestEmpty() {

rps := []lookout.ReferencePointer{}

commits, err := cl.LoadCommits(context.TODO(), rps...)
commits, _, err := cl.LoadCommits(context.TODO(), rps...)

require.Nil(commits)
require.Nil(err)
Expand Down
181 changes: 181 additions & 0 deletions service/git/merge-base/commit_walker_bfs_filtered.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package merge_base

import (
"io"

"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
)

// NewFilterCommitIter returns a CommitIter that walks the commit history,
// starting at the passed commit and visiting its parents in Breadth-first order.
// The commits returned by the CommitIter will validate the passed CommitFilter.
// The history won't be transversed beyond a commit if isLimit is true for it.
// Each commit will be visited only once.
// If the commit history can not be traversed, or the Close() method is called,
// the CommitIter won't return more commits.
// If no isValid is passed, all ancestors of from commit will be valid.
// If no isLimit is limmit, all ancestors of all commits will be visited.
func NewFilterCommitIter(
// REVIEWER: store argument wouldn't be needed if this were part of go-git/plumbing/object package
store storer.EncodedObjectStorer,
from *object.Commit,
isValid *CommitFilter,
isLimit *CommitFilter,
) object.CommitIter {
var validFilter CommitFilter
if isValid == nil {
validFilter = func(_ *object.Commit) bool {
return true
}
} else {
validFilter = *isValid
}

var limitFilter CommitFilter
if isLimit == nil {
limitFilter = func(_ *object.Commit) bool {
return false
}
} else {
limitFilter = *isLimit
}

return &filterCommitIter{
// REVIEWER: store wouldn't be needed if this were part of go-git/plumbing/object package
store: store,
isValid: validFilter,
isLimit: limitFilter,
visited: map[plumbing.Hash]bool{},
queue: []*object.Commit{from},
}
}

// CommitFilter returns a boolean for the passed Commit
type CommitFilter func(*object.Commit) bool

// filterCommitIter implments object.CommitIter
type filterCommitIter struct {
// REVIEWER: store wouldn't be needed if this were part of go-git/plumbing/object package
store storer.EncodedObjectStorer
isValid CommitFilter
isLimit CommitFilter
visited map[plumbing.Hash]bool
queue []*object.Commit
lastErr error
}

// Next returns the next commit of the CommitIter.
// It will return io.EOF if there are no more commits to visit,
// or an error if the history could not be traversed.
func (w *filterCommitIter) Next() (*object.Commit, error) {
var commit *object.Commit
var err error
for {
commit, err = w.popNewFromQueue()
if err != nil {
return nil, w.close(err)
}

w.visited[commit.Hash] = true

if !w.isLimit(commit) {
// REVIEWER: first argument would be commit.s if this were part of object.Commit
err = w.addToQueue(w.store, commit.ParentHashes...)
if err != nil {
return nil, w.close(err)
}
}

if w.isValid(commit) {
return commit, nil
}
}
}

// ForEach runs the passed callback over each Commit returned by the CommitIter
// until the callback returns an error or there is no more commits to traverse.
func (w *filterCommitIter) ForEach(cb func(*object.Commit) error) error {
for {
commit, err := w.Next()
if err == io.EOF {
break
}

if err != nil {
return err
}

if err := cb(commit); err != nil {
return err
}
}

return nil
}

// Error returns the error that caused that the CommitIter is no longer returning commits
func (w *filterCommitIter) Error() error {
return w.lastErr
}

// Close closes the CommitIter
func (w *filterCommitIter) Close() {
w.visited = map[plumbing.Hash]bool{}
w.queue = []*object.Commit{}
w.isLimit = nil
w.isValid = nil
}

// close closes the CommitIter with an error
func (w *filterCommitIter) close(err error) error {
w.Close()
w.lastErr = err
return err
}

// popNewFromQueue returns the first new commit from the internal fifo queue, or
// an io.EOF error if the queue is empty
func (w *filterCommitIter) popNewFromQueue() (*object.Commit, error) {
var first *object.Commit
for {
if len(w.queue) == 0 {
if w.lastErr != nil {
return nil, w.lastErr
}

return nil, io.EOF
}

first = w.queue[0]
w.queue = w.queue[1:]
if w.visited[first.Hash] {
continue
}

return first, nil
}
}

// addToQueue adds the passed commits to the internal fifo queue if they weren'r already seen
// or returns an error if the passed hashes could not be used to get valid commits
func (w *filterCommitIter) addToQueue(
store storer.EncodedObjectStorer,
hashes ...plumbing.Hash,
) error {
for _, hash := range hashes {
if w.visited[hash] {
continue
}

commit, err := object.GetCommit(store, hash)
if err != nil {
return err
}

w.queue = append(w.queue, commit)
}

return nil
}
Loading
0