From 90abc13e97c4ea22be78b71665b07645a862e8cd Mon Sep 17 00:00:00 2001 From: sam boyer Date: Wed, 25 Oct 2017 21:31:39 -0400 Subject: [PATCH] gps: Tests for adaptive git repo recovery/cleanup This covers three basic cases - untracked files, modified files, and some corruption in the .git directory. The first two are plausible; the third is less so, as we don't know much about what real git failure patterns could look like in the .git directory itself. However, it's an adequate test inasmuch as it triggers failure in the basic git calls we make, thereby triggering the recovery procedure. --- internal/gps/manager_test.go | 6 --- internal/gps/maybe_source.go | 2 +- internal/gps/vcs_source.go | 4 +- internal/gps/vcs_source_test.go | 96 +++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/internal/gps/manager_test.go b/internal/gps/manager_test.go index cb9d8819d5..f2d88ecd1c 100644 --- a/internal/gps/manager_test.go +++ b/internal/gps/manager_test.go @@ -259,12 +259,6 @@ func TestSourceInit(t *testing.T) { os.Stat(filepath.Join(cpath, "metadata", "github.com", "sdboyer", "gpkt", "cache.json")) - // TODO(sdboyer) disabled until we get caching working - //_, err = os.Stat(filepath.Join(cpath, "metadata", "github.com", "sdboyer", "gpkt", "cache.json")) - //if err != nil { - //t.Error("Metadata cache json file does not exist in expected location") - //} - // Ensure source existence values are what we expect var exists bool exists, err = sm.SourceExists(id) diff --git a/internal/gps/maybe_source.go b/internal/gps/maybe_source.go index c306a92405..3128181dcd 100644 --- a/internal/gps/maybe_source.go +++ b/internal/gps/maybe_source.go @@ -108,7 +108,7 @@ func (m maybeGitSource) try(ctx context.Context, cachedir string, c singleSource if err := superv.do(ctx, "git", ctValidateLocal, func(ctx context.Context) error { // If repository already exists on disk, make a pass to be sure // everything's clean. - return src.cleanup(ctx) + return src.ensureClean(ctx) }); err != nil { return nil, 0, err } diff --git a/internal/gps/vcs_source.go b/internal/gps/vcs_source.go index 29c2b4c4eb..0957128e1a 100644 --- a/internal/gps/vcs_source.go +++ b/internal/gps/vcs_source.go @@ -124,8 +124,8 @@ type gitSource struct { } // ensureClean sees to it that a git repository is clean and in working order, -// or returns an error if it can't. -func (s *gitSource) cleanup(ctx context.Context) error { +// or returns an error if the adaptive recovery attempts fail. +func (s *gitSource) ensureClean(ctx context.Context) error { r := s.repo.(*gitRepo) cmd := commandContext( ctx, diff --git a/internal/gps/vcs_source_test.go b/internal/gps/vcs_source_test.go index d48e34412d..47a1e44628 100644 --- a/internal/gps/vcs_source_test.go +++ b/internal/gps/vcs_source_test.go @@ -7,6 +7,7 @@ package gps import ( "context" "io/ioutil" + "log" "net/url" "os" "os/exec" @@ -647,6 +648,101 @@ func TestGitSourceListVersionsNoDupes(t *testing.T) { } } +func TestGitSourceAdaptiveCleanup(t *testing.T) { + t.Parallel() + + // This test is slowish, skip it on -short + if testing.Short() { + t.Skip("Skipping git adaptive failure recovery test in short mode") + } + requiresBins(t, "git") + + cpath, err := ioutil.TempDir("", "smcache") + if err != nil { + t.Fatalf("Failed to create temp dir: %s", err) + } + + var sm *SourceMgr + mkSM := func() { + // If sm is already set, make sure it's released, then create a new one. + if sm != nil { + sm.Release() + } + + var err error + sm, err = NewSourceManager(SourceManagerConfig{ + Cachedir: cpath, + Logger: log.New(test.Writer{TB: t}, "", 0), + }) + if err != nil { + t.Fatalf("Unexpected error on SourceManager creation: %s", err) + } + } + + mkSM() + id := mkPI("github.com/sdboyer/gpkt") + err = sm.SyncSourceFor(id) + if err != nil { + t.Fatal(err) + } + + repodir := filepath.Join(sm.cachedir, "sources", "https---github.com-sdboyer-gpkt") + if _, err := os.Stat(repodir); err != nil { + if os.IsNotExist(err) { + t.Fatalf("expected location for repodir did not exist: %q", repodir) + } else { + t.Fatal(err) + } + } + + // Create a file that git will see as untracked. + untrackedPath := filepath.Join(repodir, "untrackedfile") + f, err := os.Create(untrackedPath) + if err != nil { + t.Fatal(err) + } + f.Close() + + mkSM() + err = sm.SyncSourceFor(id) + if err != nil { + t.Fatalf("choked after adding dummy file: %q", err) + } + + if _, err := os.Stat(untrackedPath); err == nil { + t.Fatal("untracked file still existed after cleanup should've been triggered") + } + + // Remove a file that we know exists, which `git status` checks should catch. + readmePath := filepath.Join(repodir, "README.md") + os.Remove(readmePath) + + mkSM() + err = sm.SyncSourceFor(id) + if err != nil { + t.Fatalf("choked after removing known file: %q", err) + } + + if _, err := os.Stat(readmePath); err != nil { + t.Fatal("README was still absent after cleanup should've been triggered") + } + + // Remove .git/objects directory, which should make git bite it. + err = os.RemoveAll(filepath.Join(repodir, ".git", "objects")) + if err != nil { + t.Fatal(err) + } + + mkSM() + err = sm.SyncSourceFor(id) + if err != nil { + t.Fatalf("choked after removing .git/objects directory: %q", err) + } + + sm.Release() + os.RemoveAll(cpath) +} + func Test_bzrSource_exportRevisionTo_removeVcsFiles(t *testing.T) { t.Parallel()