Skip to content
This repository was archived by the owner on Jun 16, 2020. It is now read-only.

Commit c376422

Browse files
committed
Test commit status monitoring
1 parent 71fb5ac commit c376422

File tree

2 files changed

+210
-68
lines changed

2 files changed

+210
-68
lines changed

cache/cache.go

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,7 @@ type SourceProvider interface {
4343
}
4444

4545
// Poll provider at increasing interval for the URL of statuses associated to "ref"
46-
func monitorRefStatuses(ctx context.Context, p SourceProvider, url string, ref string, commitc chan<- Commit) error {
47-
b := backoff.ExponentialBackOff{
48-
InitialInterval: 10 * time.Second,
49-
RandomizationFactor: backoff.DefaultRandomizationFactor,
50-
Multiplier: backoff.DefaultMultiplier,
51-
MaxInterval: 2 * time.Minute,
52-
MaxElapsedTime: 0,
53-
Clock: backoff.SystemClock,
54-
}
46+
func monitorRefStatuses(ctx context.Context, p SourceProvider, b backoff.ExponentialBackOff, url string, ref string, commitc chan<- Commit) error {
5547
b.Reset()
5648

5749
commit, err := p.Commit(ctx, url, ref)
@@ -644,7 +636,7 @@ func (c *Cache) broadcastMonitorPipeline(ctx context.Context, u string, ref stri
644636
// Ask all providers to monitor the statuses of 'ref'. The URL of each status is written on the
645637
// channel urlc once. If no provider is able to handle the specified URL, ErrUnknownRepositoryURL
646638
// is returned.
647-
func (c *Cache) broadcastMonitorRefStatus(ctx context.Context, repo string, ref string, commitc chan<- Commit) error {
639+
func (c *Cache) broadcastMonitorRefStatus(ctx context.Context, repo string, ref string, commitc chan<- Commit, b backoff.ExponentialBackOff) error {
648640
repositoryURLs, commit, err := GitOriginURL(repo, ref)
649641
switch err {
650642
case nil:
@@ -664,13 +656,15 @@ func (c *Cache) broadcastMonitorRefStatus(ctx context.Context, repo string, ref
664656
errc := make(chan error)
665657
ctx, cancel := context.WithCancel(ctx)
666658
wg := sync.WaitGroup{}
659+
requestCount := 0
667660
for _, u := range repositoryURLs {
668661
for _, p := range c.sourceProviders {
662+
requestCount++
669663
wg.Add(1)
670-
go func(p SourceProvider) {
664+
go func(p SourceProvider, u string) {
671665
defer wg.Done()
672-
errc <- monitorRefStatuses(ctx, p, u, ref, commitc)
673-
}(p)
666+
errc <- monitorRefStatuses(ctx, p, b, u, ref, commitc)
667+
}(p, u)
674668
}
675669
}
676670

@@ -679,34 +673,43 @@ func (c *Cache) broadcastMonitorRefStatus(ctx context.Context, repo string, ref
679673
close(errc)
680674
}()
681675

682-
var n int
676+
errCounts := struct {
677+
nil int
678+
url int
679+
ref int
680+
other int
681+
}{}
682+
683683
var canceled = false
684684
for e := range errc {
685685
if !canceled {
686686
// ErrUnknownRepositoryURL and ErrUnknownGitReference are returned if
687687
// all providers fail with one of these errors
688688
switch e {
689+
case nil:
690+
errCounts.nil++
689691
case ErrUnknownRepositoryURL:
690-
n++
691-
if err == nil {
692-
err = e
693-
}
692+
errCounts.url++
694693
case ErrUnknownGitReference:
695-
n++
696-
// ErrUnknownGitReference must be returned over ErrUnknownRepositoryURL
697-
// since it means the repository was found but the reference was not
698-
if err == nil || err == ErrUnknownRepositoryURL {
699-
err = e
700-
}
694+
errCounts.ref++
701695
default:
702696
// Artificially trigger cancellation
703-
n = len(c.sourceProviders)
704-
err = e
697+
errCounts.other += requestCount
705698
}
706699

707-
if canceled = n >= len(c.sourceProviders); canceled {
700+
canceled = (errCounts.nil + errCounts.url + errCounts.ref + errCounts.other) >= requestCount
701+
if canceled {
708702
cancel()
709-
err = e
703+
switch {
704+
case errCounts.other > 0:
705+
err = e
706+
case errCounts.nil > 0:
707+
err = nil
708+
case errCounts.ref > 0:
709+
err = ErrUnknownGitReference
710+
case errCounts.url > 0:
711+
err = ErrUnknownRepositoryURL
712+
}
710713
}
711714
}
712715
}
@@ -724,13 +727,22 @@ func (c *Cache) MonitorPipelines(ctx context.Context, repositoryURL string, ref
724727
ctx, cancel := context.WithCancel(ctx)
725728
wg := sync.WaitGroup{}
726729

730+
b := backoff.ExponentialBackOff{
731+
InitialInterval: 10 * time.Second,
732+
RandomizationFactor: backoff.DefaultRandomizationFactor,
733+
Multiplier: backoff.DefaultMultiplier,
734+
MaxInterval: 2 * time.Minute,
735+
MaxElapsedTime: 0,
736+
Clock: backoff.SystemClock,
737+
}
738+
727739
wg.Add(1)
728740
go func() {
729741
defer wg.Done()
730742
defer close(commitc)
731743
// This gives us a stream of commits with a 'Statuses' attribute that may contain
732744
// URLs refering to CI pipelines
733-
errc <- c.broadcastMonitorRefStatus(ctx, repositoryURL, ref, commitc)
745+
errc <- c.broadcastMonitorRefStatus(ctx, repositoryURL, ref, commitc, b)
734746
}()
735747

736748
wg.Add(1)

cache/cache_test.go

Lines changed: 169 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package cache
22

33
import (
4+
"context"
45
"fmt"
56
"io/ioutil"
67
"os"
78
"path"
89
"sort"
10+
"strings"
911
"testing"
1012
"time"
1113

14+
"github.com/cenkalti/backoff/v3"
1215
"github.com/google/go-cmp/cmp"
1316
"gopkg.in/src-d/go-git.v4"
1417
"gopkg.in/src-d/go-git.v4/config"
@@ -487,49 +490,49 @@ func sortPipelines(pipelines []Pipeline) {
487490
})
488491
}
489492

490-
func TestGitOriginURL(t *testing.T) {
491-
setup := func(t *testing.T, remotes []config.RemoteConfig) (string, string) {
492-
tmpDir, err := ioutil.TempDir("", "")
493-
if err != nil {
494-
t.Fatal(err)
495-
}
496-
repo, err := git.PlainInit(tmpDir, false)
497-
if err != nil {
498-
t.Fatal(err)
499-
}
500-
501-
for _, remoteConfig := range remotes {
502-
if _, err := repo.CreateRemote(&remoteConfig); err != nil {
503-
t.Fatal(err)
504-
}
505-
}
493+
func createRepository(t *testing.T, remotes []config.RemoteConfig) (string, string) {
494+
tmpDir, err := ioutil.TempDir("", "")
495+
if err != nil {
496+
t.Fatal(err)
497+
}
498+
repo, err := git.PlainInit(tmpDir, false)
499+
if err != nil {
500+
t.Fatal(err)
501+
}
506502

507-
// Populate repository with single commit
508-
w, err := repo.Worktree()
509-
if err != nil {
510-
t.Fatal(err)
511-
}
512-
if err := ioutil.WriteFile(path.Join(tmpDir, "file.txt"), []byte("abcd"), os.ModeAppend); err != nil {
513-
t.Fatal(err)
514-
}
515-
sha, err := w.Commit("message", &git.CommitOptions{
516-
Author: &object.Signature{
517-
Name: "name",
518-
Email: "email",
519-
When: time.Date(2019, 19, 12, 21, 49, 0, 0, time.UTC),
520-
},
521-
})
522-
if err != nil {
503+
for _, remoteConfig := range remotes {
504+
if _, err := repo.CreateRemote(&remoteConfig); err != nil {
523505
t.Fatal(err)
524506
}
507+
}
525508

526-
if _, err := repo.CreateTag("0.1.0", sha, nil); err != nil {
527-
t.Fatal(err)
528-
}
509+
// Populate repository with single commit
510+
w, err := repo.Worktree()
511+
if err != nil {
512+
t.Fatal(err)
513+
}
514+
if err := ioutil.WriteFile(path.Join(tmpDir, "file.txt"), []byte("abcd"), os.ModeAppend); err != nil {
515+
t.Fatal(err)
516+
}
517+
sha, err := w.Commit("message", &git.CommitOptions{
518+
Author: &object.Signature{
519+
Name: "name",
520+
Email: "email",
521+
When: time.Date(2019, 19, 12, 21, 49, 0, 0, time.UTC),
522+
},
523+
})
524+
if err != nil {
525+
t.Fatal(err)
526+
}
529527

530-
return tmpDir, sha.String()
528+
if _, err := repo.CreateTag("0.1.0", sha, nil); err != nil {
529+
t.Fatal(err)
531530
}
532531

532+
return tmpDir, sha.String()
533+
}
534+
535+
func TestGitOriginURL(t *testing.T) {
533536
t.Run("invalid path", func(t *testing.T) {
534537
_, _, err := GitOriginURL("invalid path", "HEAD")
535538
if err != ErrUnknownRepositoryURL {
@@ -538,7 +541,7 @@ func TestGitOriginURL(t *testing.T) {
538541
})
539542

540543
t.Run("invalid path in git repository", func(t *testing.T) {
541-
repositoryPath, _ := setup(t, nil)
544+
repositoryPath, _ := createRepository(t, nil)
542545
defer os.RemoveAll(repositoryPath)
543546

544547
_, _, err := GitOriginURL(path.Join(repositoryPath, "invalidpath"), "HEAD")
@@ -560,7 +563,7 @@ func TestGitOriginURL(t *testing.T) {
560563
Fetch: nil,
561564
},
562565
}
563-
repositoryPath, _ := setup(t, remotes)
566+
repositoryPath, _ := createRepository(t, remotes)
564567
defer os.RemoveAll(repositoryPath)
565568

566569
urls, _, err := GitOriginURL(repositoryPath, "HEAD")
@@ -575,7 +578,7 @@ func TestGitOriginURL(t *testing.T) {
575578
})
576579

577580
t.Run("commit references", func(t *testing.T) {
578-
repositoryPath, sha := setup(t, nil)
581+
repositoryPath, sha := createRepository(t, nil)
579582
defer os.RemoveAll(repositoryPath)
580583

581584
expectedCommit := Commit{
@@ -609,5 +612,132 @@ func TestGitOriginURL(t *testing.T) {
609612
})
610613
}
611614
})
615+
}
612616

617+
type testProvider struct {
618+
id string
619+
url string
620+
callNumber int
621+
}
622+
623+
func (p testProvider) ID() string { return p.id }
624+
625+
func (p *testProvider) RefStatuses(ctx context.Context, url, ref, sha string) ([]string, error) {
626+
if !strings.Contains(url, p.url) {
627+
return nil, ErrUnknownRepositoryURL
628+
}
629+
switch p.callNumber++; p.callNumber {
630+
case 1:
631+
return []string{url + "_status0"}, nil
632+
case 2:
633+
return []string{url + "_status0", url + "_status1"}, nil
634+
default:
635+
return []string{url + "_status0", url + "_status1", url + "_status2"}, nil
636+
}
637+
}
638+
639+
func (p testProvider) Commit(ctx context.Context, repo, sha string) (Commit, error) {
640+
if !strings.Contains(repo, p.url) {
641+
return Commit{}, ErrUnknownRepositoryURL
642+
}
643+
return Commit{}, nil
644+
}
645+
646+
func TestCache_monitorRefStatus(t *testing.T) {
647+
ctx := context.Background()
648+
p := testProvider{"provider", "url", 0}
649+
commitc := make(chan Commit)
650+
errc := make(chan error)
651+
652+
b := backoff.ExponentialBackOff{
653+
InitialInterval: time.Millisecond,
654+
RandomizationFactor: backoff.DefaultRandomizationFactor,
655+
Multiplier: backoff.DefaultMultiplier,
656+
MaxInterval: 10 * time.Millisecond,
657+
MaxElapsedTime: 10 * time.Millisecond,
658+
Clock: backoff.SystemClock,
659+
}
660+
661+
go func() {
662+
err := monitorRefStatuses(ctx, &p, b, "url", "ref", commitc)
663+
close(commitc)
664+
errc <- err
665+
close(errc)
666+
}()
667+
668+
var c Commit
669+
for c = range commitc {
670+
}
671+
672+
if err := <-errc; err != nil {
673+
t.Fatal(err)
674+
}
675+
676+
statuses := []string{"url_status0", "url_status1", "url_status2"}
677+
if diff := cmp.Diff(c.Statuses, statuses); len(diff) > 0 {
678+
t.Fatal(diff)
679+
}
680+
}
681+
682+
func TestCache_broadcastMonitorRefStatus(t *testing.T) {
683+
ctx := context.Background()
684+
c := NewCache(nil, []SourceProvider{
685+
&testProvider{"origin", "origin", 0},
686+
&testProvider{"other", "other", 0},
687+
})
688+
689+
repositoryPath, sha := createRepository(t, []config.RemoteConfig{
690+
{
691+
Name: "origin",
692+
URLs: []string{"origin1", "origin2"},
693+
},
694+
{
695+
Name: "other",
696+
URLs: []string{"other1"},
697+
},
698+
})
699+
700+
commitc := make(chan Commit)
701+
errc := make(chan error)
702+
b := backoff.ExponentialBackOff{
703+
InitialInterval: time.Millisecond,
704+
RandomizationFactor: backoff.DefaultRandomizationFactor,
705+
Multiplier: backoff.DefaultMultiplier,
706+
MaxInterval: 10 * time.Millisecond,
707+
MaxElapsedTime: 10 * time.Millisecond,
708+
Clock: backoff.SystemClock,
709+
}
710+
711+
go func() {
712+
err := c.broadcastMonitorRefStatus(ctx, repositoryPath, sha, commitc, b)
713+
close(commitc)
714+
errc <- err
715+
close(errc)
716+
}()
717+
718+
statuses := make(map[string]struct{}, 0)
719+
for commit := range commitc {
720+
for _, status := range commit.Statuses {
721+
statuses[status] = struct{}{}
722+
}
723+
}
724+
725+
if err := <-errc; err != nil {
726+
t.Fatal(err)
727+
}
728+
729+
expectedStatuses := map[string]struct{}{
730+
"origin1_status0": {},
731+
"origin1_status1": {},
732+
"origin1_status2": {},
733+
"origin2_status0": {},
734+
"origin2_status1": {},
735+
"origin2_status2": {},
736+
"other1_status0": {},
737+
"other1_status1": {},
738+
"other1_status2": {},
739+
}
740+
if diff := cmp.Diff(statuses, expectedStatuses); len(diff) > 0 {
741+
t.Fatal(diff)
742+
}
613743
}

0 commit comments

Comments
 (0)