package main import ( "os" "path/filepath" "reflect" "runtime" "sort" "strings" "sync" "testing" "github.com/Songmu/gitconfig" ) func samePathSlice(lhss, rhss []string) bool { if len(lhss) != len(rhss) { return false } lhssAbs := make([]string, len(lhss)) rhssAbs := make([]string, len(rhss)) for i, p := range lhss { lhsAbs, _ := filepath.Abs(filepath.Clean(p)) lhssAbs[i] = strings.ToLower(lhsAbs) rhsAbs, _ := filepath.Abs(filepath.Clean(rhss[i])) rhssAbs[i] = strings.ToLower(rhsAbs) } sort.Strings(lhssAbs) sort.Strings(rhssAbs) for i := range lhssAbs { if lhssAbs[i] != rhssAbs[i] { return false } } return true } func TestLocalRepositoryFromFullPath(t *testing.T) { defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots) tmproot := newTempDir(t) _localRepositoryRoots = []string{tmproot} testCases := []struct { fpath string expect string subpaths []string }{{ fpath: filepath.Join(tmproot, "github.com/motemen/ghq"), expect: "motemen/ghq", subpaths: []string{"ghq", "motemen/ghq", "github.com/motemen/ghq"}, }, { fpath: filepath.Join(tmproot, "stash.com/scm/motemen/ghq"), expect: "scm/motemen/ghq", subpaths: []string{"ghq", "motemen/ghq", "scm/motemen/ghq", "stash.com/scm/motemen/ghq"}, }} for _, tc := range testCases { t.Run(tc.fpath, func(t *testing.T) { r, err := LocalRepositoryFromFullPath(tc.fpath, nil) if err != nil { t.Errorf("error should be nil but: %s", err) return } if r.NonHostPath() != tc.expect { t.Errorf("NonHostPath: got: %s, expect: %s", r.NonHostPath(), tc.expect) } if !reflect.DeepEqual(r.Subpaths(), tc.subpaths) { t.Errorf("Subpaths:\ngot: %+v\nexpect: %+v", r.Subpaths(), tc.subpaths) } }) } } func TestNewLocalRepository(t *testing.T) { defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots) tmproot := newTempDir(t) _localRepositoryRoots = []string{tmproot} testCases := []struct { name, url, expect string }{{ name: "GitHub", url: "ssh://git@github.com/motemen/ghq.git", expect: filepath.Join(tmproot, "github.com/motemen/ghq"), }, { name: "stash", url: "ssh://git@stash.com/scm/motemen/ghq.git", expect: filepath.Join(tmproot, "stash.com/scm/motemen/ghq"), }, { name: "svn Sourceforge", url: "http://svn.code.sf.net/p/ghq/code/trunk", expect: filepath.Join(tmproot, "svn.code.sf.net/p/ghq/code/trunk"), }, { name: "git Sourceforge", url: "http://git.code.sf.net/p/ghq/code", expect: filepath.Join(tmproot, "git.code.sf.net/p/ghq/code"), }, { name: "svn Sourceforge JP", url: "http://scm.sourceforge.jp/svnroot/ghq/", expect: filepath.Join(tmproot, "scm.sourceforge.jp/svnroot/ghq"), }, { name: "git Sourceforge JP", url: "http://scm.sourceforge.jp/gitroot/ghq/ghq.git", expect: filepath.Join(tmproot, "scm.sourceforge.jp/gitroot/ghq/ghq"), }, { name: "svn Assembla", url: "https://subversion.assembla.com/svn/ghq/", expect: filepath.Join(tmproot, "subversion.assembla.com/svn/ghq"), }, { name: "git Assembla", url: "https://git.assembla.com/ghq.git", expect: filepath.Join(tmproot, "git.assembla.com/ghq"), }, { name: "bitbucket host with port", url: "https://bitbucket.local:8888/motemen/ghq.git", expect: filepath.Join(tmproot, "bitbucket.local/motemen/ghq"), }} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { defer func(orig string) { _home = orig }(_home) _home = "" homeOnce = &sync.Once{} r, err := LocalRepositoryFromURL(mustParseURL(tc.url), false) if err != nil { t.Errorf("error should be nil but: %s", err) } if r.FullPath != tc.expect { t.Errorf("got: %s, expect: %s", r.FullPath, tc.expect) } }) } } func TestLocalRepositoryRoots(t *testing.T) { defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots) wd, err := os.Getwd() if err != nil { t.Fatal(err) } testCases := []struct { root string expect []string }{{ root: "/path/to/ghqroot", expect: []string{"/path/to/ghqroot"}, }, { root: "/path/to/ghqroot1" + string(os.PathListSeparator) + "/path/to/ghqroot2", expect: []string{"/path/to/ghqroot1", "/path/to/ghqroot2"}, }, { root: "/path/to/ghqroot11" + string(os.PathListSeparator) + "vendor", expect: []string{"/path/to/ghqroot11", filepath.Join(wd, "vendor")}, }} for _, tc := range testCases { t.Run(tc.root, func(t *testing.T) { _localRepositoryRoots = nil localRepoOnce = &sync.Once{} setEnv(t, envGhqRoot, tc.root) got, err := localRepositoryRoots(true) if err != nil { t.Errorf("error should be nil, but: %s", err) } if !samePathSlice(got, tc.expect) { t.Errorf("\ngot: %+v\nexpect: %+v", got, tc.expect) } }) } } // https://gist.github.com/kyanny/c231f48e5d08b98ff2c3 func TestList_Symlink(t *testing.T) { if runtime.GOOS == "windows" { t.SkipNow() } root := newTempDir(t) symDir := newTempDir(t) origLocalRepositoryRoots := _localRepositoryRoots _localRepositoryRoots = []string{root} defer func() { _localRepositoryRoots = origLocalRepositoryRoots }() if err := os.MkdirAll(filepath.Join(root, "github.com", "atom", "atom", ".git"), 0777); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(root, "github.com", "zabbix", "zabbix", ".git"), 0777); err != nil { t.Fatal(err) } if err := os.Symlink(symDir, filepath.Join(root, "github.com", "ghq")); err != nil { t.Fatal(err) } var paths []string walkAllLocalRepositories(func(repo *LocalRepository) { paths = append(paths, repo.RelPath) }) if len(paths) != 2 { t.Errorf("length of paths should be 2, but: %d", len(paths)) } } func TestList_Symlink_In_Same_Directory(t *testing.T) { if runtime.GOOS == "windows" { t.SkipNow() } root := newTempDir(t) symDir := newTempDir(t) origLocalRepositoryRoots := _localRepositoryRoots _localRepositoryRoots = []string{root} defer func() { _localRepositoryRoots = origLocalRepositoryRoots }() if err := os.MkdirAll(filepath.Join(root, "github.com", "root-user", "a-repository", ".git"), 0777); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(root, "github.com", "root-user", "z-repository", ".git"), 0777); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(symDir, "github.com", "sym-user", "h-repository", ".git"), 0777); err != nil { t.Fatal(err) } if err := os.Symlink(filepath.Join(symDir, "github.com", "sym-user", "h-repository"), filepath.Join(root, "github.com", "root-user", "h-repository")); err != nil { t.Fatal(err) } var paths []string walkAllLocalRepositories(func(repo *LocalRepository) { paths = append(paths, repo.RelPath) }) if len(paths) != 3 { t.Errorf("length of paths should be 3, but: %d", len(paths)) } } func TestFindVCSBackend(t *testing.T) { testCases := []struct { name string setup func(t *testing.T) (string, string) expect *VCSBackend }{{ name: "git-bare", setup: func(t *testing.T) (string, string) { dir := newTempDir(t) dir = dir + ".git" os.MkdirAll(dir, 0o755) return dir, "" }, expect: GitBackend, }, { name: "git", setup: func(t *testing.T) (string, string) { dir := newTempDir(t) os.MkdirAll(filepath.Join(dir, ".git"), 0755) return dir, "" }, expect: GitBackend, }, { name: "git svn", setup: func(t *testing.T) (string, string) { dir := newTempDir(t) os.MkdirAll(filepath.Join(dir, ".git", "svn"), 0755) return dir, "" }, expect: GitBackend, }, { name: "git with matched vcs", setup: func(t *testing.T) (string, string) { dir := newTempDir(t) os.MkdirAll(filepath.Join(dir, ".git"), 0755) return dir, "git" }, expect: GitBackend, }, { name: "git with not matched vcs", setup: func(t *testing.T) (string, string) { dir := newTempDir(t) os.MkdirAll(filepath.Join(dir, ".git"), 0755) return dir, "mercurial" }, expect: nil, }} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { fpath, vcs := tc.setup(t) backend := findVCSBackend(fpath, vcs) if backend != tc.expect { t.Errorf("got: %v, expect: %v", backend, tc.expect) } }) } } func TestLocalRepository_VCS(t *testing.T) { defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots) _localRepositoryRoots = nil localRepoOnce = &sync.Once{} tmpdir := newTempDir(t) setEnv(t, envGhqRoot, tmpdir) pkg := filepath.Join(tmpdir, "github.com", "motemen", "ghq") subpkg := filepath.Join(pkg, "logger") os.MkdirAll(filepath.Join(pkg, ".git"), 0755) os.MkdirAll(subpkg, 0755) t.Run("reporoot", func(t *testing.T) { repo, err := LocalRepositoryFromFullPath(pkg, nil) if err != nil { t.Errorf("error should be nil, but: %s (%s)", err, pkg) return } vcs, repoPath := repo.VCS() if vcs != GitBackend { t.Errorf("repo.VCS() = %+v, expect: GitBackend", vcs) return } if repoPath != pkg { t.Errorf("got: %s, expect: %s", repoPath, pkg) } }) t.Run("subdir", func(t *testing.T) { repo, err := LocalRepositoryFromFullPath(subpkg, nil) if err != nil { t.Errorf("error should be nil, but: %s", err) return } vcs, repoPath := repo.VCS() if vcs != GitBackend { t.Errorf("repo.VCS() = %+v, expect: GitBackend", vcs) } if repoPath != pkg { t.Errorf("got: %s, expect: %s", repoPath, pkg) } }) } func TestLocalRepositoryRoots_URLMatchLocalRepositoryRoots(t *testing.T) { if runtime.GOOS == "windows" { t.SkipNow() } setEnv(t, "HOME", "/home/tmp") defer func(orig string) { _home = orig }(_home) _home = "" homeOnce = &sync.Once{} t.Cleanup(gitconfig.WithConfig(t, ` [ghq] root = /hoge [ghq "https://github.com/hatena"] root = ~/proj/hatena root = /backups/hatena [ghq "https://github.com/natureglobal"] root = ~/proj/natureglobal `)) want := []string{"/hoge", "/home/tmp/proj/hatena", "/backups/hatena", "/home/tmp/proj/natureglobal"} _localRepositoryRoots = nil localRepoOnce = &sync.Once{} got, err := localRepositoryRoots(true) if err != nil { t.Errorf("error should be nil but: %s", err) } if !reflect.DeepEqual(want, got) { t.Errorf("localRepositoryRoots(true) = %+v, want: %+v", got, want) } }