| /* |
| Package mem_git provides convenience functionality for writing test data into a GitStore. |
| */ |
| |
| package mem_git |
| |
| import ( |
| "context" |
| "crypto/sha1" |
| "fmt" |
| "time" |
| |
| "github.com/stretchr/testify/require" |
| "go.skia.org/infra/go/git" |
| "go.skia.org/infra/go/gitstore" |
| "go.skia.org/infra/go/sktest" |
| "go.skia.org/infra/go/testutils" |
| "go.skia.org/infra/go/util" |
| "go.skia.org/infra/go/vcsinfo" |
| ) |
| |
| const ( |
| // These are the commit hashes generated by CommitN(ctx, 2) when run as |
| // first call to MemGit. |
| Commit0 = "62463ce8091e96b1cf0c9d328c6931ffa0844c72" |
| Commit1 = "4f5e1f4b44db19042955d08a96f9cbbb76e199d7" |
| ) |
| |
| var ( |
| // BaseTime is an arbitrary timestamp used as the time of the first |
| // commit. Subsequent commits add a fixed duration to the timestamp |
| // of their parent(s). This keeps the commit hashes predictable. |
| BaseTime = time.Unix(1571926390, 0).UTC() |
| ) |
| |
| // FakeCommitAt creates a LongCommit with the given message at the given time, |
| // belonging to the given branch, and with the given parent commits. Its Index |
| // increases monotonically with respect to the parents. |
| func FakeCommitAt(t sktest.TestingT, msg, branch string, ts time.Time, parents ...*vcsinfo.LongCommit) *vcsinfo.LongCommit { |
| index := 0 |
| var parentHashes []string |
| if len(parents) > 0 { |
| parentHashes = make([]string, 0, len(parents)) |
| for _, p := range parents { |
| if p.Index >= index { |
| index = p.Index + 1 |
| } |
| parentHashes = append(parentHashes, p.Hash) |
| } |
| } |
| lc := &vcsinfo.LongCommit{ |
| ShortCommit: &vcsinfo.ShortCommit{ |
| Author: "me@google.com", |
| Subject: msg, |
| }, |
| Parents: parentHashes, |
| Timestamp: ts, |
| Index: index, |
| Branches: map[string]bool{ |
| branch: true, |
| }, |
| } |
| j := testutils.MarshalJSON(t, lc) |
| lc.Hash = fmt.Sprintf("%040x", sha1.Sum([]byte(j))) |
| return lc |
| } |
| |
| // FakeCommit creates a LongCommit with the given message, belonging to the |
| // given branch, and with the given parent commits. Its Timestamp and Index |
| // increase monotonically with respect to the parents. |
| func FakeCommit(t sktest.TestingT, msg, branch string, parents ...*vcsinfo.LongCommit) *vcsinfo.LongCommit { |
| ts := BaseTime |
| if len(parents) > 0 { |
| for _, p := range parents { |
| if !ts.After(p.Timestamp) { |
| ts = p.Timestamp.Add(time.Minute) |
| } |
| } |
| } |
| return FakeCommitAt(t, msg, branch, ts, parents...) |
| } |
| |
| // New returns a MemGit instance which writes to the given GitStore. |
| func New(t sktest.TestingT, gs gitstore.GitStore) *MemGit { |
| return &MemGit{ |
| branch: git.DefaultBranch, |
| gs: gs, |
| t: t, |
| } |
| } |
| |
| // MemGit is a struct used for writing fake commits into a GitStore. |
| type MemGit struct { |
| branch string |
| gs gitstore.GitStore |
| t sktest.TestingT |
| } |
| |
| // head returns the current head of the given branch. |
| func (g *MemGit) head(ctx context.Context, branch string) string { |
| branches, err := g.gs.GetBranches(ctx) |
| require.NoError(g.t, err) |
| ptr, ok := branches[branch] |
| if !ok { |
| return "" |
| } |
| return ptr.Head |
| } |
| |
| // makeCommit creates a commit with the given message and parents on the |
| // currently-active branch. |
| func (g *MemGit) makeCommit(ctx context.Context, msg string, ts time.Time, parentHashes []string) *vcsinfo.LongCommit { |
| parents := []*vcsinfo.LongCommit{} |
| if len(parentHashes) > 0 { |
| var err error |
| parents, err = g.gs.Get(ctx, parentHashes) |
| require.NoError(g.t, err) |
| } |
| var lc *vcsinfo.LongCommit |
| if util.TimeIsZero(ts) { |
| lc = FakeCommit(g.t, msg, g.branch, parents...) |
| } else { |
| lc = FakeCommitAt(g.t, msg, g.branch, ts, parents...) |
| } |
| require.NoError(g.t, g.gs.Put(ctx, []*vcsinfo.LongCommit{lc})) |
| require.NoError(g.t, g.gs.PutBranches(ctx, map[string]string{ |
| g.branch: lc.Hash, |
| })) |
| return lc |
| } |
| |
| // Commit adds a commit to the GitStore and returns its hash. |
| func (g *MemGit) Commit(ctx context.Context, msg string) string { |
| return g.CommitAt(ctx, msg, time.Time{}) |
| } |
| |
| // CommitAt adds a commit to the GitStore with the given timestamp and returns |
| // its hash. |
| func (g *MemGit) CommitAt(ctx context.Context, msg string, ts time.Time) string { |
| head := g.head(ctx, g.branch) |
| var parents []string |
| if head != "" { |
| parents = []string{head} |
| } |
| return g.makeCommit(ctx, msg, ts, parents).Hash |
| } |
| |
| // CommitN adds N commits to the GitStore and returns their hashes in reverse |
| // chronological order. |
| func (g *MemGit) CommitN(ctx context.Context, n int) []string { |
| hashes := make([]string, 0, n) |
| for i := 0; i < n; i++ { |
| hashes = append(hashes, g.Commit(ctx, fmt.Sprintf("Dummy #%d/%d", i, n))) |
| } |
| return util.Reverse(hashes) |
| } |
| |
| // CheckoutBranch switches to the given branch. |
| func (g *MemGit) CheckoutBranch(ctx context.Context, branch string) { |
| branches, err := g.gs.GetBranches(ctx) |
| require.NoError(g.t, err) |
| ptr, ok := branches[branch] |
| require.True(g.t, ok) |
| require.NotNil(g.t, ptr) |
| require.NotEqual(g.t, "", ptr.Head) |
| g.branch = branch |
| } |
| |
| // NewBranch creates the given branch at the given commit hash and switches to it. |
| func (g *MemGit) NewBranch(ctx context.Context, branch, head string) { |
| branches, err := g.gs.GetBranches(ctx) |
| require.NoError(g.t, err) |
| _, ok := branches[branch] |
| require.False(g.t, ok, "Already have %s", branch) |
| require.NoError(g.t, g.gs.PutBranches(ctx, map[string]string{ |
| branch: head, |
| })) |
| g.branch = branch |
| } |
| |
| // Merge creates a new commit which merges the given branch into the currently |
| // active branch. |
| func (g *MemGit) Merge(ctx context.Context, branch string) *vcsinfo.LongCommit { |
| return g.makeCommit(ctx, fmt.Sprintf("Merge %s", branch), time.Time{}, []string{g.head(ctx, g.branch), g.head(ctx, branch)}) |
| } |