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 }