github.com/anishathalye/periscope@v0.3.5/internal/periscope/ls_test.go (about)

     1  package periscope
     2  
     3  import (
     4  	"github.com/anishathalye/periscope/internal/testfs"
     5  
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/spf13/afero"
    13  )
    14  
    15  func TestLsBasic(t *testing.T) {
    16  	fs := testfs.Read(`
    17  /d1/a [10000 1]
    18  /d1/b [1392 2]
    19  /d1/c [1191 3]
    20  /d2/a [10000 1]
    21  /d2/b [1392 2]
    22  /d2/c [1002 5]
    23  	`).Mkfs()
    24  	ps, out, _ := newTest(fs)
    25  	ps.Scan([]string{"/"}, &ScanOptions{})
    26  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
    27  	check(t, err)
    28  	got := strings.TrimRight(out.String(), "\n")
    29  	expected := strings.TrimSpace(`
    30  1 a
    31  1 b
    32    c
    33  	`)
    34  	if got != expected {
    35  		t.Fatalf("expected '%s', got '%s'", expected, got)
    36  	}
    37  }
    38  
    39  func TestLsOutsideScan(t *testing.T) {
    40  	fs := testfs.Read(`
    41  /d1/a [10000 1]
    42  /d1/a2 [10000 1]
    43  /d1/b [1002 5]
    44  /d2/b [1002 5]
    45  	`).Mkfs()
    46  	ps, out, _ := newTest(fs)
    47  	ps.Scan([]string{"/d2"}, &ScanOptions{})
    48  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
    49  	check(t, err)
    50  	got := strings.TrimRight(out.String(), "\n")
    51  	expected := strings.TrimSpace(`
    52  a
    53  a2
    54  b
    55  	`)
    56  	if got != expected {
    57  		t.Fatalf("expected '%s', got '%s'", expected, got)
    58  	}
    59  }
    60  
    61  func TestLsCountDuplicates(t *testing.T) {
    62  	fs := testfs.Read(`
    63  /d1/a [10000 1]
    64  /d1/b [1392 2]
    65  /d1/c [1191 3]
    66  /d2/a [10000 1]
    67  /d2/b [1392 2]
    68  /d2/c [1002 5]
    69  /d3/a [10000 1]
    70  /d4/a [10000 1]
    71  	`).Mkfs()
    72  	ps, out, _ := newTest(fs)
    73  	ps.Scan([]string{"/"}, &ScanOptions{})
    74  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
    75  	check(t, err)
    76  	got := strings.TrimRight(out.String(), "\n")
    77  	expected := strings.TrimSpace(`
    78  3 a
    79  1 b
    80    c
    81  	`)
    82  	if got != expected {
    83  		t.Fatalf("expected '%s', got '%s'", expected, got)
    84  	}
    85  }
    86  
    87  func TestLsCountOver10(t *testing.T) {
    88  	fs := testfs.Read(`
    89  /d1/a [10000 1]
    90  /d1/b [1234 2]
    91  /d2/a1 [10000 1]
    92  /d2/a2 [10000 1]
    93  /d2/a3 [10000 1]
    94  /d2/a4 [10000 1]
    95  /d2/a5 [10000 1]
    96  /d2/a6 [10000 1]
    97  /d2/a7 [10000 1]
    98  /d2/a8 [10000 1]
    99  /d2/a9 [10000 1]
   100  /d2/a10 [10000 1]
   101  /d2/a11 [10000 1]
   102  /d2/a12 [10000 1]
   103  /d2/b [1234 2]
   104  	`).Mkfs()
   105  	ps, out, _ := newTest(fs)
   106  	ps.Scan([]string{"/"}, &ScanOptions{})
   107  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
   108  	check(t, err)
   109  	got := strings.TrimRight(out.String(), "\n")
   110  	expected := strings.TrimSpace(`
   111  12 a
   112  1  b
   113  	`)
   114  	if got != expected {
   115  		t.Fatalf("expected '%s', got '%s'", expected, got)
   116  	}
   117  }
   118  
   119  func TestLsDuplicateSameDir(t *testing.T) {
   120  	fs := testfs.Read(`
   121  /d1/a [10000 1]
   122  /d1/a2 [10000 1]
   123  /d1/a3 [10000 1]
   124  /d1/b [1392 2]
   125  /d2/a [10000 1]
   126  /d2/b [1392 2]
   127  	`).Mkfs()
   128  	ps, out, _ := newTest(fs)
   129  	ps.Scan([]string{"/"}, &ScanOptions{})
   130  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
   131  	check(t, err)
   132  	got := strings.TrimRight(out.String(), "\n")
   133  	expected := strings.TrimSpace(`
   134  3 a
   135  3 a2
   136  3 a3
   137  1 b
   138  	`)
   139  	if got != expected {
   140  		t.Fatalf("expected '%s', got '%s'", expected, got)
   141  	}
   142  }
   143  
   144  func TestLsNoCountUnique(t *testing.T) {
   145  	fs := testfs.Read(`
   146  /d1/a [10000 1]
   147  /d1/b [1234 2]
   148  /d1/c [1111 3]
   149  	`).Mkfs()
   150  	ps, out, _ := newTest(fs)
   151  	ps.Scan([]string{"/"}, &ScanOptions{})
   152  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
   153  	check(t, err)
   154  	got := strings.TrimRight(out.String(), "\n")
   155  	expected := strings.TrimSpace(`
   156  a
   157  b
   158  c
   159  	`)
   160  	if got != expected {
   161  		t.Fatalf("expected '%s', got '%s'", expected, got)
   162  	}
   163  }
   164  
   165  func TestLsShowOnlyUnique(t *testing.T) {
   166  	fs := testfs.Read(`
   167  /d1/a [10000 1]
   168  /d1/b [1392 2]
   169  /d1/c [1191 3]
   170  /d1/d [1337 3]
   171  /d2/a [10000 1]
   172  /d2/b [1392 2]
   173  /d2/c [1002 5]
   174  	`).Mkfs()
   175  	ps, out, _ := newTest(fs)
   176  	ps.Scan([]string{"/"}, &ScanOptions{})
   177  	err := ps.Ls([]string{"/d1"}, &LsOptions{Unique: true})
   178  	check(t, err)
   179  	got := strings.TrimRight(out.String(), "\n")
   180  	expected := strings.TrimSpace(`
   181  c
   182  d
   183  	`)
   184  	if got != expected {
   185  		t.Fatalf("expected '%s', got '%s'", expected, got)
   186  	}
   187  }
   188  
   189  func TestLsShowOnlyDuplicate(t *testing.T) {
   190  	fs := testfs.Read(`
   191  /d1/a [10000 1]
   192  /d1/b [1392 2]
   193  /d1/c [1191 3]
   194  /d1/d [1337 3]
   195  /d2/a [10000 1]
   196  /d2/b [1392 2]
   197  /d2/c [1002 5]
   198  	`).Mkfs()
   199  	ps, out, _ := newTest(fs)
   200  	ps.Scan([]string{"/"}, &ScanOptions{})
   201  	err := ps.Ls([]string{"/d1"}, &LsOptions{Duplicate: true})
   202  	check(t, err)
   203  	got := strings.TrimRight(out.String(), "\n")
   204  	expected := strings.TrimSpace(`
   205  1 a
   206  1 b
   207  	`)
   208  	if got != expected {
   209  		t.Fatalf("expected '%s', got '%s'", expected, got)
   210  	}
   211  }
   212  
   213  func TestLsDirectories(t *testing.T) {
   214  	fs := testfs.Read(`
   215  /d1/a [10000 1]
   216  /d1/b [1392 2]
   217  /d1/dir/x [1191 3]
   218  /d2/a [10000 1]
   219  /d2/b [1392 2]
   220  	`).Mkfs()
   221  	ps, out, _ := newTest(fs)
   222  	ps.Scan([]string{"/"}, &ScanOptions{})
   223  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
   224  	check(t, err)
   225  	got := strings.TrimRight(out.String(), "\n")
   226  	expected := strings.TrimSpace(`
   227  1 a
   228  1 b
   229  d dir
   230  	`)
   231  	if got != expected {
   232  		t.Fatalf("expected '%s', got '%s'", expected, got)
   233  	}
   234  }
   235  
   236  func TestLsSpecialModes(t *testing.T) {
   237  	fs := afero.NewOsFs()
   238  	dir := tempDir()
   239  	defer os.RemoveAll(dir)
   240  	ioutil.WriteFile(filepath.Join(dir, "x"), []byte{'a'}, 0o644)
   241  	ioutil.WriteFile(filepath.Join(dir, "y"), []byte{'a'}, 0o644)
   242  	os.Symlink(filepath.Join(dir, "x"), filepath.Join(dir, "z"))
   243  	ps, out, _ := newTest(fs)
   244  	ps.Scan([]string{dir}, &ScanOptions{})
   245  	err := ps.Ls([]string{dir}, &LsOptions{})
   246  	check(t, err)
   247  	got := strings.TrimRight(out.String(), "\n")
   248  	expected := strings.TrimSpace(`
   249  1 x
   250  1 y
   251  L z
   252  	`)
   253  	if got != expected {
   254  		t.Fatalf("expected '%s', got '%s'", expected, got)
   255  	}
   256  }
   257  
   258  func TestLsVerbose(t *testing.T) {
   259  	fs := testfs.Read(`
   260  /d1/a [10000 1]
   261  /d1/a2 [10000 1]
   262  /d1/a3 [10000 1]
   263  /d1/b [1392 2]
   264  /d2/a [10000 1]
   265  /d2/b [1392 2]
   266  	`).Mkfs()
   267  	ps, out, _ := newTest(fs)
   268  	ps.Scan([]string{"/"}, &ScanOptions{})
   269  	err := ps.Ls([]string{"/d1"}, &LsOptions{Verbose: true})
   270  	check(t, err)
   271  	got := strings.TrimRight(out.String(), "\n")
   272  	expected := strings.TrimSpace(`
   273  3 a
   274      /d1/a2
   275      /d1/a3
   276      /d2/a
   277  3 a2
   278      /d1/a
   279      /d1/a3
   280      /d2/a
   281  3 a3
   282      /d1/a
   283      /d1/a2
   284      /d2/a
   285  1 b
   286      /d2/b
   287  	`)
   288  	if got != expected {
   289  		t.Fatalf("expected '%s', got '%s'", expected, got)
   290  	}
   291  }
   292  
   293  func TestLsVerboseRelative(t *testing.T) {
   294  	fs := testfs.Read(`
   295  /d1/a [10000 1]
   296  /d1/a2 [10000 1]
   297  /d1/a3 [10000 1]
   298  /d1/b [1392 2]
   299  /d2/a [10000 1]
   300  /d2/b [1392 2]
   301  	`).Mkfs()
   302  	ps, out, _ := newTest(fs)
   303  	ps.Scan([]string{"/"}, &ScanOptions{})
   304  	err := ps.Ls([]string{"/d1"}, &LsOptions{Verbose: true, Relative: true})
   305  	check(t, err)
   306  	got := strings.TrimRight(out.String(), "\n")
   307  	expected := strings.TrimSpace(`
   308  3 a
   309      a2
   310      a3
   311      /d2/a
   312  3 a2
   313      a
   314      a3
   315      /d2/a
   316  3 a3
   317      a
   318      a2
   319      /d2/a
   320  1 b
   321      /d2/b
   322  	`)
   323  	if got != expected {
   324  		t.Fatalf("expected '%s', got '%s'", expected, got)
   325  	}
   326  }
   327  
   328  func TestLsVerboseRelativeLong(t *testing.T) {
   329  	fs := testfs.Read(`
   330  /long/directory/a [10 1]
   331  /long/directory/b [10 1]
   332  /long/x/c [10 1]
   333  /other/directory/d [10 1]
   334  	`).Mkfs()
   335  	ps, out, _ := newTest(fs)
   336  	ps.Scan([]string{"/"}, &ScanOptions{})
   337  	err := ps.Ls([]string{"/long/directory"}, &LsOptions{Verbose: true, Relative: true})
   338  	check(t, err)
   339  	got := strings.TrimRight(out.String(), "\n")
   340  	expected := strings.TrimSpace(`
   341  3 a
   342      b
   343      ../x/c
   344      /other/directory/d
   345  3 b
   346      a
   347      ../x/c
   348      /other/directory/d
   349  	`)
   350  	if got != expected {
   351  		t.Fatalf("expected '%s', got '%s'", expected, got)
   352  	}
   353  }
   354  
   355  func TestLsHidden(t *testing.T) {
   356  	fs := testfs.Read(`
   357  /d1/a [10000 1]
   358  /d1/.b [1392 2]
   359  /d2/.a [10000 1]
   360  /d2/b [1392 2]
   361  	`).Mkfs()
   362  	ps, out, _ := newTest(fs)
   363  	ps.Scan([]string{"/"}, &ScanOptions{})
   364  	err := ps.Ls([]string{"/d1"}, &LsOptions{})
   365  	check(t, err)
   366  	got := strings.TrimRight(out.String(), "\n")
   367  	expected := strings.TrimSpace(`
   368  1 a
   369  	`)
   370  	if got != expected {
   371  		t.Fatalf("expected '%s', got '%s'", expected, got)
   372  	}
   373  	out.Reset()
   374  	err = ps.Ls([]string{"/d1"}, &LsOptions{All: true})
   375  	check(t, err)
   376  	got = strings.TrimSpace(out.String())
   377  	expected = strings.TrimSpace(`
   378  1 .b
   379  1 a
   380  	`)
   381  	if got != expected {
   382  		t.Fatalf("expected '%s', got '%s'", expected, got)
   383  	}
   384  }
   385  
   386  func TestLsMultiple(t *testing.T) {
   387  	fs := testfs.Read(`
   388  /d1/a [10000 1]
   389  /d1/.b [1392 2]
   390  /d2/.a [10000 1]
   391  /d2/b [1392 2]
   392  	`).Mkfs()
   393  	ps, out, _ := newTest(fs)
   394  	ps.Scan([]string{"/"}, &ScanOptions{})
   395  	err := ps.Ls([]string{"/d1", "/d2"}, &LsOptions{All: true})
   396  	check(t, err)
   397  	got := strings.TrimRight(out.String(), "\n")
   398  	expected := strings.TrimSpace(`
   399  /d1:
   400  1 .b
   401  1 a
   402  
   403  /d2:
   404  1 .a
   405  1 b
   406  	`)
   407  	if got != expected {
   408  		t.Fatalf("expected '%s', got '%s'", expected, got)
   409  	}
   410  }
   411  
   412  func TestLsInaccessible(t *testing.T) {
   413  	dir := tempDir()
   414  	defer os.RemoveAll(dir)
   415  	inner := filepath.Join(dir, "d")
   416  	os.Mkdir(inner, 0111)
   417  	fs := afero.NewOsFs()
   418  	ps, out, stderr := newTest(fs)
   419  	err := ps.Ls([]string{inner}, &LsOptions{})
   420  	checkErr(t, err)
   421  	got := strings.TrimRight(out.String(), "\n")
   422  	if got != "" {
   423  		t.Fatalf("expected no output, got '%s'", got)
   424  	}
   425  	expected := "permission denied"
   426  	got = stderr.String()
   427  	if !strings.Contains(got, expected) {
   428  		t.Fatalf("expected stderr to contain '%s', was '%s'", expected, got)
   429  	}
   430  }
   431  
   432  func TestLsNonexistent(t *testing.T) {
   433  	fs := testfs.Read(`
   434  /d1/a [10000 1]
   435  /d1/.b [1392 2]
   436  	`).Mkfs()
   437  	ps, out, stderr := newTest(fs)
   438  	err := ps.Ls([]string{"/d2"}, &LsOptions{})
   439  	checkErr(t, err)
   440  	got := strings.TrimRight(out.String(), "\n")
   441  	if got != "" {
   442  		t.Fatalf("expected no output, got '%s'", got)
   443  	}
   444  	expected := "no such file or directory"
   445  	got = stderr.String()
   446  	if !strings.Contains(got, expected) {
   447  		t.Fatalf("expected stderr to contain '%s', was '%s'", expected, got)
   448  	}
   449  }
   450  
   451  func TestLsFile(t *testing.T) {
   452  	fs := testfs.Read(`
   453  /d1/a [10000 1]
   454  	`).Mkfs()
   455  	ps, out, stderr := newTest(fs)
   456  	err := ps.Ls([]string{"/d1/a"}, &LsOptions{})
   457  	checkErr(t, err)
   458  	got := strings.TrimRight(out.String(), "\n")
   459  	if got != "" {
   460  		t.Fatalf("expected no output, got '%s'", got)
   461  	}
   462  	expected := "not a directory"
   463  	got = stderr.String()
   464  	if !strings.Contains(got, expected) {
   465  		t.Fatalf("expected stderr to contain '%s', was '%s'", expected, got)
   466  	}
   467  }
   468  
   469  func TestLsSomeInaccessible(t *testing.T) {
   470  	fs := testfs.Read(`
   471  /d1/a [10000 1]
   472  /d3/a [10000 1]
   473  /d3/b [1337 2]
   474  	`).Mkfs()
   475  	ps, out, stderr := newTest(fs)
   476  	ps.Scan([]string{"/"}, &ScanOptions{})
   477  	err := ps.Ls([]string{"/d1", "/d2", "/d3"}, &LsOptions{})
   478  	checkErr(t, err)
   479  	got := strings.TrimRight(out.String(), "\n")
   480  	expected := strings.TrimSpace(`
   481  /d1:
   482  1 a
   483  
   484  /d3:
   485  1 a
   486    b
   487  	`)
   488  	if got != expected {
   489  		t.Fatalf("expected '%s', got '%s'", expected, got)
   490  	}
   491  	expected = "cannot list '/d2': no such file or directory"
   492  	got = stderr.String()
   493  	if !strings.Contains(got, expected) {
   494  		t.Fatalf("expected stderr to contain '%s', was '%s'", expected, got)
   495  	}
   496  }
   497  
   498  func TestLsRejectSymlink(t *testing.T) {
   499  	fs := afero.NewOsFs()
   500  	dir := tempDir()
   501  	defer os.RemoveAll(dir)
   502  	ioutil.WriteFile(filepath.Join(dir, "x"), []byte{'a'}, 0o644)
   503  	ioutil.WriteFile(filepath.Join(dir, "y"), []byte{'a'}, 0o644)
   504  	os.Symlink(dir, filepath.Join(dir, "rec"))
   505  	ps, out, stderr := newTest(fs)
   506  	err := ps.Ls([]string{filepath.Join(dir, "rec")}, &LsOptions{})
   507  	checkErr(t, err)
   508  	got := strings.TrimRight(out.String(), "\n")
   509  	if got != "" {
   510  		t.Fatalf("expected no output, got '%s'", got)
   511  	}
   512  	expected := "path has symbolic links"
   513  	got = stderr.String()
   514  	if !strings.Contains(got, expected) {
   515  		t.Fatalf("expected stderr to contain '%s', was '%s'", expected, got)
   516  	}
   517  }
   518  
   519  func TestLsRecursive(t *testing.T) {
   520  	fs := testfs.Read(`
   521  /foo.txt [100 1]
   522  /Pictures/2020/January/IMG_1234.JPG [1000 3]
   523  /Pictures/2021/August/DSC1337.HEIC [1000 2]
   524  /Pictures/2021/August/DSC1337.copy.HEIC [1000 2]
   525  /Temporary/recovered.heic [1000 2]
   526  	`).Mkfs()
   527  	ps, out, _ := newTest(fs)
   528  	ps.Scan([]string{"/"}, &ScanOptions{})
   529  	err := ps.Ls([]string{"/"}, &LsOptions{Recursive: true})
   530  	check(t, err)
   531  	got := strings.TrimRight(out.String(), "\n")
   532  	expected := strings.TrimSpace(`
   533  /:
   534  d Pictures
   535  d Temporary
   536    foo.txt
   537  
   538  /Pictures:
   539  d 2020
   540  d 2021
   541  
   542  /Pictures/2020:
   543  d January
   544  
   545  /Pictures/2020/January:
   546  IMG_1234.JPG
   547  
   548  /Pictures/2021:
   549  d August
   550  
   551  /Pictures/2021/August:
   552  2 DSC1337.HEIC
   553  2 DSC1337.copy.HEIC
   554  
   555  /Temporary:
   556  2 recovered.heic
   557  	`)
   558  	if got != expected {
   559  		t.Fatalf("expected '%s', got '%s'", expected, got)
   560  	}
   561  }
   562  
   563  func TestLsRecursiveDuplicateOnly(t *testing.T) {
   564  	fs := testfs.Read(`
   565  /foo.txt [100 1]
   566  /Pictures/2020/January/IMG_1234.JPG [1000 3]
   567  /Pictures/2021/August/DSC1337.HEIC [1000 2]
   568  /Pictures/2021/August/DSC1337.copy.HEIC [1000 2]
   569  /Temporary/recovered.heic [1000 2]
   570  	`).Mkfs()
   571  	ps, out, _ := newTest(fs)
   572  	ps.Scan([]string{"/"}, &ScanOptions{})
   573  	err := ps.Ls([]string{"/"}, &LsOptions{Recursive: true, Duplicate: true})
   574  	check(t, err)
   575  	got := strings.TrimRight(out.String(), "\n")
   576  	expected := strings.TrimSpace(`
   577  /Pictures/2021/August:
   578  2 DSC1337.HEIC
   579  2 DSC1337.copy.HEIC
   580  
   581  /Temporary:
   582  2 recovered.heic
   583  	`)
   584  	if got != expected {
   585  		t.Fatalf("expected '%s', got '%s'", expected, got)
   586  	}
   587  }
   588  
   589  func TestLsRecursiveHidden(t *testing.T) {
   590  	fs := testfs.Read(`
   591  /d1/a [10000 1]
   592  /d1/.b [1392 2]
   593  /d2/.a [10000 1]
   594  /d2/b [1392 2]
   595  	`).Mkfs()
   596  	ps, out, _ := newTest(fs)
   597  	ps.Scan([]string{"/"}, &ScanOptions{})
   598  	err := ps.Ls([]string{"/"}, &LsOptions{Recursive: true, Duplicate: true})
   599  	check(t, err)
   600  	got := strings.TrimSpace(out.String())
   601  	expected := strings.TrimSpace(`
   602  /d1:
   603  1 a
   604  
   605  /d2:
   606  1 b
   607  	`)
   608  	if got != expected {
   609  		t.Fatalf("expected '%s', got '%s'", expected, got)
   610  	}
   611  	out.Reset()
   612  	err = ps.Ls([]string{"/"}, &LsOptions{Recursive: true, Duplicate: true, All: true})
   613  	check(t, err)
   614  	got = strings.TrimSpace(out.String())
   615  	expected = strings.TrimSpace(`
   616  /d1:
   617  1 .b
   618  1 a
   619  
   620  /d2:
   621  1 .a
   622  1 b
   623  	`)
   624  	if got != expected {
   625  		t.Fatalf("expected '%s', got '%s'", expected, got)
   626  	}
   627  }
   628  
   629  func TestLsRelative(t *testing.T) {
   630  	fs := afero.NewOsFs()
   631  	dir := tempDir()
   632  	defer os.RemoveAll(dir)
   633  	ioutil.WriteFile(filepath.Join(dir, "x"), []byte{'a'}, 0o644)
   634  	ioutil.WriteFile(filepath.Join(dir, "y"), []byte{'a'}, 0o644)
   635  	os.Mkdir(filepath.Join(dir, "d"), 0o755)
   636  	ioutil.WriteFile(filepath.Join(dir, "d", "a"), []byte{'b'}, 0o644)
   637  	ioutil.WriteFile(filepath.Join(dir, "d", "b"), []byte{'b'}, 0o644)
   638  	ps, out, _ := newTest(fs)
   639  	oldWd, err := os.Getwd()
   640  	if err != nil {
   641  		panic(err)
   642  	}
   643  	os.Chdir(dir)
   644  	defer os.Chdir(oldWd)
   645  	ps.Scan([]string{"."}, &ScanOptions{})
   646  	err = ps.Ls([]string{".", "d"}, &LsOptions{})
   647  	check(t, err)
   648  	got := strings.TrimSpace(out.String())
   649  	expected := strings.TrimSpace(`
   650  .:
   651  d d
   652  1 x
   653  1 y
   654  
   655  d:
   656  1 a
   657  1 b
   658  	`)
   659  	if got != expected {
   660  		t.Fatalf("expected '%s', got '%s'", expected, got)
   661  	}
   662  }
   663  
   664  func TestLsRelativeRecursive(t *testing.T) {
   665  	fs := afero.NewOsFs()
   666  	dir := tempDir()
   667  	defer os.RemoveAll(dir)
   668  	ioutil.WriteFile(filepath.Join(dir, "x"), []byte{'a'}, 0o644)
   669  	ioutil.WriteFile(filepath.Join(dir, "y"), []byte{'a'}, 0o644)
   670  	os.Mkdir(filepath.Join(dir, "d"), 0o755)
   671  	ioutil.WriteFile(filepath.Join(dir, "d", "a"), []byte{'b'}, 0o644)
   672  	ioutil.WriteFile(filepath.Join(dir, "d", "b"), []byte{'b'}, 0o644)
   673  	ps, out, _ := newTest(fs)
   674  	oldWd, err := os.Getwd()
   675  	if err != nil {
   676  		panic(err)
   677  	}
   678  	os.Chdir(dir)
   679  	defer os.Chdir(oldWd)
   680  	ps.Scan([]string{"."}, &ScanOptions{})
   681  	err = ps.Ls([]string{"."}, &LsOptions{Recursive: true, Duplicate: true})
   682  	check(t, err)
   683  	got := strings.TrimSpace(out.String())
   684  	expected := strings.TrimSpace(`
   685  .:
   686  1 x
   687  1 y
   688  
   689  d:
   690  1 a
   691  1 b
   692  	`)
   693  	if got != expected {
   694  		t.Fatalf("expected '%s', got '%s'", expected, got)
   695  	}
   696  }
   697  
   698  func TestLsRelativeDir(t *testing.T) {
   699  	fs := afero.NewOsFs()
   700  	dir := tempDir()
   701  	defer os.RemoveAll(dir)
   702  	ioutil.WriteFile(filepath.Join(dir, "x"), []byte{'a'}, 0o644)
   703  	ioutil.WriteFile(filepath.Join(dir, "y"), []byte{'a'}, 0o644)
   704  	os.Mkdir(filepath.Join(dir, "d"), 0o755)
   705  	ioutil.WriteFile(filepath.Join(dir, "d", "a"), []byte{'b'}, 0o644)
   706  	ioutil.WriteFile(filepath.Join(dir, "d", "b"), []byte{'b'}, 0o644)
   707  	ps, out, _ := newTest(fs)
   708  	oldWd, err := os.Getwd()
   709  	if err != nil {
   710  		panic(err)
   711  	}
   712  	os.Chdir(dir)
   713  	defer os.Chdir(oldWd)
   714  	ps.Scan([]string{"."}, &ScanOptions{})
   715  	err = ps.Ls([]string{"d"}, &LsOptions{})
   716  	check(t, err)
   717  	got := strings.TrimSpace(out.String())
   718  	expected := strings.TrimSpace(`
   719  1 a
   720  1 b
   721  	`)
   722  	if got != expected {
   723  		t.Fatalf("expected '%s', got '%s'", expected, got)
   724  	}
   725  }