github.com/go4org/go4@v0.0.0-20200104003542-c7e774b10ea0/xdgdir/xdgdir_test.go (about) 1 /* 2 Copyright 2017 The go4 Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package xdgdir 18 19 import ( 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "testing" 24 ) 25 26 func TestDir_Path(t *testing.T) { 27 td := newTempDir(t) 28 defer td.cleanup() 29 allopenDir := td.mkdir("allopen", 0777) 30 readonlyDir := td.mkdir("readonly", 0400) 31 secureDir := td.mkdir("secure", 0700) 32 33 tests := []struct { 34 dir Dir 35 env env 36 path string 37 geteuid func() int 38 }{ 39 { 40 dir: Data, 41 env: env{"HOME": "/xHOMEx/me", "XDG_DATA_HOME": "/foo/data"}, 42 path: "/foo/data", 43 }, 44 { 45 dir: Data, 46 env: env{"HOME": "/xHOMEx/me"}, 47 path: "/xHOMEx/me/.local/share", 48 }, 49 { 50 dir: Data, 51 env: env{"HOME": "/xHOMEx/me", "XDG_DATA_HOME": "relative/path"}, 52 path: "/xHOMEx/me/.local/share", 53 }, 54 { 55 dir: Data, 56 env: env{}, 57 path: "", 58 }, 59 { 60 dir: Data, 61 env: env{"HOME": "relative/path"}, 62 path: "", 63 }, 64 { 65 dir: Config, 66 env: env{"HOME": "/xHOMEx/me", "XDG_CONFIG_HOME": "/foo/config"}, 67 path: "/foo/config", 68 }, 69 { 70 dir: Config, 71 env: env{"HOME": "/xHOMEx/me"}, 72 path: "/xHOMEx/me/.config", 73 }, 74 { 75 dir: Config, 76 env: env{"HOME": "/xHOMEx/me", "XDG_CONFIG_HOME": "relative/path"}, 77 path: "/xHOMEx/me/.config", 78 }, 79 { 80 dir: Config, 81 env: env{}, 82 path: "", 83 }, 84 { 85 dir: Cache, 86 env: env{"HOME": "/xHOMEx/me", "XDG_CACHE_HOME": "/foo/cache"}, 87 path: "/foo/cache", 88 }, 89 { 90 dir: Cache, 91 env: env{"HOME": "/xHOMEx/me"}, 92 path: "/xHOMEx/me/.cache", 93 }, 94 { 95 dir: Cache, 96 env: env{"HOME": "/xHOMEx/me", "XDG_CACHE_HOME": "relative/path"}, 97 path: "/xHOMEx/me/.cache", 98 }, 99 { 100 dir: Cache, 101 env: env{}, 102 path: "", 103 }, 104 { 105 dir: Runtime, 106 env: env{"XDG_RUNTIME_DIR": secureDir}, 107 path: secureDir, 108 }, 109 { 110 dir: Runtime, 111 env: env{"XDG_RUNTIME_DIR": secureDir}, 112 geteuid: func() int { return os.Geteuid() + 1 }, 113 path: "", 114 }, 115 { 116 dir: Runtime, 117 env: env{"XDG_RUNTIME_DIR": readonlyDir}, 118 path: "", 119 }, 120 { 121 dir: Runtime, 122 env: env{"XDG_RUNTIME_DIR": allopenDir}, 123 path: "", 124 }, 125 { 126 dir: Runtime, 127 env: env{"HOME": secureDir}, 128 path: "", 129 }, 130 { 131 dir: Runtime, 132 env: env{}, 133 path: "", 134 }, 135 } 136 for _, test := range tests { 137 test.env.set() 138 if test.geteuid != nil { 139 geteuid = test.geteuid 140 } else { 141 geteuid = os.Geteuid 142 } 143 if path := test.dir.Path(); path != test.path { 144 var euidMod string 145 if test.geteuid != nil { 146 euidMod = " (euid modified)" 147 } 148 t.Errorf("In environment %v%s, %v.Path() = %q; want %q", test.env, euidMod, test.dir, path, test.path) 149 } 150 } 151 } 152 153 func TestDir_SearchPaths(t *testing.T) { 154 td := newTempDir(t) 155 defer td.cleanup() 156 allopenDir := td.mkdir("allopen", 0777) 157 secureDir := td.mkdir("secure", 0700) 158 159 tests := []struct { 160 dir Dir 161 env env 162 paths []string 163 }{ 164 { 165 dir: Data, 166 env: env{}, 167 paths: []string{"/usr/local/share", "/usr/share"}, 168 }, 169 { 170 dir: Data, 171 env: env{"HOME": "/xHOMEx/me"}, 172 paths: []string{"/xHOMEx/me/.local/share", "/usr/local/share", "/usr/share"}, 173 }, 174 { 175 dir: Data, 176 env: env{"HOME": "/xHOMEx/me", "XDG_DATA_HOME": "/foo/data"}, 177 paths: []string{"/foo/data", "/usr/local/share", "/usr/share"}, 178 }, 179 { 180 dir: Data, 181 env: env{"XDG_DATA_HOME": "/foo/data", "XDG_DATA_DIRS": "/mybacon/data"}, 182 paths: []string{"/foo/data", "/mybacon/data"}, 183 }, 184 { 185 dir: Data, 186 env: env{"XDG_DATA_HOME": "/foo/data", "XDG_DATA_DIRS": "/mybacon/data:/eggs/data"}, 187 paths: []string{"/foo/data", "/mybacon/data", "/eggs/data"}, 188 }, 189 { 190 dir: Data, 191 env: env{"XDG_DATA_HOME": "/foo/data", "XDG_DATA_DIRS": "/mybacon/data:/eggs/data:/woka/woka"}, 192 paths: []string{"/foo/data", "/mybacon/data", "/eggs/data", "/woka/woka"}, 193 }, 194 { 195 dir: Data, 196 env: env{"XDG_DATA_HOME": "/foo/data", "XDG_DATA_DIRS": "/mybacon/data:relative/path:/woka/woka"}, 197 paths: []string{"/foo/data", "/mybacon/data", "/woka/woka"}, 198 }, 199 { 200 dir: Data, 201 env: env{"XDG_DATA_HOME": "relative/path", "XDG_DATA_DIRS": "/mybacon/data:relative/path:/woka/woka"}, 202 paths: []string{"/mybacon/data", "/woka/woka"}, 203 }, 204 { 205 dir: Data, 206 env: env{"XDG_DATA_DIRS": "/mybacon/data:/eggs/data:/woka/woka"}, 207 paths: []string{"/mybacon/data", "/eggs/data", "/woka/woka"}, 208 }, 209 { 210 dir: Config, 211 env: env{"XDG_CONFIG_HOME": "/foo/config", "XDG_CONFIG_DIRS": "/mybacon/config:/eggs/config:/woka/woka"}, 212 paths: []string{"/foo/config", "/mybacon/config", "/eggs/config", "/woka/woka"}, 213 }, 214 { 215 // Cache only has primary dir 216 dir: Cache, 217 env: env{"XDG_CACHE_HOME": "/foo/cache", "XDG_CACHE_DIRS": "/mybacon/config:/eggs/config:/woka/woka"}, 218 paths: []string{"/foo/cache"}, 219 }, 220 { 221 dir: Runtime, 222 env: env{"XDG_RUNTIME_DIR": secureDir}, 223 paths: []string{secureDir}, 224 }, 225 { 226 dir: Runtime, 227 env: env{"XDG_RUNTIME_DIR": allopenDir}, 228 paths: []string{}, 229 }, 230 } 231 for _, test := range tests { 232 test.env.set() 233 paths := test.dir.SearchPaths() 234 if !stringsEqual(paths, test.paths) { 235 t.Errorf("In environment %v, %v.SearchPaths() = %q; want %q", test.env, test.dir, paths, test.paths) 236 } 237 } 238 } 239 240 func TestDir_Open(t *testing.T) { 241 td := newTempDir(t) 242 defer td.cleanup() 243 junkDir := td.mkdir("junk", 0777) 244 dir1 := td.mkdir("dir1", 0777) 245 dir2 := td.mkdir("dir2", 0777) 246 dir3 := td.mkdir("dir3", 0777) 247 td.newFile("dir1/foo.txt", "foo") 248 td.newFile("dir1/multiple.txt", "1") 249 td.newFile("dir2/bar.txt", "bar") 250 td.newFile("dir2/only2_3.txt", "this is 2") 251 td.newFile("dir2/multiple.txt", "2") 252 td.newFile("dir3/multiple.txt", "3") 253 td.newFile("dir3/only2_3.txt", "this is 3") 254 255 tests := []struct { 256 dir Dir 257 env env 258 name string 259 260 path string 261 err bool 262 }{ 263 { 264 dir: Data, 265 env: env{}, 266 name: "foo.txt", 267 err: true, 268 }, 269 { 270 dir: Data, 271 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": junkDir}, 272 name: "foo.txt", 273 path: filepath.Join(dir1, "foo.txt"), 274 }, 275 { 276 dir: Data, 277 env: env{"XDG_DATA_HOME": junkDir, "XDG_DATA_DIRS": junkDir}, 278 name: "foo.txt", 279 err: true, 280 }, 281 { 282 dir: Data, 283 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": dir2}, 284 name: "foo.txt", 285 path: filepath.Join(dir1, "foo.txt"), 286 }, 287 { 288 dir: Data, 289 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": dir2}, 290 name: "bar.txt", 291 path: filepath.Join(dir2, "bar.txt"), 292 }, 293 { 294 dir: Data, 295 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": dir2 + ":" + dir3}, 296 name: "NOTREAL.txt", 297 err: true, 298 }, 299 { 300 dir: Data, 301 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": dir2 + ":" + dir3}, 302 name: "foo.txt", 303 path: filepath.Join(dir1, "foo.txt"), 304 }, 305 { 306 dir: Data, 307 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": dir2 + ":" + dir3}, 308 name: "bar.txt", 309 path: filepath.Join(dir2, "bar.txt"), 310 }, 311 { 312 dir: Data, 313 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": dir2 + ":" + dir3}, 314 name: "multiple.txt", 315 path: filepath.Join(dir1, "multiple.txt"), 316 }, 317 { 318 dir: Data, 319 env: env{"XDG_DATA_HOME": dir1, "XDG_DATA_DIRS": dir2 + ":" + dir3}, 320 name: "only2_3.txt", 321 path: filepath.Join(dir2, "only2_3.txt"), 322 }, 323 } 324 for _, test := range tests { 325 test.env.set() 326 f, err := test.dir.Open(test.name) 327 switch { 328 case err == nil && test.err: 329 t.Errorf("In environment %v, %v.Open(%q) succeeded; want error", test.env, test.dir, test.name) 330 case err == nil && !test.err && f.Name() != test.path: 331 t.Errorf("In environment %v, %v.Open(%q).Name() = %q; want %q", test.env, test.dir, test.name, f.Name(), test.path) 332 case err != nil && !test.err: 333 t.Errorf("In environment %v, %v.Open(%q) error: %v", test.env, test.dir, test.name, err) 334 } 335 if f != nil { 336 f.Close() 337 } 338 } 339 } 340 341 func TestDir_Create(t *testing.T) { 342 td := newTempDir(t) 343 defer td.cleanup() 344 junkDir := td.mkdir("junk", 0777) 345 dataDir := td.mkdir("data", 0777) 346 347 tests := []struct { 348 dir Dir 349 env env 350 name string 351 352 path string 353 err bool 354 permChecks []permCheck 355 }{ 356 { 357 dir: Data, 358 env: env{"XDG_DATA_HOME": dataDir, "XDG_DATA_DIRS": junkDir}, 359 name: "foo01", 360 path: filepath.Join(dataDir, "foo01"), 361 }, 362 { 363 dir: Data, 364 env: env{}, 365 name: "foo02", 366 err: true, 367 }, 368 { 369 dir: Data, 370 env: env{"XDG_DATA_HOME": dataDir, "XDG_DATA_DIRS": junkDir}, 371 name: filepath.Join("foo03", "bar"), 372 path: filepath.Join(dataDir, "foo03", "bar"), 373 permChecks: []permCheck{ 374 {filepath.Join(dataDir, "foo03"), 0700}, 375 }, 376 }, 377 { 378 dir: Data, 379 env: env{"XDG_DATA_HOME": filepath.Join(td.dir, "NOTREAL"), "XDG_DATA_DIRS": junkDir}, 380 name: filepath.Join("foo04", "bar"), 381 path: filepath.Join(td.dir, "NOTREAL", "foo04", "bar"), 382 permChecks: []permCheck{ 383 {filepath.Join(td.dir, "NOTREAL"), 0700}, 384 {filepath.Join(td.dir, "NOTREAL", "foo04"), 0700}, 385 }, 386 }, 387 } 388 for _, test := range tests { 389 test.env.set() 390 f, err := test.dir.Create(test.name) 391 switch { 392 case err == nil && test.err: 393 t.Errorf("In environment %v, %v.Create(%q) succeeded; want error", test.env, test.dir, test.name) 394 case err == nil && !test.err && f.Name() != test.path: 395 t.Errorf("In environment %v, %v.Create(%q).Name() = %q; want %q", test.env, test.dir, test.name, f.Name(), test.path) 396 case err != nil && !test.err: 397 t.Errorf("In environment %v, %v.Create(%q) error: %v", test.env, test.dir, test.name, err) 398 } 399 if f != nil { 400 f.Close() 401 } 402 for _, pc := range test.permChecks { 403 info, err := os.Stat(pc.name) 404 if err != nil { 405 t.Errorf("In environment %v, %v.Create(%q): stat %s error: %v", test.env, test.dir, test.name, pc.name, err) 406 continue 407 } 408 if perm := info.Mode().Perm(); perm != pc.perm { 409 t.Errorf("In environment %v, %v.Create(%q): %s has permission %v; want %v", test.env, test.dir, test.name, pc.name, perm, pc.perm) 410 } 411 } 412 } 413 } 414 415 func stringsEqual(a, b []string) bool { 416 if len(a) != len(b) { 417 return false 418 } 419 for i := range a { 420 if a[i] != b[i] { 421 return false 422 } 423 } 424 return true 425 } 426 427 type tempDir struct { 428 t *testing.T 429 dir string 430 } 431 432 func newTempDir(t *testing.T) *tempDir { 433 td := &tempDir{t: t} 434 var err error 435 td.dir, err = ioutil.TempDir("", "xdgdir_test") 436 if err != nil { 437 t.Fatal("making temp dir:", err) 438 } 439 return td 440 } 441 442 // newFile creates a file and returns its path. 443 func (td *tempDir) newFile(name string, data string) string { 444 path := filepath.Join(td.dir, name) 445 f, err := os.Create(path) 446 if err != nil { 447 td.t.Fatalf("newFile(%q, %q) error: %v", name, data, err) 448 } 449 _, werr := f.Write([]byte(data)) 450 cerr := f.Close() 451 if werr != nil { 452 td.t.Errorf("newFile(%q, %q) write error: %v", name, data, err) 453 } 454 if cerr != nil { 455 td.t.Errorf("newFile(%q, %q) close error: %v", name, data, err) 456 } 457 if werr != nil || cerr != nil { 458 td.t.FailNow() 459 } 460 return path 461 } 462 463 // mkdir creates a directory and returns its path. 464 func (td *tempDir) mkdir(name string, perm os.FileMode) string { 465 path := filepath.Join(td.dir, name) 466 err := os.Mkdir(path, perm) 467 if err != nil { 468 td.t.Fatal(err) 469 } 470 return path 471 } 472 473 func (td *tempDir) cleanup() { 474 err := os.RemoveAll(td.dir) 475 if err != nil { 476 td.t.Log("failed to clean up temp dir:", err) 477 } 478 } 479 480 type permCheck struct { 481 name string 482 perm os.FileMode 483 } 484 485 type env map[string]string 486 487 func (e env) set() { 488 getenv = func(key string) string { 489 return e[key] 490 } 491 findHome = func() string { 492 return e["HOME"] 493 } 494 }