1// Copyright 2015 Google Inc. All rights reserved 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package kati 16 17import ( 18 "os" 19 "path/filepath" 20 "reflect" 21 "strings" 22 "testing" 23) 24 25type mockfs struct { 26 id fileid 27 ofscache *fsCacheT 28} 29 30func newFS() *mockfs { 31 fs := &mockfs{ 32 ofscache: fsCache, 33 } 34 fsCache = &fsCacheT{ 35 ids: make(map[string]fileid), 36 dirents: make(map[fileid][]dirent), 37 } 38 fsCache.ids["."] = fs.dir(".").id 39 return fs 40} 41 42func (m *mockfs) dump(t *testing.T) { 43 t.Log("fs ids:") 44 for name, id := range fsCache.ids { 45 t.Logf(" %q=%v", name, id) 46 } 47 t.Log("fs dirents:") 48 for id, ents := range fsCache.dirents { 49 t.Logf(" %v:", id) 50 for _, ent := range ents { 51 t.Logf(" %#v", ent) 52 } 53 } 54} 55 56func (m *mockfs) close() { 57 fsCache = m.ofscache 58} 59 60func (m *mockfs) dirent(name string, mode os.FileMode) dirent { 61 id := m.id 62 m.id.ino++ 63 return dirent{id: id, name: name, mode: mode, lmode: mode} 64} 65 66func (m *mockfs) addent(name string, ent dirent) { 67 dir, name := filepath.Split(name) 68 dir = strings.TrimSuffix(dir, string(filepath.Separator)) 69 if dir == "" { 70 dir = "." 71 } 72 di, ok := fsCache.ids[dir] 73 if !ok { 74 if dir == "." { 75 panic(". not found:" + name) 76 } 77 de := m.add(m.dir, dir) 78 fsCache.ids[dir] = de.id 79 di = de.id 80 } 81 for _, e := range fsCache.dirents[di] { 82 if e.name == ent.name { 83 return 84 } 85 } 86 fsCache.dirents[di] = append(fsCache.dirents[di], ent) 87} 88 89func (m *mockfs) add(t func(string) dirent, name string) dirent { 90 ent := t(filepath.Base(name)) 91 m.addent(name, ent) 92 return ent 93} 94 95func (m *mockfs) symlink(name string, ent dirent) { 96 lent := ent 97 lent.lmode = os.ModeSymlink 98 lent.name = filepath.Base(name) 99 m.addent(name, lent) 100} 101 102func (m *mockfs) dirref(name string) dirent { 103 id := fsCache.ids[name] 104 return dirent{id: id, name: filepath.Base(name), mode: os.ModeDir, lmode: os.ModeDir} 105} 106 107func (m *mockfs) notfound() dirent { return dirent{id: invalidFileid} } 108func (m *mockfs) dir(name string) dirent { return m.dirent(name, os.ModeDir) } 109func (m *mockfs) file(name string) dirent { return m.dirent(name, os.FileMode(0644)) } 110 111func TestFilepathClean(t *testing.T) { 112 fs := newFS() 113 defer fs.close() 114 di := fs.add(fs.dir, "dir") 115 fs.symlink("link", di) 116 117 fs.dump(t) 118 119 for _, tc := range []struct { 120 path string 121 want string 122 }{ 123 {path: "foo", want: "foo"}, 124 {path: ".", want: "."}, 125 {path: "./", want: "."}, 126 {path: ".///", want: "."}, 127 {path: "", want: "."}, 128 {path: "foo/bar", want: "foo/bar"}, 129 {path: "./foo", want: "foo"}, 130 {path: "foo///", want: "foo"}, 131 {path: "foo//bar", want: "foo/bar"}, 132 {path: "foo/../bar", want: "foo/../bar"}, // foo doesn't exist 133 {path: "dir/../bar", want: "bar"}, // dir is real dir 134 {path: "link/../bar", want: "link/../bar"}, // link is symlink 135 {path: "foo/./bar", want: "foo/bar"}, 136 {path: "/foo/bar", want: "/foo/bar"}, 137 } { 138 if got, want := filepathClean(tc.path), tc.want; got != want { 139 t.Errorf("filepathClean(%q)=%q; want=%q", tc.path, got, want) 140 } 141 } 142} 143 144func TestParseFindCommand(t *testing.T) { 145 fs := newFS() 146 defer fs.close() 147 fs.add(fs.dir, "testdir") 148 149 maxdepth := 1<<31 - 1 150 for _, tc := range []struct { 151 cmd string 152 want findCommand 153 }{ 154 { 155 cmd: "find testdir", 156 want: findCommand{ 157 finddirs: []string{"testdir"}, 158 ops: []findOp{findOpPrint{}}, 159 depth: maxdepth, 160 }, 161 }, 162 { 163 cmd: "find .", 164 want: findCommand{ 165 finddirs: []string{"."}, 166 ops: []findOp{findOpPrint{}}, 167 depth: maxdepth, 168 }, 169 }, 170 { 171 cmd: "find ", 172 want: findCommand{ 173 finddirs: []string{"."}, 174 ops: []findOp{findOpPrint{}}, 175 depth: maxdepth, 176 }, 177 }, 178 { 179 cmd: "find testdir/../testdir", 180 want: findCommand{ 181 finddirs: []string{"testdir/../testdir"}, 182 ops: []findOp{findOpPrint{}}, 183 depth: maxdepth, 184 }, 185 }, 186 { 187 cmd: "find testdir -print", 188 want: findCommand{ 189 finddirs: []string{"testdir"}, 190 ops: []findOp{findOpPrint{}}, 191 depth: maxdepth, 192 }, 193 }, 194 { 195 cmd: "find testdir -name foo", 196 want: findCommand{ 197 finddirs: []string{"testdir"}, 198 ops: []findOp{findOpName("foo"), findOpPrint{}}, 199 depth: maxdepth, 200 }, 201 }, 202 { 203 cmd: `find testdir -name "file1"`, 204 want: findCommand{ 205 finddirs: []string{"testdir"}, 206 ops: []findOp{findOpName("file1"), findOpPrint{}}, 207 depth: maxdepth, 208 }, 209 }, 210 { 211 cmd: `find testdir -name "*1"`, 212 want: findCommand{ 213 finddirs: []string{"testdir"}, 214 ops: []findOp{findOpName("*1"), findOpPrint{}}, 215 depth: maxdepth, 216 }, 217 }, 218 { 219 cmd: `find testdir -name "*1" -and -name "file*"`, 220 want: findCommand{ 221 finddirs: []string{"testdir"}, 222 ops: []findOp{findOpAnd{findOpName("*1"), findOpName("file*")}, findOpPrint{}}, 223 depth: maxdepth, 224 }, 225 }, 226 { 227 cmd: `find testdir -name "*1" -or -name "file*"`, 228 want: findCommand{ 229 finddirs: []string{"testdir"}, 230 ops: []findOp{findOpOr{findOpName("*1"), findOpName("file*")}, findOpPrint{}}, 231 depth: maxdepth, 232 }, 233 }, 234 { 235 cmd: `find testdir -name "*1" -or -type f`, 236 want: findCommand{ 237 finddirs: []string{"testdir"}, 238 ops: []findOp{findOpOr{findOpName("*1"), findOpRegular{}}, findOpPrint{}}, 239 depth: maxdepth, 240 }, 241 }, 242 { 243 cmd: `find testdir -name "*1" -or -not -type f`, 244 want: findCommand{ 245 finddirs: []string{"testdir"}, 246 ops: []findOp{findOpOr{findOpName("*1"), findOpNot{findOpRegular{}}}, findOpPrint{}}, 247 depth: maxdepth, 248 }, 249 }, 250 { 251 cmd: `find testdir -name "*1" -or \! -type f`, 252 want: findCommand{ 253 finddirs: []string{"testdir"}, 254 ops: []findOp{findOpOr{findOpName("*1"), findOpNot{findOpRegular{}}}, findOpPrint{}}, 255 depth: maxdepth, 256 }, 257 }, 258 { 259 cmd: `find testdir -name "*1" -or -type d`, 260 want: findCommand{ 261 finddirs: []string{"testdir"}, 262 ops: []findOp{findOpOr{findOpName("*1"), findOpType{mode: os.ModeDir}}, findOpPrint{}}, 263 depth: maxdepth, 264 }, 265 }, 266 { 267 cmd: `find testdir -name "*1" -or -type l`, 268 want: findCommand{ 269 finddirs: []string{"testdir"}, 270 ops: []findOp{findOpOr{findOpName("*1"), findOpType{mode: os.ModeSymlink}}, findOpPrint{}}, 271 depth: maxdepth, 272 }, 273 }, 274 { 275 cmd: `find testdir -name "*1" -a -type l -o -name "dir*"`, 276 want: findCommand{ 277 finddirs: []string{"testdir"}, 278 ops: []findOp{findOpOr{findOpAnd([]findOp{findOpName("*1"), findOpType{mode: os.ModeSymlink}}), findOpName("dir*")}, findOpPrint{}}, 279 depth: maxdepth, 280 }, 281 }, 282 { 283 cmd: `find testdir \( -name "dir*" -o -name "*1" \) -a -type f`, 284 want: findCommand{ 285 finddirs: []string{"testdir"}, 286 ops: []findOp{findOpAnd([]findOp{findOpOr{findOpName("dir*"), findOpName("*1")}, findOpRegular{}}), findOpPrint{}}, 287 depth: maxdepth, 288 }, 289 }, 290 { 291 cmd: `cd testdir && find`, 292 want: findCommand{ 293 chdir: "testdir", 294 finddirs: []string{"."}, 295 ops: []findOp{findOpPrint{}}, 296 depth: maxdepth, 297 }, 298 }, 299 { 300 cmd: `test -d testdir && find testdir`, 301 want: findCommand{ 302 testdir: "testdir", 303 finddirs: []string{"testdir"}, 304 ops: []findOp{findOpPrint{}}, 305 depth: maxdepth, 306 }, 307 }, 308 { 309 cmd: `if [ -d testdir ] ; then find testdir ; fi`, 310 want: findCommand{ 311 testdir: "testdir", 312 finddirs: []string{"testdir"}, 313 ops: []findOp{findOpPrint{}}, 314 depth: maxdepth, 315 }, 316 }, 317 { 318 cmd: `if [ -d testdir ]; then find testdir; fi`, 319 want: findCommand{ 320 testdir: "testdir", 321 finddirs: []string{"testdir"}, 322 ops: []findOp{findOpPrint{}}, 323 depth: maxdepth, 324 }, 325 }, 326 { 327 cmd: `if [ -d testdir ]; then cd testdir && find .; fi`, 328 want: findCommand{ 329 chdir: "testdir", 330 testdir: "testdir", 331 finddirs: []string{"."}, 332 ops: []findOp{findOpPrint{}}, 333 depth: maxdepth, 334 }, 335 }, 336 { 337 cmd: `find testdir -name dir2 -prune -o -name file1`, 338 want: findCommand{ 339 finddirs: []string{"testdir"}, 340 ops: []findOp{findOpOr{findOpAnd([]findOp{findOpName("dir2"), findOpPrune{}}), findOpName("file1")}, findOpPrint{}}, 341 depth: maxdepth, 342 }, 343 }, 344 { 345 cmd: `find testdir testdir`, 346 want: findCommand{ 347 finddirs: []string{"testdir", "testdir"}, 348 ops: []findOp{findOpPrint{}}, 349 depth: maxdepth, 350 }, 351 }, 352 { 353 cmd: `find -L testdir -type f`, 354 want: findCommand{ 355 finddirs: []string{"testdir"}, 356 followSymlinks: true, 357 ops: []findOp{findOpRegular{followSymlinks: true}, findOpPrint{}}, 358 depth: maxdepth, 359 }, 360 }, 361 { 362 cmd: `cd testdir; find -L . -type f`, 363 want: findCommand{ 364 chdir: "testdir", 365 finddirs: []string{"."}, 366 followSymlinks: true, 367 ops: []findOp{findOpRegular{followSymlinks: true}, findOpPrint{}}, 368 depth: maxdepth, 369 }, 370 }, 371 { 372 cmd: `find testdir -maxdepth 1`, 373 want: findCommand{ 374 finddirs: []string{"testdir"}, 375 ops: []findOp{findOpPrint{}}, 376 depth: 1, 377 }, 378 }, 379 { 380 cmd: `find testdir -maxdepth 0`, 381 want: findCommand{ 382 finddirs: []string{"testdir"}, 383 ops: []findOp{findOpPrint{}}, 384 depth: 0, 385 }, 386 }, 387 } { 388 fc, err := parseFindCommand(tc.cmd) 389 if err != nil { 390 t.Errorf("parseFindCommand(%q)=_, %v; want=_, <nil>", tc.cmd, err) 391 continue 392 } 393 if got, want := fc, tc.want; !reflect.DeepEqual(got, want) { 394 t.Errorf("parseFindCommand(%q)=%#v\n want=%#v\n", tc.cmd, got, want) 395 } 396 } 397 398} 399 400func TestParseFindCommandFail(t *testing.T) { 401 for _, cmd := range []string{ 402 `find testdir -maxdepth hoge`, 403 `find testdir -maxdepth 1hoge`, 404 `find testdir -maxdepth -1`, 405 } { 406 _, err := parseFindCommand(cmd) 407 if err == nil { 408 t.Errorf("parseFindCommand(%q)=_, <nil>; want=_, err", cmd) 409 } 410 } 411} 412 413func TestFind(t *testing.T) { 414 fs := newFS() 415 defer fs.close() 416 fs.add(fs.file, "Makefile") 417 fs.add(fs.file, "testdir/file1") 418 fs.add(fs.file, "testdir/file2") 419 file1 := fs.add(fs.file, "testdir/dir1/file1") 420 dir1 := fs.dirref("testdir/dir1") 421 fs.add(fs.file, "testdir/dir1/file2") 422 fs.add(fs.file, "testdir/dir2/file1") 423 fs.add(fs.file, "testdir/dir2/file2") 424 fs.symlink("testdir/dir2/link1", file1) 425 fs.symlink("testdir/dir2/link2", dir1) 426 fs.symlink("testdir/dir2/link3", fs.notfound()) 427 428 fs.dump(t) 429 430 maxdepth := 1<<31 - 1 431 for _, tc := range []struct { 432 fc findCommand 433 want string 434 }{ 435 { 436 fc: findCommand{ 437 finddirs: []string{"testdir"}, 438 ops: []findOp{findOpPrint{}}, 439 depth: maxdepth, 440 }, 441 want: `testdir testdir/file1 testdir/file2 testdir/dir1 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1 testdir/dir2/link2 testdir/dir2/link3`, 442 }, 443 { 444 fc: findCommand{ 445 finddirs: []string{"."}, 446 ops: []findOp{findOpPrint{}}, 447 depth: maxdepth, 448 }, 449 want: `. ./Makefile ./testdir ./testdir/file1 ./testdir/file2 ./testdir/dir1 ./testdir/dir1/file1 ./testdir/dir1/file2 ./testdir/dir2 ./testdir/dir2/file1 ./testdir/dir2/file2 ./testdir/dir2/link1 ./testdir/dir2/link2 ./testdir/dir2/link3`, 450 }, 451 { 452 fc: findCommand{ 453 finddirs: []string{"./"}, 454 ops: []findOp{findOpPrint{}}, 455 depth: maxdepth, 456 }, 457 want: `./ ./Makefile ./testdir ./testdir/file1 ./testdir/file2 ./testdir/dir1 ./testdir/dir1/file1 ./testdir/dir1/file2 ./testdir/dir2 ./testdir/dir2/file1 ./testdir/dir2/file2 ./testdir/dir2/link1 ./testdir/dir2/link2 ./testdir/dir2/link3`, 458 }, 459 { 460 fc: findCommand{ 461 finddirs: []string{".///"}, 462 ops: []findOp{findOpPrint{}}, 463 depth: maxdepth, 464 }, 465 want: `./// .///Makefile .///testdir .///testdir/file1 .///testdir/file2 .///testdir/dir1 .///testdir/dir1/file1 .///testdir/dir1/file2 .///testdir/dir2 .///testdir/dir2/file1 .///testdir/dir2/file2 .///testdir/dir2/link1 .///testdir/dir2/link2 .///testdir/dir2/link3`, 466 }, 467 { 468 fc: findCommand{ 469 finddirs: []string{"./."}, 470 ops: []findOp{findOpPrint{}}, 471 depth: maxdepth, 472 }, 473 want: `./. ././Makefile ././testdir ././testdir/file1 ././testdir/file2 ././testdir/dir1 ././testdir/dir1/file1 ././testdir/dir1/file2 ././testdir/dir2 ././testdir/dir2/file1 ././testdir/dir2/file2 ././testdir/dir2/link1 ././testdir/dir2/link2 ././testdir/dir2/link3`, 474 }, 475 { 476 fc: findCommand{ 477 finddirs: []string{"././"}, 478 ops: []findOp{findOpPrint{}}, 479 depth: maxdepth, 480 }, 481 want: `././ ././Makefile ././testdir ././testdir/file1 ././testdir/file2 ././testdir/dir1 ././testdir/dir1/file1 ././testdir/dir1/file2 ././testdir/dir2 ././testdir/dir2/file1 ././testdir/dir2/file2 ././testdir/dir2/link1 ././testdir/dir2/link2 ././testdir/dir2/link3`, 482 }, 483 { 484 fc: findCommand{ 485 finddirs: []string{"testdir/../testdir"}, 486 ops: []findOp{findOpPrint{}}, 487 depth: maxdepth, 488 }, 489 want: `testdir/../testdir testdir/../testdir/file1 testdir/../testdir/file2 testdir/../testdir/dir1 testdir/../testdir/dir1/file1 testdir/../testdir/dir1/file2 testdir/../testdir/dir2 testdir/../testdir/dir2/file1 testdir/../testdir/dir2/file2 testdir/../testdir/dir2/link1 testdir/../testdir/dir2/link2 testdir/../testdir/dir2/link3`, 490 }, 491 { 492 fc: findCommand{ 493 finddirs: []string{"testdir"}, 494 ops: []findOp{findOpName("foo"), findOpPrint{}}, 495 depth: maxdepth, 496 }, 497 want: ``, 498 }, 499 { 500 fc: findCommand{ 501 finddirs: []string{"testdir"}, 502 ops: []findOp{findOpName("file1"), findOpPrint{}}, 503 depth: maxdepth, 504 }, 505 want: `testdir/file1 testdir/dir1/file1 testdir/dir2/file1`, 506 }, 507 { 508 fc: findCommand{ 509 finddirs: []string{"testdir"}, 510 ops: []findOp{findOpAnd{findOpName("*1"), findOpName("file*")}, findOpPrint{}}, 511 depth: maxdepth, 512 }, 513 want: `testdir/file1 testdir/dir1/file1 testdir/dir2/file1`, 514 }, 515 { 516 fc: findCommand{ 517 finddirs: []string{"testdir"}, 518 ops: []findOp{findOpOr{findOpName("*1"), findOpName("file*")}, findOpPrint{}}, 519 depth: maxdepth, 520 }, 521 want: `testdir/file1 testdir/file2 testdir/dir1 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1`, 522 }, 523 { 524 fc: findCommand{ 525 finddirs: []string{"testdir"}, 526 ops: []findOp{findOpOr{findOpName("*1"), findOpRegular{}}, findOpPrint{}}, 527 depth: maxdepth, 528 }, 529 want: `testdir/file1 testdir/file2 testdir/dir1 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1`, 530 }, 531 { 532 fc: findCommand{ 533 finddirs: []string{"testdir"}, 534 ops: []findOp{findOpOr{findOpName("*1"), findOpNot{findOpRegular{}}}, findOpPrint{}}, 535 depth: maxdepth, 536 }, 537 want: `testdir testdir/file1 testdir/dir1 testdir/dir1/file1 testdir/dir2 testdir/dir2/file1 testdir/dir2/link1 testdir/dir2/link2 testdir/dir2/link3`, 538 }, 539 { 540 fc: findCommand{ 541 finddirs: []string{"testdir"}, 542 ops: []findOp{findOpOr{findOpName("*1"), findOpType{mode: os.ModeDir}}, findOpPrint{}}, 543 depth: maxdepth, 544 }, 545 want: `testdir testdir/file1 testdir/dir1 testdir/dir1/file1 testdir/dir2 testdir/dir2/file1 testdir/dir2/link1`, 546 }, 547 { 548 fc: findCommand{ 549 finddirs: []string{"testdir"}, 550 ops: []findOp{findOpOr{findOpName("*1"), findOpType{mode: os.ModeSymlink}}, findOpPrint{}}, 551 depth: maxdepth, 552 }, 553 want: `testdir/file1 testdir/dir1 testdir/dir1/file1 testdir/dir2/file1 testdir/dir2/link1 testdir/dir2/link2 testdir/dir2/link3`, 554 }, 555 { 556 fc: findCommand{ 557 finddirs: []string{"testdir"}, 558 ops: []findOp{findOpOr{findOpAnd([]findOp{findOpName("*1"), findOpType{mode: os.ModeSymlink}}), findOpName("dir*")}, findOpPrint{}}, 559 depth: maxdepth, 560 }, 561 want: `testdir/dir1 testdir/dir2 testdir/dir2/link1`, 562 }, 563 { 564 fc: findCommand{ 565 finddirs: []string{"testdir"}, 566 ops: []findOp{findOpOr{findOpAnd([]findOp{findOpName("*1"), findOpType{mode: os.ModeSymlink}}), findOpName("dir*")}, findOpPrint{}}, 567 depth: maxdepth, 568 }, 569 want: `testdir/dir1 testdir/dir2 testdir/dir2/link1`, 570 }, 571 { 572 fc: findCommand{ 573 finddirs: []string{"testdir"}, 574 ops: []findOp{findOpAnd([]findOp{findOpOr{findOpName("dir*"), findOpName("*1")}, findOpRegular{}}), findOpPrint{}}, 575 depth: maxdepth, 576 }, 577 want: `testdir/file1 testdir/dir1/file1 testdir/dir2/file1`, 578 }, 579 { 580 fc: findCommand{ 581 chdir: "testdir", 582 finddirs: []string{"."}, 583 ops: []findOp{findOpPrint{}}, 584 depth: maxdepth, 585 }, 586 want: `. ./file1 ./file2 ./dir1 ./dir1/file1 ./dir1/file2 ./dir2 ./dir2/file1 ./dir2/file2 ./dir2/link1 ./dir2/link2 ./dir2/link3`, 587 }, 588 { 589 fc: findCommand{ 590 chdir: "testdir", 591 finddirs: []string{"../testdir"}, 592 ops: []findOp{findOpPrint{}}, 593 depth: maxdepth, 594 }, 595 want: `../testdir ../testdir/file1 ../testdir/file2 ../testdir/dir1 ../testdir/dir1/file1 ../testdir/dir1/file2 ../testdir/dir2 ../testdir/dir2/file1 ../testdir/dir2/file2 ../testdir/dir2/link1 ../testdir/dir2/link2 ../testdir/dir2/link3`, 596 }, 597 { 598 fc: findCommand{ 599 testdir: "testdir", 600 finddirs: []string{"testdir"}, 601 ops: []findOp{findOpPrint{}}, 602 depth: maxdepth, 603 }, 604 want: `testdir testdir/file1 testdir/file2 testdir/dir1 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1 testdir/dir2/link2 testdir/dir2/link3`, 605 }, 606 { 607 fc: findCommand{ 608 chdir: "testdir", 609 testdir: "testdir", 610 finddirs: []string{"."}, 611 ops: []findOp{findOpPrint{}}, 612 depth: maxdepth, 613 }, 614 want: `. ./file1 ./file2 ./dir1 ./dir1/file1 ./dir1/file2 ./dir2 ./dir2/file1 ./dir2/file2 ./dir2/link1 ./dir2/link2 ./dir2/link3`, 615 }, 616 { 617 fc: findCommand{ 618 finddirs: []string{"testdir"}, 619 ops: []findOp{findOpOr{findOpAnd([]findOp{findOpName("dir2"), findOpPrune{}}), findOpName("file1")}, findOpPrint{}}, 620 depth: maxdepth, 621 }, 622 want: `testdir/file1 testdir/dir1/file1 testdir/dir2`, 623 }, 624 { 625 fc: findCommand{ 626 finddirs: []string{"testdir", "testdir"}, 627 ops: []findOp{findOpPrint{}}, 628 depth: maxdepth, 629 }, 630 want: `testdir testdir/file1 testdir/file2 testdir/dir1 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1 testdir/dir2/link2 testdir/dir2/link3 testdir testdir/file1 testdir/file2 testdir/dir1 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1 testdir/dir2/link2 testdir/dir2/link3`, 631 }, 632 // symlink 633 { 634 fc: findCommand{ 635 finddirs: []string{"testdir"}, 636 followSymlinks: true, 637 ops: []findOp{findOpRegular{followSymlinks: true}, findOpPrint{}}, 638 depth: maxdepth, 639 }, 640 want: `testdir/file1 testdir/file2 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1 testdir/dir2/link2/file1 testdir/dir2/link2/file2`, 641 }, 642 { 643 fc: findCommand{ 644 finddirs: []string{"testdir"}, 645 followSymlinks: true, 646 ops: []findOp{findOpType{mode: os.ModeDir, followSymlinks: true}, findOpPrint{}}, 647 depth: maxdepth, 648 }, 649 want: `testdir testdir/dir1 testdir/dir2 testdir/dir2/link2`, 650 }, 651 { 652 fc: findCommand{ 653 finddirs: []string{"testdir"}, 654 followSymlinks: true, 655 ops: []findOp{findOpType{mode: os.ModeSymlink, followSymlinks: true}, findOpPrint{}}, 656 depth: maxdepth, 657 }, 658 want: `testdir/dir2/link3`, 659 }, 660 { 661 fc: findCommand{ 662 chdir: "testdir", 663 finddirs: []string{"."}, 664 followSymlinks: true, 665 ops: []findOp{findOpRegular{followSymlinks: true}, findOpPrint{}}, 666 depth: maxdepth, 667 }, 668 want: `./file1 ./file2 ./dir1/file1 ./dir1/file2 ./dir2/file1 ./dir2/file2 ./dir2/link1 ./dir2/link2/file1 ./dir2/link2/file2`, 669 }, 670 // maxdepth 671 { 672 fc: findCommand{ 673 finddirs: []string{"testdir"}, 674 ops: []findOp{findOpPrint{}}, 675 depth: 1, 676 }, 677 want: `testdir testdir/file1 testdir/file2 testdir/dir1 testdir/dir2`, 678 }, 679 { 680 fc: findCommand{ 681 finddirs: []string{"testdir"}, 682 ops: []findOp{findOpPrint{}}, 683 depth: 2, 684 }, 685 want: `testdir testdir/file1 testdir/file2 testdir/dir1 testdir/dir1/file1 testdir/dir1/file2 testdir/dir2 testdir/dir2/file1 testdir/dir2/file2 testdir/dir2/link1 testdir/dir2/link2 testdir/dir2/link3`, 686 }, 687 { 688 fc: findCommand{ 689 finddirs: []string{"testdir"}, 690 ops: []findOp{findOpPrint{}}, 691 depth: 0, 692 }, 693 want: `testdir`, 694 }, 695 } { 696 var wb wordBuffer 697 tc.fc.run(&wb) 698 if got, want := wb.buf.String(), tc.want; got != want { 699 t.Errorf("%#v\n got %q\n want %q", tc.fc, got, want) 700 } 701 } 702} 703 704func TestParseFindleavesCommand(t *testing.T) { 705 for _, tc := range []struct { 706 cmd string 707 want findleavesCommand 708 }{ 709 { 710 cmd: `build/tools/findleaves.py --prune=out --prune=.repo --prune=.git . CleanSpec.mk`, 711 want: findleavesCommand{ 712 name: "CleanSpec.mk", 713 dirs: []string{"."}, 714 prunes: []string{"out", ".repo", ".git"}, 715 mindepth: -1, 716 }, 717 }, 718 { 719 cmd: `build/tools/findleaves.py --prune=out --prune=.repo --prune=.git --mindepth=2 art bionic Android.mk`, 720 want: findleavesCommand{ 721 name: "Android.mk", 722 dirs: []string{"art", "bionic"}, 723 prunes: []string{"out", ".repo", ".git"}, 724 mindepth: 2, 725 }, 726 }, 727 } { 728 fc, err := parseFindleavesCommand(tc.cmd) 729 if err != nil { 730 t.Errorf("parseFindleavesCommand(%q)=_, %v; want=_, <nil", tc.cmd, err) 731 continue 732 } 733 if got, want := fc, tc.want; !reflect.DeepEqual(got, want) { 734 t.Errorf("parseFindleavesCommand(%q)=%#v\n want=%#v\n", tc.cmd, got, want) 735 } 736 } 737} 738 739func TestFindleaves(t *testing.T) { 740 fs := newFS() 741 defer fs.close() 742 743 fs.add(fs.file, "art/Android.mk") 744 fs.add(fs.file, "art/compiler/Android.mk") 745 fs.add(fs.file, "art/CleanSpec.mk") 746 fs.add(fs.file, "bionic/Android.mk") 747 fs.add(fs.file, "bionic/CleanSpec.mk") 748 fs.add(fs.file, "bootable/recovery/Android.mk") 749 fs.add(fs.file, "bootable/recovery/CleanSpec.mk") 750 fs.add(fs.file, "frameworks/base/Android.mk") 751 fs.add(fs.file, "frameworks/base/CleanSpec.mk") 752 fs.add(fs.file, "frameworks/base/cmds/am/Android.mk") 753 fs.add(fs.file, "frameworks/base/cmds/pm/Android.mk") 754 fs.add(fs.file, "frameworks/base/location/Android.mk") 755 fs.add(fs.file, "frameworks/base/packages/WAPPushManager/CleanSpec.mk") 756 fs.add(fs.file, "out/outputfile") 757 fs.add(fs.file, "art/.git/index") 758 fs.add(fs.file, ".repo/manifests") 759 760 fs.dump(t) 761 762 for _, tc := range []struct { 763 fc findleavesCommand 764 want string 765 }{ 766 { 767 fc: findleavesCommand{ 768 name: "CleanSpec.mk", 769 dirs: []string{"."}, 770 prunes: []string{"out", ".repo", ".git"}, 771 mindepth: -1, 772 }, 773 want: `./art/CleanSpec.mk ./bionic/CleanSpec.mk ./bootable/recovery/CleanSpec.mk ./frameworks/base/CleanSpec.mk`, 774 }, 775 { 776 fc: findleavesCommand{ 777 name: "Android.mk", 778 dirs: []string{"art", "bionic", "frameworks/base"}, 779 prunes: []string{"out", ".repo", ".git"}, 780 mindepth: 2, 781 }, 782 want: `art/compiler/Android.mk frameworks/base/cmds/am/Android.mk frameworks/base/cmds/pm/Android.mk frameworks/base/location/Android.mk`, 783 }, 784 { 785 fc: findleavesCommand{ 786 name: "Android.mk", 787 dirs: []string{"art", "bionic", "frameworks/base"}, 788 prunes: []string{"out", ".repo", ".git"}, 789 mindepth: 3, 790 }, 791 want: `frameworks/base/cmds/am/Android.mk frameworks/base/cmds/pm/Android.mk`, 792 }, 793 } { 794 var wb wordBuffer 795 tc.fc.run(&wb) 796 if got, want := wb.buf.String(), tc.want; got != want { 797 t.Errorf("%#v\n got %q\n want %q", tc.fc, got, want) 798 } 799 } 800} 801