github.com/x-motemen/ghq@v1.6.1/local_repository_test.go (about)

     1  package main
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"reflect"
     7  	"runtime"
     8  	"sort"
     9  	"strings"
    10  	"sync"
    11  	"testing"
    12  
    13  	"github.com/Songmu/gitconfig"
    14  )
    15  
    16  func samePathSlice(lhss, rhss []string) bool {
    17  	if len(lhss) != len(rhss) {
    18  		return false
    19  	}
    20  	lhssAbs := make([]string, len(lhss))
    21  	rhssAbs := make([]string, len(rhss))
    22  
    23  	for i, p := range lhss {
    24  		lhsAbs, _ := filepath.Abs(filepath.Clean(p))
    25  		lhssAbs[i] = strings.ToLower(lhsAbs)
    26  
    27  		rhsAbs, _ := filepath.Abs(filepath.Clean(rhss[i]))
    28  		rhssAbs[i] = strings.ToLower(rhsAbs)
    29  	}
    30  	sort.Strings(lhssAbs)
    31  	sort.Strings(rhssAbs)
    32  	for i := range lhssAbs {
    33  		if lhssAbs[i] != rhssAbs[i] {
    34  			return false
    35  		}
    36  	}
    37  	return true
    38  }
    39  
    40  func TestLocalRepositoryFromFullPath(t *testing.T) {
    41  	defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots)
    42  	tmproot := newTempDir(t)
    43  	_localRepositoryRoots = []string{tmproot}
    44  
    45  	testCases := []struct {
    46  		fpath    string
    47  		expect   string
    48  		subpaths []string
    49  	}{{
    50  		fpath:    filepath.Join(tmproot, "github.com/motemen/ghq"),
    51  		expect:   "motemen/ghq",
    52  		subpaths: []string{"ghq", "motemen/ghq", "github.com/motemen/ghq"},
    53  	}, {
    54  		fpath:    filepath.Join(tmproot, "stash.com/scm/motemen/ghq"),
    55  		expect:   "scm/motemen/ghq",
    56  		subpaths: []string{"ghq", "motemen/ghq", "scm/motemen/ghq", "stash.com/scm/motemen/ghq"},
    57  	}}
    58  
    59  	for _, tc := range testCases {
    60  		t.Run(tc.fpath, func(t *testing.T) {
    61  			r, err := LocalRepositoryFromFullPath(tc.fpath, nil)
    62  			if err != nil {
    63  				t.Errorf("error should be nil but: %s", err)
    64  				return
    65  			}
    66  			if r.NonHostPath() != tc.expect {
    67  				t.Errorf("NonHostPath: got: %s, expect: %s", r.NonHostPath(), tc.expect)
    68  			}
    69  			if !reflect.DeepEqual(r.Subpaths(), tc.subpaths) {
    70  				t.Errorf("Subpaths:\ngot:    %+v\nexpect: %+v", r.Subpaths(), tc.subpaths)
    71  			}
    72  		})
    73  	}
    74  }
    75  
    76  func TestNewLocalRepository(t *testing.T) {
    77  	defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots)
    78  	tmproot := newTempDir(t)
    79  	_localRepositoryRoots = []string{tmproot}
    80  
    81  	testCases := []struct {
    82  		name, url, expect string
    83  	}{{
    84  		name:   "GitHub",
    85  		url:    "ssh://git@github.com/motemen/ghq.git",
    86  		expect: filepath.Join(tmproot, "github.com/motemen/ghq"),
    87  	}, {
    88  		name:   "stash",
    89  		url:    "ssh://git@stash.com/scm/motemen/ghq.git",
    90  		expect: filepath.Join(tmproot, "stash.com/scm/motemen/ghq"),
    91  	}, {
    92  		name:   "svn Sourceforge",
    93  		url:    "http://svn.code.sf.net/p/ghq/code/trunk",
    94  		expect: filepath.Join(tmproot, "svn.code.sf.net/p/ghq/code/trunk"),
    95  	}, {
    96  		name:   "git Sourceforge",
    97  		url:    "http://git.code.sf.net/p/ghq/code",
    98  		expect: filepath.Join(tmproot, "git.code.sf.net/p/ghq/code"),
    99  	}, {
   100  		name:   "svn Sourceforge JP",
   101  		url:    "http://scm.sourceforge.jp/svnroot/ghq/",
   102  		expect: filepath.Join(tmproot, "scm.sourceforge.jp/svnroot/ghq"),
   103  	}, {
   104  		name:   "git Sourceforge JP",
   105  		url:    "http://scm.sourceforge.jp/gitroot/ghq/ghq.git",
   106  		expect: filepath.Join(tmproot, "scm.sourceforge.jp/gitroot/ghq/ghq"),
   107  	}, {
   108  		name:   "svn Assembla",
   109  		url:    "https://subversion.assembla.com/svn/ghq/",
   110  		expect: filepath.Join(tmproot, "subversion.assembla.com/svn/ghq"),
   111  	}, {
   112  		name:   "git Assembla",
   113  		url:    "https://git.assembla.com/ghq.git",
   114  		expect: filepath.Join(tmproot, "git.assembla.com/ghq"),
   115  	}, {
   116  		name:   "bitbucket host with port",
   117  		url:    "https://bitbucket.local:8888/motemen/ghq.git",
   118  		expect: filepath.Join(tmproot, "bitbucket.local/motemen/ghq"),
   119  	}}
   120  
   121  	for _, tc := range testCases {
   122  		t.Run(tc.name, func(t *testing.T) {
   123  			defer func(orig string) { _home = orig }(_home)
   124  			_home = ""
   125  			homeOnce = &sync.Once{}
   126  			r, err := LocalRepositoryFromURL(mustParseURL(tc.url), false)
   127  			if err != nil {
   128  				t.Errorf("error should be nil but: %s", err)
   129  			}
   130  			if r.FullPath != tc.expect {
   131  				t.Errorf("got: %s, expect: %s", r.FullPath, tc.expect)
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  func TestLocalRepositoryRoots(t *testing.T) {
   138  	defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots)
   139  
   140  	wd, err := os.Getwd()
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  
   145  	testCases := []struct {
   146  		root   string
   147  		expect []string
   148  	}{{
   149  		root:   "/path/to/ghqroot",
   150  		expect: []string{"/path/to/ghqroot"},
   151  	}, {
   152  		root:   "/path/to/ghqroot1" + string(os.PathListSeparator) + "/path/to/ghqroot2",
   153  		expect: []string{"/path/to/ghqroot1", "/path/to/ghqroot2"},
   154  	}, {
   155  		root:   "/path/to/ghqroot11" + string(os.PathListSeparator) + "vendor",
   156  		expect: []string{"/path/to/ghqroot11", filepath.Join(wd, "vendor")},
   157  	}}
   158  
   159  	for _, tc := range testCases {
   160  		t.Run(tc.root, func(t *testing.T) {
   161  			_localRepositoryRoots = nil
   162  			localRepoOnce = &sync.Once{}
   163  			setEnv(t, envGhqRoot, tc.root)
   164  			got, err := localRepositoryRoots(true)
   165  			if err != nil {
   166  				t.Errorf("error should be nil, but: %s", err)
   167  			}
   168  			if !samePathSlice(got, tc.expect) {
   169  				t.Errorf("\ngot:    %+v\nexpect: %+v", got, tc.expect)
   170  			}
   171  		})
   172  	}
   173  }
   174  
   175  // https://gist.github.com/kyanny/c231f48e5d08b98ff2c3
   176  func TestList_Symlink(t *testing.T) {
   177  	if runtime.GOOS == "windows" {
   178  		t.SkipNow()
   179  	}
   180  	root := newTempDir(t)
   181  	symDir := newTempDir(t)
   182  
   183  	origLocalRepositoryRoots := _localRepositoryRoots
   184  	_localRepositoryRoots = []string{root}
   185  	defer func() { _localRepositoryRoots = origLocalRepositoryRoots }()
   186  
   187  	if err := os.MkdirAll(filepath.Join(root, "github.com", "atom", "atom", ".git"), 0777); err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	if err := os.MkdirAll(filepath.Join(root, "github.com", "zabbix", "zabbix", ".git"), 0777); err != nil {
   192  		t.Fatal(err)
   193  	}
   194  
   195  	if err := os.Symlink(symDir, filepath.Join(root, "github.com", "ghq")); err != nil {
   196  		t.Fatal(err)
   197  	}
   198  
   199  	var paths []string
   200  	walkAllLocalRepositories(func(repo *LocalRepository) {
   201  		paths = append(paths, repo.RelPath)
   202  	})
   203  
   204  	if len(paths) != 2 {
   205  		t.Errorf("length of paths should be 2, but: %d", len(paths))
   206  	}
   207  }
   208  
   209  func TestList_Symlink_In_Same_Directory(t *testing.T) {
   210  	if runtime.GOOS == "windows" {
   211  		t.SkipNow()
   212  	}
   213  	root := newTempDir(t)
   214  	symDir := newTempDir(t)
   215  
   216  	origLocalRepositoryRoots := _localRepositoryRoots
   217  	_localRepositoryRoots = []string{root}
   218  	defer func() { _localRepositoryRoots = origLocalRepositoryRoots }()
   219  
   220  	if err := os.MkdirAll(filepath.Join(root, "github.com", "root-user", "a-repository", ".git"), 0777); err != nil {
   221  		t.Fatal(err)
   222  	}
   223  
   224  	if err := os.MkdirAll(filepath.Join(root, "github.com", "root-user", "z-repository", ".git"), 0777); err != nil {
   225  		t.Fatal(err)
   226  	}
   227  
   228  	if err := os.MkdirAll(filepath.Join(symDir, "github.com", "sym-user", "h-repository", ".git"), 0777); err != nil {
   229  		t.Fatal(err)
   230  	}
   231  
   232  	if err := os.Symlink(filepath.Join(symDir, "github.com", "sym-user", "h-repository"), filepath.Join(root, "github.com", "root-user", "h-repository")); err != nil {
   233  		t.Fatal(err)
   234  	}
   235  
   236  	var paths []string
   237  	walkAllLocalRepositories(func(repo *LocalRepository) {
   238  		paths = append(paths, repo.RelPath)
   239  	})
   240  
   241  	if len(paths) != 3 {
   242  		t.Errorf("length of paths should be 3, but: %d", len(paths))
   243  	}
   244  }
   245  
   246  func TestFindVCSBackend(t *testing.T) {
   247  	testCases := []struct {
   248  		name   string
   249  		setup  func(t *testing.T) (string, string)
   250  		expect *VCSBackend
   251  	}{{
   252  		name: "git-bare",
   253  		setup: func(t *testing.T) (string, string) {
   254  			dir := newTempDir(t)
   255  			dir = dir + ".git"
   256  			os.MkdirAll(dir, 0o755)
   257  			return dir, ""
   258  		},
   259  		expect: GitBackend,
   260  	}, {
   261  		name: "git",
   262  		setup: func(t *testing.T) (string, string) {
   263  			dir := newTempDir(t)
   264  			os.MkdirAll(filepath.Join(dir, ".git"), 0755)
   265  			return dir, ""
   266  		},
   267  		expect: GitBackend,
   268  	}, {
   269  		name: "git svn",
   270  		setup: func(t *testing.T) (string, string) {
   271  			dir := newTempDir(t)
   272  			os.MkdirAll(filepath.Join(dir, ".git", "svn"), 0755)
   273  			return dir, ""
   274  		},
   275  		expect: GitBackend,
   276  	}, {
   277  		name: "git with matched vcs",
   278  		setup: func(t *testing.T) (string, string) {
   279  			dir := newTempDir(t)
   280  			os.MkdirAll(filepath.Join(dir, ".git"), 0755)
   281  			return dir, "git"
   282  		},
   283  		expect: GitBackend,
   284  	}, {
   285  		name: "git with not matched vcs",
   286  		setup: func(t *testing.T) (string, string) {
   287  			dir := newTempDir(t)
   288  			os.MkdirAll(filepath.Join(dir, ".git"), 0755)
   289  			return dir, "mercurial"
   290  		},
   291  		expect: nil,
   292  	}}
   293  
   294  	for _, tc := range testCases {
   295  		t.Run(tc.name, func(t *testing.T) {
   296  			fpath, vcs := tc.setup(t)
   297  			backend := findVCSBackend(fpath, vcs)
   298  			if backend != tc.expect {
   299  				t.Errorf("got: %v, expect: %v", backend, tc.expect)
   300  			}
   301  		})
   302  	}
   303  }
   304  
   305  func TestLocalRepository_VCS(t *testing.T) {
   306  	defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots)
   307  
   308  	_localRepositoryRoots = nil
   309  	localRepoOnce = &sync.Once{}
   310  	tmpdir := newTempDir(t)
   311  	setEnv(t, envGhqRoot, tmpdir)
   312  
   313  	pkg := filepath.Join(tmpdir, "github.com", "motemen", "ghq")
   314  	subpkg := filepath.Join(pkg, "logger")
   315  
   316  	os.MkdirAll(filepath.Join(pkg, ".git"), 0755)
   317  	os.MkdirAll(subpkg, 0755)
   318  
   319  	t.Run("reporoot", func(t *testing.T) {
   320  		repo, err := LocalRepositoryFromFullPath(pkg, nil)
   321  		if err != nil {
   322  			t.Errorf("error should be nil, but: %s (%s)", err, pkg)
   323  			return
   324  		}
   325  		vcs, repoPath := repo.VCS()
   326  		if vcs != GitBackend {
   327  			t.Errorf("repo.VCS() = %+v, expect: GitBackend", vcs)
   328  			return
   329  		}
   330  		if repoPath != pkg {
   331  			t.Errorf("got: %s, expect: %s", repoPath, pkg)
   332  		}
   333  	})
   334  
   335  	t.Run("subdir", func(t *testing.T) {
   336  		repo, err := LocalRepositoryFromFullPath(subpkg, nil)
   337  		if err != nil {
   338  			t.Errorf("error should be nil, but: %s", err)
   339  			return
   340  		}
   341  		vcs, repoPath := repo.VCS()
   342  		if vcs != GitBackend {
   343  			t.Errorf("repo.VCS() = %+v, expect: GitBackend", vcs)
   344  		}
   345  		if repoPath != pkg {
   346  			t.Errorf("got: %s, expect: %s", repoPath, pkg)
   347  		}
   348  	})
   349  }
   350  
   351  func TestLocalRepositoryRoots_URLMatchLocalRepositoryRoots(t *testing.T) {
   352  	if runtime.GOOS == "windows" {
   353  		t.SkipNow()
   354  	}
   355  	setEnv(t, "HOME", "/home/tmp")
   356  	defer func(orig string) { _home = orig }(_home)
   357  
   358  	_home = ""
   359  	homeOnce = &sync.Once{}
   360  	t.Cleanup(gitconfig.WithConfig(t, `
   361  [ghq]
   362    root = /hoge
   363  [ghq "https://github.com/hatena"]
   364    root = ~/proj/hatena
   365    root = /backups/hatena
   366  [ghq "https://github.com/natureglobal"]
   367    root = ~/proj/natureglobal
   368  `))
   369  
   370  	want := []string{"/hoge", "/home/tmp/proj/hatena", "/backups/hatena", "/home/tmp/proj/natureglobal"}
   371  
   372  	_localRepositoryRoots = nil
   373  	localRepoOnce = &sync.Once{}
   374  	got, err := localRepositoryRoots(true)
   375  	if err != nil {
   376  		t.Errorf("error should be nil but: %s", err)
   377  	}
   378  	if !reflect.DeepEqual(want, got) {
   379  		t.Errorf("localRepositoryRoots(true) = %+v, want: %+v", got, want)
   380  	}
   381  }