blob: 65247e796e9e2b98586bb6eeda70402528a457be [file] [log] [blame]
package repo_manager
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"testing"
assert "github.com/stretchr/testify/require"
"go.skia.org/infra/autoroll/go/strategy"
"go.skia.org/infra/go/exec"
"go.skia.org/infra/go/gerrit"
"go.skia.org/infra/go/git"
git_testutils "go.skia.org/infra/go/git/testutils"
"go.skia.org/infra/go/mockhttpclient"
"go.skia.org/infra/go/recipe_cfg"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/util"
)
var (
manifestEmails = []string{"reviewer@chromium.org"}
)
func manifestCfg() *ManifestRepoManagerConfig {
return &ManifestRepoManagerConfig{
DepotToolsRepoManagerConfig: DepotToolsRepoManagerConfig{
CommonRepoManagerConfig: CommonRepoManagerConfig{
ChildBranch: "master",
ChildPath: childPath,
ParentBranch: "master",
},
},
}
}
func setupManifest(t *testing.T) (context.Context, string, *git_testutils.GitBuilder, []string, *git_testutils.GitBuilder, func()) {
wd, err := ioutil.TempDir("", "")
assert.NoError(t, err)
// Create child and parent repos.
child := git_testutils.GitInit(t, context.Background())
f := "somefile.txt"
childCommits := make([]string, 0, 10)
for i := 0; i < numChildCommits; i++ {
childCommits = append(childCommits, child.CommitGen(context.Background(), f))
}
parent := git_testutils.GitInit(t, context.Background())
parent.Add(context.Background(), manifestFileName, fmt.Sprintf(`
<manifest>
<projects>
<project name="third_party/sbase"
path="third_party/sbase"
remote="https://fuchsia.googlesource.com/third_party/sbase"
gerrithost="https://fuchsia-review.googlesource.com"
revision="abc"
githooks="manifest/git-hooks"/>
<project name="%s"
path="%s"
remote="%s"
revision="%s"/>
<project name="third_party/snappy"
path="third_party/snappy"
remote="https://fuchsia.googlesource.com/third_party/snappy"
gerrithost="https://fuchsia-review.googlesource.com"
githooks="manifest/git-hooks"/>
</projects>
</manifest>`, childPath, childPath, child.RepoUrl(), childCommits[0]))
parent.Commit(context.Background())
mockRun := exec.CommandCollector{}
ctx := exec.NewContext(context.Background(), mockRun.Run)
mockRun.SetDelegateRun(func(cmd *exec.Command) error {
if cmd.Name == "git" {
if cmd.Args[0] == "cl" {
if cmd.Args[1] == "upload" {
return nil
} else if cmd.Args[1] == "issue" {
json := testutils.MarshalJSON(t, &issueJson{
Issue: issueNum,
IssueUrl: "???",
})
f := strings.Split(cmd.Args[2], "=")[1]
testutils.WriteFile(t, f, json)
return nil
}
} else if cmd.Args[0] == "clone" {
if !util.In(child.RepoUrl(), cmd.Args) {
return nil
}
}
} else if cmd.Name == "python" {
if strings.Contains(cmd.Args[0], "gclient.py") && cmd.Args[1] == "sync" {
// This needs to be deferred until the repo manager creates the dir,
// so we run it at "gclient sync".
dest := path.Join(wd, "repo_manager", childPath)
if _, err := os.Stat(dest); os.IsNotExist(err) {
co, err := git.NewCheckout(ctx, child.RepoUrl(), cmd.Dir)
assert.NoError(t, err)
err = os.MkdirAll(path.Dir(dest), os.ModePerm)
assert.NoError(t, err)
assert.NoError(t, os.Rename(co.Dir(), dest))
}
}
}
return exec.DefaultRun(cmd)
})
cleanup := func() {
testutils.RemoveAll(t, wd)
child.Cleanup()
parent.Cleanup()
}
return ctx, wd, child, childCommits, parent, cleanup
}
func setupManifestFakeGerrit(t *testing.T, wd string) *gerrit.Gerrit {
gUrl := "https://fake-skia-review.googlesource.com"
urlMock := mockhttpclient.NewURLMock()
serialized, err := json.Marshal(&gerrit.AccountDetails{
AccountId: 101,
Name: mockUser,
Email: mockUser,
UserName: mockUser,
})
assert.NoError(t, err)
serialized = append([]byte("abcd\n"), serialized...)
urlMock.MockOnce(gUrl+"/a/accounts/self/detail", mockhttpclient.MockGetDialogue(serialized))
serializedIssue, err := json.Marshal(&gerrit.ChangeInfo{
Issue: 12345,
ChangeId: "abc",
Revisions: map[string]*gerrit.Revision{
"1": &gerrit.Revision{},
},
})
assert.NoError(t, err)
serializedIssue = append([]byte("abcd\n"), serializedIssue...)
urlMock.MockOnce(gUrl+"/a/changes/12345/detail?o=ALL_REVISIONS", mockhttpclient.MockGetDialogue(serializedIssue))
urlMock.MockOnce(gUrl+"/a/changes/abc/revisions/1/review", mockhttpclient.MockPostDialogue("application/json", mockhttpclient.DONT_CARE_REQUEST, []byte{}))
gitcookies := path.Join(wd, "gitcookies_fake")
assert.NoError(t, ioutil.WriteFile(gitcookies, []byte(".googlesource.com\tTRUE\t/\tTRUE\t123\to\tgit-user.google.com=abc123"), os.ModePerm))
g, err := gerrit.NewGerrit(gUrl, gitcookies, urlMock.Client())
assert.NoError(t, err)
return g
}
// TestRepoManager tests all aspects of the ManifestRepoManager except for CreateNewRoll.
func TestManifestRepoManager(t *testing.T) {
testutils.LargeTest(t)
ctx, wd, child, childCommits, parent, cleanup := setupManifest(t)
defer cleanup()
recipesCfg := filepath.Join(testutils.GetRepoRoot(t), recipe_cfg.RECIPE_CFG_PATH)
g := setupManifestFakeGerrit(t, wd)
cfg := manifestCfg()
cfg.ParentRepo = parent.RepoUrl()
rm, err := NewManifestRepoManager(ctx, cfg, wd, g, recipesCfg, "fake.server.com", nil, gerritCR(t, g), false)
assert.NoError(t, err)
assert.NoError(t, SetStrategy(ctx, rm, strategy.ROLL_STRATEGY_BATCH))
assert.NoError(t, rm.Update(ctx))
assert.Equal(t, childCommits[0], rm.LastRollRev())
assert.Equal(t, childCommits[len(childCommits)-1], rm.NextRollRev())
// Test update.
lastCommit := child.CommitGen(ctx, "abc.txt")
assert.NoError(t, rm.Update(ctx))
assert.Equal(t, lastCommit, rm.NextRollRev())
}
// TestCreateNewManifestRoll tests that CreateNewRoll returns the expected issueNum by mocking out
// the git cl upload call.
func TestCreateNewManifestRoll(t *testing.T) {
testutils.LargeTest(t)
ctx, wd, _, _, parent, cleanup := setupManifest(t)
defer cleanup()
recipesCfg := filepath.Join(testutils.GetRepoRoot(t), recipe_cfg.RECIPE_CFG_PATH)
g := setupManifestFakeGerrit(t, wd)
cfg := manifestCfg()
cfg.ParentRepo = parent.RepoUrl()
rm, err := NewManifestRepoManager(ctx, cfg, wd, g, recipesCfg, "fake.server.com", nil, gerritCR(t, g), false)
assert.NoError(t, err)
assert.NoError(t, SetStrategy(ctx, rm, strategy.ROLL_STRATEGY_BATCH))
assert.NoError(t, rm.Update(ctx))
// Create a roll, assert that it's at tip of tree.
issue, err := rm.CreateNewRoll(ctx, rm.LastRollRev(), rm.NextRollRev(), manifestEmails, "", false)
assert.NoError(t, err)
assert.Equal(t, issueNum, issue)
}
// Verify that we ran the PreUploadSteps.
func TestRanPreUploadStepsManifest(t *testing.T) {
testutils.LargeTest(t)
ctx, wd, _, _, parent, cleanup := setupManifest(t)
defer cleanup()
recipesCfg := filepath.Join(testutils.GetRepoRoot(t), recipe_cfg.RECIPE_CFG_PATH)
g := setupManifestFakeGerrit(t, wd)
cfg := manifestCfg()
cfg.ParentRepo = parent.RepoUrl()
rm, err := NewManifestRepoManager(ctx, cfg, wd, g, recipesCfg, "fake.server.com", nil, gerritCR(t, g), false)
assert.NoError(t, err)
assert.NoError(t, SetStrategy(ctx, rm, strategy.ROLL_STRATEGY_BATCH))
assert.NoError(t, rm.Update(ctx))
ran := false
rm.(*manifestRepoManager).preUploadSteps = []PreUploadStep{
func(context.Context, *http.Client, string) error {
ran = true
return nil
},
}
// Create a roll, assert that we ran the PreUploadSteps.
_, err = rm.CreateNewRoll(ctx, rm.LastRollRev(), rm.NextRollRev(), manifestEmails, "", false)
assert.NoError(t, err)
assert.True(t, ran)
}
func TestManifestConfigValidation(t *testing.T) {
testutils.SmallTest(t)
cfg := manifestCfg()
cfg.ParentRepo = "repo" // Excluded from manifestCfg.
assert.NoError(t, cfg.Validate())
// The only fields come from the nested Configs, so exclude them and
// verify that we fail validation.
cfg = &ManifestRepoManagerConfig{}
assert.Error(t, cfg.Validate())
}