github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/fs/operations/operations_test.go (about)

     1  // Integration tests - test rclone by doing real transactions to a
     2  // storage provider to and from the local disk.
     3  //
     4  // By default it will use a local fs, however you can provide a
     5  // -remote option to use a different remote.  The test_all.go script
     6  // is a wrapper to call this for all the test remotes.
     7  //
     8  // FIXME not safe for concurrent running of tests until fs.Config is
     9  // no longer a global
    10  //
    11  // NB When writing tests
    12  //
    13  // Make sure every series of writes to the remote has a
    14  // fstest.CheckItems() before use.  This make sure the directory
    15  // listing is now consistent and stops cascading errors.
    16  //
    17  // Call accounting.GlobalStats().ResetCounters() before every fs.Sync() as it
    18  // uses the error count internally.
    19  
    20  package operations_test
    21  
    22  import (
    23  	"bytes"
    24  	"context"
    25  	"errors"
    26  	"fmt"
    27  	"io"
    28  	"log"
    29  	"net/http"
    30  	"net/http/httptest"
    31  	"os"
    32  	"regexp"
    33  	"strings"
    34  	"testing"
    35  	"time"
    36  
    37  	_ "github.com/rclone/rclone/backend/all" // import all backends
    38  	"github.com/rclone/rclone/fs"
    39  	"github.com/rclone/rclone/fs/accounting"
    40  	"github.com/rclone/rclone/fs/filter"
    41  	"github.com/rclone/rclone/fs/fshttp"
    42  	"github.com/rclone/rclone/fs/hash"
    43  	"github.com/rclone/rclone/fs/operations"
    44  	"github.com/rclone/rclone/fstest"
    45  	"github.com/stretchr/testify/assert"
    46  	"github.com/stretchr/testify/require"
    47  )
    48  
    49  // Some times used in the tests
    50  var (
    51  	t1 = fstest.Time("2001-02-03T04:05:06.499999999Z")
    52  	t2 = fstest.Time("2011-12-25T12:59:59.123456789Z")
    53  	t3 = fstest.Time("2011-12-30T12:59:59.000000000Z")
    54  )
    55  
    56  // TestMain drives the tests
    57  func TestMain(m *testing.M) {
    58  	fstest.TestMain(m)
    59  }
    60  
    61  func TestMkdir(t *testing.T) {
    62  	r := fstest.NewRun(t)
    63  	defer r.Finalise()
    64  
    65  	err := operations.Mkdir(context.Background(), r.Fremote, "")
    66  	require.NoError(t, err)
    67  	fstest.CheckListing(t, r.Fremote, []fstest.Item{})
    68  
    69  	err = operations.Mkdir(context.Background(), r.Fremote, "")
    70  	require.NoError(t, err)
    71  }
    72  
    73  func TestLsd(t *testing.T) {
    74  	r := fstest.NewRun(t)
    75  	defer r.Finalise()
    76  	file1 := r.WriteObject(context.Background(), "sub dir/hello world", "hello world", t1)
    77  
    78  	fstest.CheckItems(t, r.Fremote, file1)
    79  
    80  	var buf bytes.Buffer
    81  	err := operations.ListDir(context.Background(), r.Fremote, &buf)
    82  	require.NoError(t, err)
    83  	res := buf.String()
    84  	assert.Contains(t, res, "sub dir\n")
    85  }
    86  
    87  func TestLs(t *testing.T) {
    88  	r := fstest.NewRun(t)
    89  	defer r.Finalise()
    90  	file1 := r.WriteBoth(context.Background(), "potato2", "------------------------------------------------------------", t1)
    91  	file2 := r.WriteBoth(context.Background(), "empty space", "-", t2)
    92  
    93  	fstest.CheckItems(t, r.Fremote, file1, file2)
    94  
    95  	var buf bytes.Buffer
    96  	err := operations.List(context.Background(), r.Fremote, &buf)
    97  	require.NoError(t, err)
    98  	res := buf.String()
    99  	assert.Contains(t, res, "        1 empty space\n")
   100  	assert.Contains(t, res, "       60 potato2\n")
   101  }
   102  
   103  func TestLsWithFilesFrom(t *testing.T) {
   104  	r := fstest.NewRun(t)
   105  	defer r.Finalise()
   106  	file1 := r.WriteBoth(context.Background(), "potato2", "------------------------------------------------------------", t1)
   107  	file2 := r.WriteBoth(context.Background(), "empty space", "-", t2)
   108  
   109  	fstest.CheckItems(t, r.Fremote, file1, file2)
   110  
   111  	// Set the --files-from equivalent
   112  	f, err := filter.NewFilter(nil)
   113  	require.NoError(t, err)
   114  	require.NoError(t, f.AddFile("potato2"))
   115  	require.NoError(t, f.AddFile("notfound"))
   116  
   117  	// Monkey patch the active filter
   118  	oldFilter := filter.Active
   119  	filter.Active = f
   120  	defer func() {
   121  		filter.Active = oldFilter
   122  	}()
   123  
   124  	var buf bytes.Buffer
   125  	err = operations.List(context.Background(), r.Fremote, &buf)
   126  	require.NoError(t, err)
   127  	assert.Equal(t, "       60 potato2\n", buf.String())
   128  
   129  	// Now try with --no-traverse
   130  	oldNoTraverse := fs.Config.NoTraverse
   131  	fs.Config.NoTraverse = true
   132  	defer func() {
   133  		fs.Config.NoTraverse = oldNoTraverse
   134  	}()
   135  
   136  	buf.Reset()
   137  	err = operations.List(context.Background(), r.Fremote, &buf)
   138  	require.NoError(t, err)
   139  	assert.Equal(t, "       60 potato2\n", buf.String())
   140  }
   141  
   142  func TestLsLong(t *testing.T) {
   143  	r := fstest.NewRun(t)
   144  	defer r.Finalise()
   145  	file1 := r.WriteBoth(context.Background(), "potato2", "------------------------------------------------------------", t1)
   146  	file2 := r.WriteBoth(context.Background(), "empty space", "-", t2)
   147  
   148  	fstest.CheckItems(t, r.Fremote, file1, file2)
   149  
   150  	var buf bytes.Buffer
   151  	err := operations.ListLong(context.Background(), r.Fremote, &buf)
   152  	require.NoError(t, err)
   153  	res := buf.String()
   154  	lines := strings.Split(strings.Trim(res, "\n"), "\n")
   155  	assert.Equal(t, 2, len(lines))
   156  
   157  	timeFormat := "2006-01-02 15:04:05.000000000"
   158  	precision := r.Fremote.Precision()
   159  	location := time.Now().Location()
   160  	checkTime := func(m, filename string, expected time.Time) {
   161  		modTime, err := time.ParseInLocation(timeFormat, m, location) // parse as localtime
   162  		if err != nil {
   163  			t.Errorf("Error parsing %q: %v", m, err)
   164  		} else {
   165  			dt, ok := fstest.CheckTimeEqualWithPrecision(expected, modTime, precision)
   166  			if !ok {
   167  				t.Errorf("%s: Modification time difference too big |%s| > %s (%s vs %s) (precision %s)", filename, dt, precision, modTime, expected, precision)
   168  			}
   169  		}
   170  	}
   171  
   172  	m1 := regexp.MustCompile(`(?m)^        1 (\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d{9}) empty space$`)
   173  	if ms := m1.FindStringSubmatch(res); ms == nil {
   174  		t.Errorf("empty space missing: %q", res)
   175  	} else {
   176  		checkTime(ms[1], "empty space", t2.Local())
   177  	}
   178  
   179  	m2 := regexp.MustCompile(`(?m)^       60 (\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d{9}) potato2$`)
   180  	if ms := m2.FindStringSubmatch(res); ms == nil {
   181  		t.Errorf("potato2 missing: %q", res)
   182  	} else {
   183  		checkTime(ms[1], "potato2", t1.Local())
   184  	}
   185  }
   186  
   187  func TestHashSums(t *testing.T) {
   188  	r := fstest.NewRun(t)
   189  	defer r.Finalise()
   190  	file1 := r.WriteBoth(context.Background(), "potato2", "------------------------------------------------------------", t1)
   191  	file2 := r.WriteBoth(context.Background(), "empty space", "-", t2)
   192  
   193  	fstest.CheckItems(t, r.Fremote, file1, file2)
   194  
   195  	// MD5 Sum
   196  
   197  	var buf bytes.Buffer
   198  	err := operations.Md5sum(context.Background(), r.Fremote, &buf)
   199  	require.NoError(t, err)
   200  	res := buf.String()
   201  	if !strings.Contains(res, "336d5ebc5436534e61d16e63ddfca327  empty space\n") &&
   202  		!strings.Contains(res, "                     UNSUPPORTED  empty space\n") &&
   203  		!strings.Contains(res, "                                  empty space\n") {
   204  		t.Errorf("empty space missing: %q", res)
   205  	}
   206  	if !strings.Contains(res, "d6548b156ea68a4e003e786df99eee76  potato2\n") &&
   207  		!strings.Contains(res, "                     UNSUPPORTED  potato2\n") &&
   208  		!strings.Contains(res, "                                  potato2\n") {
   209  		t.Errorf("potato2 missing: %q", res)
   210  	}
   211  
   212  	// SHA1 Sum
   213  
   214  	buf.Reset()
   215  	err = operations.Sha1sum(context.Background(), r.Fremote, &buf)
   216  	require.NoError(t, err)
   217  	res = buf.String()
   218  	if !strings.Contains(res, "3bc15c8aae3e4124dd409035f32ea2fd6835efc9  empty space\n") &&
   219  		!strings.Contains(res, "                             UNSUPPORTED  empty space\n") &&
   220  		!strings.Contains(res, "                                          empty space\n") {
   221  		t.Errorf("empty space missing: %q", res)
   222  	}
   223  	if !strings.Contains(res, "9dc7f7d3279715991a22853f5981df582b7f9f6d  potato2\n") &&
   224  		!strings.Contains(res, "                             UNSUPPORTED  potato2\n") &&
   225  		!strings.Contains(res, "                                          potato2\n") {
   226  		t.Errorf("potato2 missing: %q", res)
   227  	}
   228  
   229  	// QuickXorHash Sum
   230  
   231  	buf.Reset()
   232  	var ht hash.Type
   233  	err = ht.Set("QuickXorHash")
   234  	require.NoError(t, err)
   235  	err = operations.HashLister(context.Background(), ht, r.Fremote, &buf)
   236  	require.NoError(t, err)
   237  	res = buf.String()
   238  	if !strings.Contains(res, "2d00000000000000000000000100000000000000  empty space\n") &&
   239  		!strings.Contains(res, "                             UNSUPPORTED  empty space\n") &&
   240  		!strings.Contains(res, "                                          empty space\n") {
   241  		t.Errorf("empty space missing: %q", res)
   242  	}
   243  	if !strings.Contains(res, "4001dad296b6b4a52d6d694b67dad296b6b4a52d  potato2\n") &&
   244  		!strings.Contains(res, "                             UNSUPPORTED  potato2\n") &&
   245  		!strings.Contains(res, "                                          potato2\n") {
   246  		t.Errorf("potato2 missing: %q", res)
   247  	}
   248  
   249  	// QuickXorHash Sum with Base64 Encoded
   250  
   251  	buf.Reset()
   252  	err = operations.HashListerBase64(context.Background(), ht, r.Fremote, &buf)
   253  	require.NoError(t, err)
   254  	res = buf.String()
   255  	if !strings.Contains(res, "LQAAAAAAAAAAAAAAAQAAAAAAAAA=  empty space\n") &&
   256  		!strings.Contains(res, "                 UNSUPPORTED  empty space\n") &&
   257  		!strings.Contains(res, "                              empty space\n") {
   258  		t.Errorf("empty space missing: %q", res)
   259  	}
   260  	if !strings.Contains(res, "QAHa0pa2tKUtbWlLZ9rSlra0pS0=  potato2\n") &&
   261  		!strings.Contains(res, "                 UNSUPPORTED  potato2\n") &&
   262  		!strings.Contains(res, "                              potato2\n") {
   263  		t.Errorf("potato2 missing: %q", res)
   264  	}
   265  }
   266  
   267  func TestSuffixName(t *testing.T) {
   268  	origSuffix, origKeepExt := fs.Config.Suffix, fs.Config.SuffixKeepExtension
   269  	defer func() {
   270  		fs.Config.Suffix, fs.Config.SuffixKeepExtension = origSuffix, origKeepExt
   271  	}()
   272  	for _, test := range []struct {
   273  		remote  string
   274  		suffix  string
   275  		keepExt bool
   276  		want    string
   277  	}{
   278  		{"test.txt", "", false, "test.txt"},
   279  		{"test.txt", "", true, "test.txt"},
   280  		{"test.txt", "-suffix", false, "test.txt-suffix"},
   281  		{"test.txt", "-suffix", true, "test-suffix.txt"},
   282  		{"test.txt.csv", "-suffix", false, "test.txt.csv-suffix"},
   283  		{"test.txt.csv", "-suffix", true, "test.txt-suffix.csv"},
   284  		{"test", "-suffix", false, "test-suffix"},
   285  		{"test", "-suffix", true, "test-suffix"},
   286  	} {
   287  		fs.Config.Suffix = test.suffix
   288  		fs.Config.SuffixKeepExtension = test.keepExt
   289  		got := operations.SuffixName(test.remote)
   290  		assert.Equal(t, test.want, got, fmt.Sprintf("%+v", test))
   291  	}
   292  }
   293  
   294  func TestCount(t *testing.T) {
   295  	r := fstest.NewRun(t)
   296  	defer r.Finalise()
   297  	file1 := r.WriteBoth(context.Background(), "potato2", "------------------------------------------------------------", t1)
   298  	file2 := r.WriteBoth(context.Background(), "empty space", "-", t2)
   299  	file3 := r.WriteBoth(context.Background(), "sub dir/potato3", "hello", t2)
   300  
   301  	fstest.CheckItems(t, r.Fremote, file1, file2, file3)
   302  
   303  	// Check the MaxDepth too
   304  	fs.Config.MaxDepth = 1
   305  	defer func() { fs.Config.MaxDepth = -1 }()
   306  
   307  	objects, size, err := operations.Count(context.Background(), r.Fremote)
   308  	require.NoError(t, err)
   309  	assert.Equal(t, int64(2), objects)
   310  	assert.Equal(t, int64(61), size)
   311  }
   312  
   313  func TestDelete(t *testing.T) {
   314  	r := fstest.NewRun(t)
   315  	defer r.Finalise()
   316  	file1 := r.WriteObject(context.Background(), "small", "1234567890", t2)                                                                                           // 10 bytes
   317  	file2 := r.WriteObject(context.Background(), "medium", "------------------------------------------------------------", t1)                                        // 60 bytes
   318  	file3 := r.WriteObject(context.Background(), "large", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", t1) // 100 bytes
   319  	fstest.CheckItems(t, r.Fremote, file1, file2, file3)
   320  
   321  	filter.Active.Opt.MaxSize = 60
   322  	defer func() {
   323  		filter.Active.Opt.MaxSize = -1
   324  	}()
   325  
   326  	err := operations.Delete(context.Background(), r.Fremote)
   327  	require.NoError(t, err)
   328  	fstest.CheckItems(t, r.Fremote, file3)
   329  }
   330  
   331  func testCheck(t *testing.T, checkFunction func(ctx context.Context, fdst, fsrc fs.Fs, oneway bool) error) {
   332  	r := fstest.NewRun(t)
   333  	defer r.Finalise()
   334  
   335  	check := func(i int, wantErrors int64, wantChecks int64, oneway bool) {
   336  		fs.Debugf(r.Fremote, "%d: Starting check test", i)
   337  		accounting.GlobalStats().ResetCounters()
   338  		var buf bytes.Buffer
   339  		log.SetOutput(&buf)
   340  		defer func() {
   341  			log.SetOutput(os.Stderr)
   342  		}()
   343  		err := checkFunction(context.Background(), r.Fremote, r.Flocal, oneway)
   344  		gotErrors := accounting.GlobalStats().GetErrors()
   345  		gotChecks := accounting.GlobalStats().GetChecks()
   346  		if wantErrors == 0 && err != nil {
   347  			t.Errorf("%d: Got error when not expecting one: %v", i, err)
   348  		}
   349  		if wantErrors != 0 && err == nil {
   350  			t.Errorf("%d: No error when expecting one", i)
   351  		}
   352  		if wantErrors != gotErrors {
   353  			t.Errorf("%d: Expecting %d errors but got %d", i, wantErrors, gotErrors)
   354  		}
   355  		if gotChecks > 0 && !strings.Contains(buf.String(), "matching files") {
   356  			t.Errorf("%d: Total files matching line missing", i)
   357  		}
   358  		if wantChecks != gotChecks {
   359  			t.Errorf("%d: Expecting %d total matching files but got %d", i, wantChecks, gotChecks)
   360  		}
   361  		fs.Debugf(r.Fremote, "%d: Ending check test", i)
   362  	}
   363  
   364  	file1 := r.WriteBoth(context.Background(), "rutabaga", "is tasty", t3)
   365  	fstest.CheckItems(t, r.Fremote, file1)
   366  	fstest.CheckItems(t, r.Flocal, file1)
   367  	check(1, 0, 1, false)
   368  
   369  	file2 := r.WriteFile("potato2", "------------------------------------------------------------", t1)
   370  	fstest.CheckItems(t, r.Flocal, file1, file2)
   371  	check(2, 1, 1, false)
   372  
   373  	file3 := r.WriteObject(context.Background(), "empty space", "-", t2)
   374  	fstest.CheckItems(t, r.Fremote, file1, file3)
   375  	check(3, 2, 1, false)
   376  
   377  	file2r := file2
   378  	if fs.Config.SizeOnly {
   379  		file2r = r.WriteObject(context.Background(), "potato2", "--Some-Differences-But-Size-Only-Is-Enabled-----------------", t1)
   380  	} else {
   381  		r.WriteObject(context.Background(), "potato2", "------------------------------------------------------------", t1)
   382  	}
   383  	fstest.CheckItems(t, r.Fremote, file1, file2r, file3)
   384  	check(4, 1, 2, false)
   385  
   386  	r.WriteFile("empty space", "-", t2)
   387  	fstest.CheckItems(t, r.Flocal, file1, file2, file3)
   388  	check(5, 0, 3, false)
   389  
   390  	file4 := r.WriteObject(context.Background(), "remotepotato", "------------------------------------------------------------", t1)
   391  	fstest.CheckItems(t, r.Fremote, file1, file2r, file3, file4)
   392  	check(6, 1, 3, false)
   393  	check(7, 0, 3, true)
   394  }
   395  
   396  func TestCheck(t *testing.T) {
   397  	testCheck(t, operations.Check)
   398  }
   399  
   400  func TestCheckFsError(t *testing.T) {
   401  	dstFs, err := fs.NewFs("non-existent")
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  	srcFs, err := fs.NewFs("non-existent")
   406  	if err != nil {
   407  		t.Fatal(err)
   408  	}
   409  	err = operations.Check(context.Background(), dstFs, srcFs, false)
   410  	require.Error(t, err)
   411  }
   412  
   413  func TestCheckDownload(t *testing.T) {
   414  	testCheck(t, operations.CheckDownload)
   415  }
   416  
   417  func TestCheckSizeOnly(t *testing.T) {
   418  	fs.Config.SizeOnly = true
   419  	defer func() { fs.Config.SizeOnly = false }()
   420  	TestCheck(t)
   421  }
   422  
   423  func TestCat(t *testing.T) {
   424  	r := fstest.NewRun(t)
   425  	defer r.Finalise()
   426  	file1 := r.WriteBoth(context.Background(), "file1", "ABCDEFGHIJ", t1)
   427  	file2 := r.WriteBoth(context.Background(), "file2", "012345678", t2)
   428  
   429  	fstest.CheckItems(t, r.Fremote, file1, file2)
   430  
   431  	for _, test := range []struct {
   432  		offset int64
   433  		count  int64
   434  		a      string
   435  		b      string
   436  	}{
   437  		{0, -1, "ABCDEFGHIJ", "012345678"},
   438  		{0, 5, "ABCDE", "01234"},
   439  		{-3, -1, "HIJ", "678"},
   440  		{1, 3, "BCD", "123"},
   441  	} {
   442  		var buf bytes.Buffer
   443  		err := operations.Cat(context.Background(), r.Fremote, &buf, test.offset, test.count)
   444  		require.NoError(t, err)
   445  		res := buf.String()
   446  
   447  		if res != test.a+test.b && res != test.b+test.a {
   448  			t.Errorf("Incorrect output from Cat(%d,%d): %q", test.offset, test.count, res)
   449  		}
   450  	}
   451  }
   452  
   453  func TestPurge(t *testing.T) {
   454  	r := fstest.NewRunIndividual(t) // make new container (azureblob has delayed mkdir after rmdir)
   455  	defer r.Finalise()
   456  	r.Mkdir(context.Background(), r.Fremote)
   457  
   458  	// Make some files and dirs
   459  	r.ForceMkdir(context.Background(), r.Fremote)
   460  	file1 := r.WriteObject(context.Background(), "A1/B1/C1/one", "aaa", t1)
   461  	//..and dirs we expect to delete
   462  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A2"))
   463  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B2"))
   464  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B2/C2"))
   465  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B1/C3"))
   466  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A3"))
   467  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A3/B3"))
   468  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A3/B3/C4"))
   469  	//..and one more file at the end
   470  	file2 := r.WriteObject(context.Background(), "A1/two", "bbb", t2)
   471  
   472  	fstest.CheckListingWithPrecision(
   473  		t,
   474  		r.Fremote,
   475  		[]fstest.Item{
   476  			file1, file2,
   477  		},
   478  		[]string{
   479  			"A1",
   480  			"A1/B1",
   481  			"A1/B1/C1",
   482  			"A2",
   483  			"A1/B2",
   484  			"A1/B2/C2",
   485  			"A1/B1/C3",
   486  			"A3",
   487  			"A3/B3",
   488  			"A3/B3/C4",
   489  		},
   490  		fs.GetModifyWindow(r.Fremote),
   491  	)
   492  
   493  	require.NoError(t, operations.Purge(context.Background(), r.Fremote, "A1/B1"))
   494  
   495  	fstest.CheckListingWithPrecision(
   496  		t,
   497  		r.Fremote,
   498  		[]fstest.Item{
   499  			file2,
   500  		},
   501  		[]string{
   502  			"A1",
   503  			"A2",
   504  			"A1/B2",
   505  			"A1/B2/C2",
   506  			"A3",
   507  			"A3/B3",
   508  			"A3/B3/C4",
   509  		},
   510  		fs.GetModifyWindow(r.Fremote),
   511  	)
   512  
   513  	require.NoError(t, operations.Purge(context.Background(), r.Fremote, ""))
   514  
   515  	fstest.CheckListingWithPrecision(
   516  		t,
   517  		r.Fremote,
   518  		[]fstest.Item{},
   519  		[]string{},
   520  		fs.GetModifyWindow(r.Fremote),
   521  	)
   522  
   523  }
   524  
   525  func TestRmdirsNoLeaveRoot(t *testing.T) {
   526  	r := fstest.NewRun(t)
   527  	defer r.Finalise()
   528  	r.Mkdir(context.Background(), r.Fremote)
   529  
   530  	// Make some files and dirs we expect to keep
   531  	r.ForceMkdir(context.Background(), r.Fremote)
   532  	file1 := r.WriteObject(context.Background(), "A1/B1/C1/one", "aaa", t1)
   533  	//..and dirs we expect to delete
   534  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A2"))
   535  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B2"))
   536  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B2/C2"))
   537  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B1/C3"))
   538  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A3"))
   539  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A3/B3"))
   540  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A3/B3/C4"))
   541  	//..and one more file at the end
   542  	file2 := r.WriteObject(context.Background(), "A1/two", "bbb", t2)
   543  
   544  	fstest.CheckListingWithPrecision(
   545  		t,
   546  		r.Fremote,
   547  		[]fstest.Item{
   548  			file1, file2,
   549  		},
   550  		[]string{
   551  			"A1",
   552  			"A1/B1",
   553  			"A1/B1/C1",
   554  			"A2",
   555  			"A1/B2",
   556  			"A1/B2/C2",
   557  			"A1/B1/C3",
   558  			"A3",
   559  			"A3/B3",
   560  			"A3/B3/C4",
   561  		},
   562  		fs.GetModifyWindow(r.Fremote),
   563  	)
   564  
   565  	require.NoError(t, operations.Rmdirs(context.Background(), r.Fremote, "A3/B3/C4", false))
   566  
   567  	fstest.CheckListingWithPrecision(
   568  		t,
   569  		r.Fremote,
   570  		[]fstest.Item{
   571  			file1, file2,
   572  		},
   573  		[]string{
   574  			"A1",
   575  			"A1/B1",
   576  			"A1/B1/C1",
   577  			"A2",
   578  			"A1/B2",
   579  			"A1/B2/C2",
   580  			"A1/B1/C3",
   581  			"A3",
   582  			"A3/B3",
   583  		},
   584  		fs.GetModifyWindow(r.Fremote),
   585  	)
   586  
   587  	require.NoError(t, operations.Rmdirs(context.Background(), r.Fremote, "", false))
   588  
   589  	fstest.CheckListingWithPrecision(
   590  		t,
   591  		r.Fremote,
   592  		[]fstest.Item{
   593  			file1, file2,
   594  		},
   595  		[]string{
   596  			"A1",
   597  			"A1/B1",
   598  			"A1/B1/C1",
   599  		},
   600  		fs.GetModifyWindow(r.Fremote),
   601  	)
   602  
   603  }
   604  
   605  func TestRmdirsLeaveRoot(t *testing.T) {
   606  	r := fstest.NewRun(t)
   607  	defer r.Finalise()
   608  	r.Mkdir(context.Background(), r.Fremote)
   609  
   610  	r.ForceMkdir(context.Background(), r.Fremote)
   611  
   612  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1"))
   613  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B1"))
   614  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B1/C1"))
   615  
   616  	fstest.CheckListingWithPrecision(
   617  		t,
   618  		r.Fremote,
   619  		[]fstest.Item{},
   620  		[]string{
   621  			"A1",
   622  			"A1/B1",
   623  			"A1/B1/C1",
   624  		},
   625  		fs.GetModifyWindow(r.Fremote),
   626  	)
   627  
   628  	require.NoError(t, operations.Rmdirs(context.Background(), r.Fremote, "A1", true))
   629  
   630  	fstest.CheckListingWithPrecision(
   631  		t,
   632  		r.Fremote,
   633  		[]fstest.Item{},
   634  		[]string{
   635  			"A1",
   636  		},
   637  		fs.GetModifyWindow(r.Fremote),
   638  	)
   639  }
   640  
   641  func TestCopyURL(t *testing.T) {
   642  	r := fstest.NewRun(t)
   643  	defer r.Finalise()
   644  
   645  	contents := "file contents\n"
   646  	file1 := r.WriteFile("file1", contents, t1)
   647  	file2 := r.WriteFile("file2", contents, t1)
   648  	r.Mkdir(context.Background(), r.Fremote)
   649  	fstest.CheckItems(t, r.Fremote)
   650  
   651  	// check when reading from regular HTTP server
   652  	status := 0
   653  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   654  		if status != 0 {
   655  			http.Error(w, "an error ocurred", status)
   656  		}
   657  		_, err := w.Write([]byte(contents))
   658  		assert.NoError(t, err)
   659  	})
   660  	ts := httptest.NewServer(handler)
   661  	defer ts.Close()
   662  
   663  	o, err := operations.CopyURL(context.Background(), r.Fremote, "file1", ts.URL, false)
   664  	require.NoError(t, err)
   665  	assert.Equal(t, int64(len(contents)), o.Size())
   666  
   667  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, nil, fs.ModTimeNotSupported)
   668  
   669  	// Check auto file naming
   670  	status = 0
   671  	urlFileName := "filename.txt"
   672  	o, err = operations.CopyURL(context.Background(), r.Fremote, "", ts.URL+"/"+urlFileName, true)
   673  	require.NoError(t, err)
   674  	assert.Equal(t, int64(len(contents)), o.Size())
   675  	assert.Equal(t, urlFileName, o.Remote())
   676  
   677  	// Check auto file naming when url without file name
   678  	o, err = operations.CopyURL(context.Background(), r.Fremote, "file1", ts.URL, true)
   679  	require.Error(t, err)
   680  
   681  	// Check an error is returned for a 404
   682  	status = http.StatusNotFound
   683  	o, err = operations.CopyURL(context.Background(), r.Fremote, "file1", ts.URL, false)
   684  	require.Error(t, err)
   685  	assert.Contains(t, err.Error(), "Not Found")
   686  	assert.Nil(t, o)
   687  	status = 0
   688  
   689  	// check when reading from unverified HTTPS server
   690  	fs.Config.InsecureSkipVerify = true
   691  	fshttp.ResetTransport()
   692  	defer func() {
   693  		fs.Config.InsecureSkipVerify = false
   694  		fshttp.ResetTransport()
   695  	}()
   696  	tss := httptest.NewTLSServer(handler)
   697  	defer tss.Close()
   698  
   699  	o, err = operations.CopyURL(context.Background(), r.Fremote, "file2", tss.URL, false)
   700  	require.NoError(t, err)
   701  	assert.Equal(t, int64(len(contents)), o.Size())
   702  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, file2, fstest.NewItem(urlFileName, contents, t1)}, nil, fs.ModTimeNotSupported)
   703  }
   704  
   705  func TestCopyURLToWriter(t *testing.T) {
   706  	contents := "file contents\n"
   707  
   708  	// check when reading from regular HTTP server
   709  	status := 0
   710  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   711  		if status != 0 {
   712  			http.Error(w, "an error ocurred", status)
   713  			return
   714  		}
   715  		_, err := w.Write([]byte(contents))
   716  		assert.NoError(t, err)
   717  	})
   718  	ts := httptest.NewServer(handler)
   719  	defer ts.Close()
   720  
   721  	// test normal fetch
   722  	var buf bytes.Buffer
   723  	err := operations.CopyURLToWriter(context.Background(), ts.URL, &buf)
   724  	require.NoError(t, err)
   725  	assert.Equal(t, contents, buf.String())
   726  
   727  	// test fetch with error
   728  	status = http.StatusNotFound
   729  	buf.Reset()
   730  	err = operations.CopyURLToWriter(context.Background(), ts.URL, &buf)
   731  	require.Error(t, err)
   732  	assert.Contains(t, err.Error(), "Not Found")
   733  	assert.Equal(t, 0, len(buf.String()))
   734  }
   735  
   736  func TestMoveFile(t *testing.T) {
   737  	r := fstest.NewRun(t)
   738  	defer r.Finalise()
   739  
   740  	file1 := r.WriteFile("file1", "file1 contents", t1)
   741  	fstest.CheckItems(t, r.Flocal, file1)
   742  
   743  	file2 := file1
   744  	file2.Path = "sub/file2"
   745  
   746  	err := operations.MoveFile(context.Background(), r.Fremote, r.Flocal, file2.Path, file1.Path)
   747  	require.NoError(t, err)
   748  	fstest.CheckItems(t, r.Flocal)
   749  	fstest.CheckItems(t, r.Fremote, file2)
   750  
   751  	r.WriteFile("file1", "file1 contents", t1)
   752  	fstest.CheckItems(t, r.Flocal, file1)
   753  
   754  	err = operations.MoveFile(context.Background(), r.Fremote, r.Flocal, file2.Path, file1.Path)
   755  	require.NoError(t, err)
   756  	fstest.CheckItems(t, r.Flocal)
   757  	fstest.CheckItems(t, r.Fremote, file2)
   758  
   759  	err = operations.MoveFile(context.Background(), r.Fremote, r.Fremote, file2.Path, file2.Path)
   760  	require.NoError(t, err)
   761  	fstest.CheckItems(t, r.Flocal)
   762  	fstest.CheckItems(t, r.Fremote, file2)
   763  }
   764  
   765  func TestCaseInsensitiveMoveFile(t *testing.T) {
   766  	r := fstest.NewRun(t)
   767  	defer r.Finalise()
   768  	if !r.Fremote.Features().CaseInsensitive {
   769  		return
   770  	}
   771  
   772  	file1 := r.WriteFile("file1", "file1 contents", t1)
   773  	fstest.CheckItems(t, r.Flocal, file1)
   774  
   775  	file2 := file1
   776  	file2.Path = "sub/file2"
   777  
   778  	err := operations.MoveFile(context.Background(), r.Fremote, r.Flocal, file2.Path, file1.Path)
   779  	require.NoError(t, err)
   780  	fstest.CheckItems(t, r.Flocal)
   781  	fstest.CheckItems(t, r.Fremote, file2)
   782  
   783  	r.WriteFile("file1", "file1 contents", t1)
   784  	fstest.CheckItems(t, r.Flocal, file1)
   785  
   786  	err = operations.MoveFile(context.Background(), r.Fremote, r.Flocal, file2.Path, file1.Path)
   787  	require.NoError(t, err)
   788  	fstest.CheckItems(t, r.Flocal)
   789  	fstest.CheckItems(t, r.Fremote, file2)
   790  
   791  	file2Capitalized := file2
   792  	file2Capitalized.Path = "sub/File2"
   793  
   794  	err = operations.MoveFile(context.Background(), r.Fremote, r.Fremote, file2Capitalized.Path, file2.Path)
   795  	require.NoError(t, err)
   796  	fstest.CheckItems(t, r.Flocal)
   797  	fstest.CheckItems(t, r.Fremote, file2Capitalized)
   798  }
   799  
   800  func TestMoveFileBackupDir(t *testing.T) {
   801  	r := fstest.NewRun(t)
   802  	defer r.Finalise()
   803  	if !operations.CanServerSideMove(r.Fremote) {
   804  		t.Skip("Skipping test as remote does not support server side move or copy")
   805  	}
   806  
   807  	oldBackupDir := fs.Config.BackupDir
   808  	fs.Config.BackupDir = r.FremoteName + "/backup"
   809  	defer func() {
   810  		fs.Config.BackupDir = oldBackupDir
   811  	}()
   812  
   813  	file1 := r.WriteFile("dst/file1", "file1 contents", t1)
   814  	fstest.CheckItems(t, r.Flocal, file1)
   815  
   816  	file1old := r.WriteObject(context.Background(), "dst/file1", "file1 contents old", t1)
   817  	fstest.CheckItems(t, r.Fremote, file1old)
   818  
   819  	err := operations.MoveFile(context.Background(), r.Fremote, r.Flocal, file1.Path, file1.Path)
   820  	require.NoError(t, err)
   821  	fstest.CheckItems(t, r.Flocal)
   822  	file1old.Path = "backup/dst/file1"
   823  	fstest.CheckItems(t, r.Fremote, file1old, file1)
   824  }
   825  
   826  func TestCopyFile(t *testing.T) {
   827  	r := fstest.NewRun(t)
   828  	defer r.Finalise()
   829  
   830  	file1 := r.WriteFile("file1", "file1 contents", t1)
   831  	fstest.CheckItems(t, r.Flocal, file1)
   832  
   833  	file2 := file1
   834  	file2.Path = "sub/file2"
   835  
   836  	err := operations.CopyFile(context.Background(), r.Fremote, r.Flocal, file2.Path, file1.Path)
   837  	require.NoError(t, err)
   838  	fstest.CheckItems(t, r.Flocal, file1)
   839  	fstest.CheckItems(t, r.Fremote, file2)
   840  
   841  	err = operations.CopyFile(context.Background(), r.Fremote, r.Flocal, file2.Path, file1.Path)
   842  	require.NoError(t, err)
   843  	fstest.CheckItems(t, r.Flocal, file1)
   844  	fstest.CheckItems(t, r.Fremote, file2)
   845  
   846  	err = operations.CopyFile(context.Background(), r.Fremote, r.Fremote, file2.Path, file2.Path)
   847  	require.NoError(t, err)
   848  	fstest.CheckItems(t, r.Flocal, file1)
   849  	fstest.CheckItems(t, r.Fremote, file2)
   850  }
   851  
   852  func TestCopyFileBackupDir(t *testing.T) {
   853  	r := fstest.NewRun(t)
   854  	defer r.Finalise()
   855  	if !operations.CanServerSideMove(r.Fremote) {
   856  		t.Skip("Skipping test as remote does not support server side move or copy")
   857  	}
   858  
   859  	oldBackupDir := fs.Config.BackupDir
   860  	fs.Config.BackupDir = r.FremoteName + "/backup"
   861  	defer func() {
   862  		fs.Config.BackupDir = oldBackupDir
   863  	}()
   864  
   865  	file1 := r.WriteFile("dst/file1", "file1 contents", t1)
   866  	fstest.CheckItems(t, r.Flocal, file1)
   867  
   868  	file1old := r.WriteObject(context.Background(), "dst/file1", "file1 contents old", t1)
   869  	fstest.CheckItems(t, r.Fremote, file1old)
   870  
   871  	err := operations.CopyFile(context.Background(), r.Fremote, r.Flocal, file1.Path, file1.Path)
   872  	require.NoError(t, err)
   873  	fstest.CheckItems(t, r.Flocal, file1)
   874  	file1old.Path = "backup/dst/file1"
   875  	fstest.CheckItems(t, r.Fremote, file1old, file1)
   876  }
   877  
   878  // Test with CompareDest set
   879  func TestCopyFileCompareDest(t *testing.T) {
   880  	r := fstest.NewRun(t)
   881  	defer r.Finalise()
   882  
   883  	fs.Config.CompareDest = r.FremoteName + "/CompareDest"
   884  	defer func() {
   885  		fs.Config.CompareDest = ""
   886  	}()
   887  	fdst, err := fs.NewFs(r.FremoteName + "/dst")
   888  	require.NoError(t, err)
   889  
   890  	// check empty dest, empty compare
   891  	file1 := r.WriteFile("one", "one", t1)
   892  	fstest.CheckItems(t, r.Flocal, file1)
   893  
   894  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file1.Path, file1.Path)
   895  	require.NoError(t, err)
   896  
   897  	file1dst := file1
   898  	file1dst.Path = "dst/one"
   899  
   900  	fstest.CheckItems(t, r.Fremote, file1dst)
   901  
   902  	// check old dest, empty compare
   903  	file1b := r.WriteFile("one", "onet2", t2)
   904  	fstest.CheckItems(t, r.Fremote, file1dst)
   905  	fstest.CheckItems(t, r.Flocal, file1b)
   906  
   907  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file1b.Path, file1b.Path)
   908  	require.NoError(t, err)
   909  
   910  	file1bdst := file1b
   911  	file1bdst.Path = "dst/one"
   912  
   913  	fstest.CheckItems(t, r.Fremote, file1bdst)
   914  
   915  	// check old dest, new compare
   916  	file3 := r.WriteObject(context.Background(), "dst/one", "one", t1)
   917  	file2 := r.WriteObject(context.Background(), "CompareDest/one", "onet2", t2)
   918  	file1c := r.WriteFile("one", "onet2", t2)
   919  	fstest.CheckItems(t, r.Fremote, file2, file3)
   920  	fstest.CheckItems(t, r.Flocal, file1c)
   921  
   922  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file1c.Path, file1c.Path)
   923  	require.NoError(t, err)
   924  
   925  	fstest.CheckItems(t, r.Fremote, file2, file3)
   926  
   927  	// check empty dest, new compare
   928  	file4 := r.WriteObject(context.Background(), "CompareDest/two", "two", t2)
   929  	file5 := r.WriteFile("two", "two", t2)
   930  	fstest.CheckItems(t, r.Fremote, file2, file3, file4)
   931  	fstest.CheckItems(t, r.Flocal, file1c, file5)
   932  
   933  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file5.Path, file5.Path)
   934  	require.NoError(t, err)
   935  
   936  	fstest.CheckItems(t, r.Fremote, file2, file3, file4)
   937  
   938  	// check new dest, new compare
   939  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file5.Path, file5.Path)
   940  	require.NoError(t, err)
   941  
   942  	fstest.CheckItems(t, r.Fremote, file2, file3, file4)
   943  
   944  	// check empty dest, old compare
   945  	file5b := r.WriteFile("two", "twot3", t3)
   946  	fstest.CheckItems(t, r.Fremote, file2, file3, file4)
   947  	fstest.CheckItems(t, r.Flocal, file1c, file5b)
   948  
   949  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file5b.Path, file5b.Path)
   950  	require.NoError(t, err)
   951  
   952  	file5bdst := file5b
   953  	file5bdst.Path = "dst/two"
   954  
   955  	fstest.CheckItems(t, r.Fremote, file2, file3, file4, file5bdst)
   956  }
   957  
   958  // Test with CopyDest set
   959  func TestCopyFileCopyDest(t *testing.T) {
   960  	r := fstest.NewRun(t)
   961  	defer r.Finalise()
   962  
   963  	if r.Fremote.Features().Copy == nil {
   964  		t.Skip("Skipping test as remote does not support server side copy")
   965  	}
   966  
   967  	fs.Config.CopyDest = r.FremoteName + "/CopyDest"
   968  	defer func() {
   969  		fs.Config.CopyDest = ""
   970  	}()
   971  
   972  	fdst, err := fs.NewFs(r.FremoteName + "/dst")
   973  	require.NoError(t, err)
   974  
   975  	// check empty dest, empty copy
   976  	file1 := r.WriteFile("one", "one", t1)
   977  	fstest.CheckItems(t, r.Flocal, file1)
   978  
   979  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file1.Path, file1.Path)
   980  	require.NoError(t, err)
   981  
   982  	file1dst := file1
   983  	file1dst.Path = "dst/one"
   984  
   985  	fstest.CheckItems(t, r.Fremote, file1dst)
   986  
   987  	// check old dest, empty copy
   988  	file1b := r.WriteFile("one", "onet2", t2)
   989  	fstest.CheckItems(t, r.Fremote, file1dst)
   990  	fstest.CheckItems(t, r.Flocal, file1b)
   991  
   992  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file1b.Path, file1b.Path)
   993  	require.NoError(t, err)
   994  
   995  	file1bdst := file1b
   996  	file1bdst.Path = "dst/one"
   997  
   998  	fstest.CheckItems(t, r.Fremote, file1bdst)
   999  
  1000  	// check old dest, new copy, backup-dir
  1001  
  1002  	fs.Config.BackupDir = r.FremoteName + "/BackupDir"
  1003  
  1004  	file3 := r.WriteObject(context.Background(), "dst/one", "one", t1)
  1005  	file2 := r.WriteObject(context.Background(), "CopyDest/one", "onet2", t2)
  1006  	file1c := r.WriteFile("one", "onet2", t2)
  1007  	fstest.CheckItems(t, r.Fremote, file2, file3)
  1008  	fstest.CheckItems(t, r.Flocal, file1c)
  1009  
  1010  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file1c.Path, file1c.Path)
  1011  	require.NoError(t, err)
  1012  
  1013  	file2dst := file2
  1014  	file2dst.Path = "dst/one"
  1015  	file3.Path = "BackupDir/one"
  1016  
  1017  	fstest.CheckItems(t, r.Fremote, file2, file2dst, file3)
  1018  	fs.Config.BackupDir = ""
  1019  
  1020  	// check empty dest, new copy
  1021  	file4 := r.WriteObject(context.Background(), "CopyDest/two", "two", t2)
  1022  	file5 := r.WriteFile("two", "two", t2)
  1023  	fstest.CheckItems(t, r.Fremote, file2, file2dst, file3, file4)
  1024  	fstest.CheckItems(t, r.Flocal, file1c, file5)
  1025  
  1026  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file5.Path, file5.Path)
  1027  	require.NoError(t, err)
  1028  
  1029  	file4dst := file4
  1030  	file4dst.Path = "dst/two"
  1031  
  1032  	fstest.CheckItems(t, r.Fremote, file2, file2dst, file3, file4, file4dst)
  1033  
  1034  	// check new dest, new copy
  1035  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file5.Path, file5.Path)
  1036  	require.NoError(t, err)
  1037  
  1038  	fstest.CheckItems(t, r.Fremote, file2, file2dst, file3, file4, file4dst)
  1039  
  1040  	// check empty dest, old copy
  1041  	file6 := r.WriteObject(context.Background(), "CopyDest/three", "three", t2)
  1042  	file7 := r.WriteFile("three", "threet3", t3)
  1043  	fstest.CheckItems(t, r.Fremote, file2, file2dst, file3, file4, file4dst, file6)
  1044  	fstest.CheckItems(t, r.Flocal, file1c, file5, file7)
  1045  
  1046  	err = operations.CopyFile(context.Background(), fdst, r.Flocal, file7.Path, file7.Path)
  1047  	require.NoError(t, err)
  1048  
  1049  	file7dst := file7
  1050  	file7dst.Path = "dst/three"
  1051  
  1052  	fstest.CheckItems(t, r.Fremote, file2, file2dst, file3, file4, file4dst, file6, file7dst)
  1053  }
  1054  
  1055  // testFsInfo is for unit testing fs.Info
  1056  type testFsInfo struct {
  1057  	name      string
  1058  	root      string
  1059  	stringVal string
  1060  	precision time.Duration
  1061  	hashes    hash.Set
  1062  	features  fs.Features
  1063  }
  1064  
  1065  // Name of the remote (as passed into NewFs)
  1066  func (i *testFsInfo) Name() string { return i.name }
  1067  
  1068  // Root of the remote (as passed into NewFs)
  1069  func (i *testFsInfo) Root() string { return i.root }
  1070  
  1071  // String returns a description of the FS
  1072  func (i *testFsInfo) String() string { return i.stringVal }
  1073  
  1074  // Precision of the ModTimes in this Fs
  1075  func (i *testFsInfo) Precision() time.Duration { return i.precision }
  1076  
  1077  // Returns the supported hash types of the filesystem
  1078  func (i *testFsInfo) Hashes() hash.Set { return i.hashes }
  1079  
  1080  // Returns the supported hash types of the filesystem
  1081  func (i *testFsInfo) Features() *fs.Features { return &i.features }
  1082  
  1083  func TestSameConfig(t *testing.T) {
  1084  	a := &testFsInfo{name: "name", root: "root"}
  1085  	for _, test := range []struct {
  1086  		name     string
  1087  		root     string
  1088  		expected bool
  1089  	}{
  1090  		{"name", "root", true},
  1091  		{"name", "rooty", true},
  1092  		{"namey", "root", false},
  1093  		{"namey", "roott", false},
  1094  	} {
  1095  		b := &testFsInfo{name: test.name, root: test.root}
  1096  		actual := operations.SameConfig(a, b)
  1097  		assert.Equal(t, test.expected, actual)
  1098  		actual = operations.SameConfig(b, a)
  1099  		assert.Equal(t, test.expected, actual)
  1100  	}
  1101  }
  1102  
  1103  func TestSame(t *testing.T) {
  1104  	a := &testFsInfo{name: "name", root: "root"}
  1105  	for _, test := range []struct {
  1106  		name     string
  1107  		root     string
  1108  		expected bool
  1109  	}{
  1110  		{"name", "root", true},
  1111  		{"name", "rooty", false},
  1112  		{"namey", "root", false},
  1113  		{"namey", "roott", false},
  1114  	} {
  1115  		b := &testFsInfo{name: test.name, root: test.root}
  1116  		actual := operations.Same(a, b)
  1117  		assert.Equal(t, test.expected, actual)
  1118  		actual = operations.Same(b, a)
  1119  		assert.Equal(t, test.expected, actual)
  1120  	}
  1121  }
  1122  
  1123  func TestOverlapping(t *testing.T) {
  1124  	a := &testFsInfo{name: "name", root: "root"}
  1125  	slash := string(os.PathSeparator) // native path separator
  1126  	for _, test := range []struct {
  1127  		name     string
  1128  		root     string
  1129  		expected bool
  1130  	}{
  1131  		{"name", "root", true},
  1132  		{"namey", "root", false},
  1133  		{"name", "rooty", false},
  1134  		{"namey", "rooty", false},
  1135  		{"name", "roo", false},
  1136  		{"name", "root/toot", true},
  1137  		{"name", "root/toot/", true},
  1138  		{"name", "root" + slash + "toot", true},
  1139  		{"name", "root" + slash + "toot" + slash, true},
  1140  		{"name", "", true},
  1141  		{"name", "/", true},
  1142  	} {
  1143  		b := &testFsInfo{name: test.name, root: test.root}
  1144  		what := fmt.Sprintf("(%q,%q) vs (%q,%q)", a.name, a.root, b.name, b.root)
  1145  		actual := operations.Overlapping(a, b)
  1146  		assert.Equal(t, test.expected, actual, what)
  1147  		actual = operations.Overlapping(b, a)
  1148  		assert.Equal(t, test.expected, actual, what)
  1149  	}
  1150  }
  1151  
  1152  type errorReader struct {
  1153  	err error
  1154  }
  1155  
  1156  func (er errorReader) Read(p []byte) (n int, err error) {
  1157  	return 0, er.err
  1158  }
  1159  
  1160  func TestCheckEqualReaders(t *testing.T) {
  1161  	b65a := make([]byte, 65*1024)
  1162  	b65b := make([]byte, 65*1024)
  1163  	b65b[len(b65b)-1] = 1
  1164  	b66 := make([]byte, 66*1024)
  1165  
  1166  	differ, err := operations.CheckEqualReaders(bytes.NewBuffer(b65a), bytes.NewBuffer(b65a))
  1167  	assert.NoError(t, err)
  1168  	assert.Equal(t, differ, false)
  1169  
  1170  	differ, err = operations.CheckEqualReaders(bytes.NewBuffer(b65a), bytes.NewBuffer(b65b))
  1171  	assert.NoError(t, err)
  1172  	assert.Equal(t, differ, true)
  1173  
  1174  	differ, err = operations.CheckEqualReaders(bytes.NewBuffer(b65a), bytes.NewBuffer(b66))
  1175  	assert.NoError(t, err)
  1176  	assert.Equal(t, differ, true)
  1177  
  1178  	differ, err = operations.CheckEqualReaders(bytes.NewBuffer(b66), bytes.NewBuffer(b65a))
  1179  	assert.NoError(t, err)
  1180  	assert.Equal(t, differ, true)
  1181  
  1182  	myErr := errors.New("sentinel")
  1183  	wrap := func(b []byte) io.Reader {
  1184  		r := bytes.NewBuffer(b)
  1185  		e := errorReader{myErr}
  1186  		return io.MultiReader(r, e)
  1187  	}
  1188  
  1189  	differ, err = operations.CheckEqualReaders(wrap(b65a), bytes.NewBuffer(b65a))
  1190  	assert.Equal(t, myErr, err)
  1191  	assert.Equal(t, differ, true)
  1192  
  1193  	differ, err = operations.CheckEqualReaders(wrap(b65a), bytes.NewBuffer(b65b))
  1194  	assert.Equal(t, myErr, err)
  1195  	assert.Equal(t, differ, true)
  1196  
  1197  	differ, err = operations.CheckEqualReaders(wrap(b65a), bytes.NewBuffer(b66))
  1198  	assert.Equal(t, myErr, err)
  1199  	assert.Equal(t, differ, true)
  1200  
  1201  	differ, err = operations.CheckEqualReaders(wrap(b66), bytes.NewBuffer(b65a))
  1202  	assert.Equal(t, myErr, err)
  1203  	assert.Equal(t, differ, true)
  1204  
  1205  	differ, err = operations.CheckEqualReaders(bytes.NewBuffer(b65a), wrap(b65a))
  1206  	assert.Equal(t, myErr, err)
  1207  	assert.Equal(t, differ, true)
  1208  
  1209  	differ, err = operations.CheckEqualReaders(bytes.NewBuffer(b65a), wrap(b65b))
  1210  	assert.Equal(t, myErr, err)
  1211  	assert.Equal(t, differ, true)
  1212  
  1213  	differ, err = operations.CheckEqualReaders(bytes.NewBuffer(b65a), wrap(b66))
  1214  	assert.Equal(t, myErr, err)
  1215  	assert.Equal(t, differ, true)
  1216  
  1217  	differ, err = operations.CheckEqualReaders(bytes.NewBuffer(b66), wrap(b65a))
  1218  	assert.Equal(t, myErr, err)
  1219  	assert.Equal(t, differ, true)
  1220  }
  1221  
  1222  func TestListFormat(t *testing.T) {
  1223  	item0 := &operations.ListJSONItem{
  1224  		Path:      "a",
  1225  		Name:      "a",
  1226  		Encrypted: "encryptedFileName",
  1227  		Size:      1,
  1228  		MimeType:  "application/octet-stream",
  1229  		ModTime: operations.Timestamp{
  1230  			When:   t1,
  1231  			Format: "2006-01-02T15:04:05.000000000Z07:00"},
  1232  		IsDir: false,
  1233  		Hashes: map[string]string{
  1234  			"MD5":          "0cc175b9c0f1b6a831c399e269772661",
  1235  			"SHA-1":        "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
  1236  			"DropboxHash":  "bf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8",
  1237  			"QuickXorHash": "6100000000000000000000000100000000000000"},
  1238  		ID:     "fileID",
  1239  		OrigID: "fileOrigID",
  1240  	}
  1241  
  1242  	item1 := &operations.ListJSONItem{
  1243  		Path:      "subdir",
  1244  		Name:      "subdir",
  1245  		Encrypted: "encryptedDirName",
  1246  		Size:      -1,
  1247  		MimeType:  "inode/directory",
  1248  		ModTime: operations.Timestamp{
  1249  			When:   t2,
  1250  			Format: "2006-01-02T15:04:05.000000000Z07:00"},
  1251  		IsDir:  true,
  1252  		Hashes: map[string]string(nil),
  1253  		ID:     "dirID",
  1254  		OrigID: "dirOrigID",
  1255  	}
  1256  
  1257  	var list operations.ListFormat
  1258  	list.AddPath()
  1259  	list.SetDirSlash(false)
  1260  	assert.Equal(t, "subdir", list.Format(item1))
  1261  
  1262  	list.SetDirSlash(true)
  1263  	assert.Equal(t, "subdir/", list.Format(item1))
  1264  
  1265  	list.SetOutput(nil)
  1266  	assert.Equal(t, "", list.Format(item1))
  1267  
  1268  	list.AppendOutput(func(item *operations.ListJSONItem) string { return "a" })
  1269  	list.AppendOutput(func(item *operations.ListJSONItem) string { return "b" })
  1270  	assert.Equal(t, "ab", list.Format(item1))
  1271  	list.SetSeparator(":::")
  1272  	assert.Equal(t, "a:::b", list.Format(item1))
  1273  
  1274  	list.SetOutput(nil)
  1275  	list.AddModTime()
  1276  	assert.Equal(t, t1.Local().Format("2006-01-02 15:04:05"), list.Format(item0))
  1277  
  1278  	list.SetOutput(nil)
  1279  	list.SetSeparator("|")
  1280  	list.AddID()
  1281  	list.AddOrigID()
  1282  	assert.Equal(t, "fileID|fileOrigID", list.Format(item0))
  1283  	assert.Equal(t, "dirID|dirOrigID", list.Format(item1))
  1284  
  1285  	list.SetOutput(nil)
  1286  	list.AddMimeType()
  1287  	assert.Contains(t, list.Format(item0), "/")
  1288  	assert.Equal(t, "inode/directory", list.Format(item1))
  1289  
  1290  	list.SetOutput(nil)
  1291  	list.AddPath()
  1292  	list.SetAbsolute(true)
  1293  	assert.Equal(t, "/a", list.Format(item0))
  1294  	list.SetAbsolute(false)
  1295  	assert.Equal(t, "a", list.Format(item0))
  1296  
  1297  	list.SetOutput(nil)
  1298  	list.AddSize()
  1299  	assert.Equal(t, "1", list.Format(item0))
  1300  
  1301  	list.AddPath()
  1302  	list.AddModTime()
  1303  	list.SetDirSlash(true)
  1304  	list.SetSeparator("__SEP__")
  1305  	assert.Equal(t, "1__SEP__a__SEP__"+t1.Local().Format("2006-01-02 15:04:05"), list.Format(item0))
  1306  	assert.Equal(t, "-1__SEP__subdir/__SEP__"+t2.Local().Format("2006-01-02 15:04:05"), list.Format(item1))
  1307  
  1308  	for _, test := range []struct {
  1309  		ht   hash.Type
  1310  		want string
  1311  	}{
  1312  		{hash.MD5, "0cc175b9c0f1b6a831c399e269772661"},
  1313  		{hash.SHA1, "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"},
  1314  	} {
  1315  		list.SetOutput(nil)
  1316  		list.AddHash(test.ht)
  1317  		assert.Equal(t, test.want, list.Format(item0))
  1318  	}
  1319  
  1320  	list.SetOutput(nil)
  1321  	list.SetSeparator("|")
  1322  	list.SetCSV(true)
  1323  	list.AddSize()
  1324  	list.AddPath()
  1325  	list.AddModTime()
  1326  	list.SetDirSlash(true)
  1327  	assert.Equal(t, "1|a|"+t1.Local().Format("2006-01-02 15:04:05"), list.Format(item0))
  1328  	assert.Equal(t, "-1|subdir/|"+t2.Local().Format("2006-01-02 15:04:05"), list.Format(item1))
  1329  
  1330  	list.SetOutput(nil)
  1331  	list.SetSeparator("|")
  1332  	list.AddPath()
  1333  	list.AddEncrypted()
  1334  	assert.Equal(t, "a|encryptedFileName", list.Format(item0))
  1335  	assert.Equal(t, "subdir/|encryptedDirName/", list.Format(item1))
  1336  
  1337  }
  1338  
  1339  func TestDirMove(t *testing.T) {
  1340  	r := fstest.NewRun(t)
  1341  	defer r.Finalise()
  1342  
  1343  	r.Mkdir(context.Background(), r.Fremote)
  1344  
  1345  	// Make some files and dirs
  1346  	r.ForceMkdir(context.Background(), r.Fremote)
  1347  	files := []fstest.Item{
  1348  		r.WriteObject(context.Background(), "A1/one", "one", t1),
  1349  		r.WriteObject(context.Background(), "A1/two", "two", t2),
  1350  		r.WriteObject(context.Background(), "A1/B1/three", "three", t3),
  1351  		r.WriteObject(context.Background(), "A1/B1/C1/four", "four", t1),
  1352  		r.WriteObject(context.Background(), "A1/B1/C2/five", "five", t2),
  1353  	}
  1354  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B2"))
  1355  	require.NoError(t, operations.Mkdir(context.Background(), r.Fremote, "A1/B1/C3"))
  1356  
  1357  	fstest.CheckListingWithPrecision(
  1358  		t,
  1359  		r.Fremote,
  1360  		files,
  1361  		[]string{
  1362  			"A1",
  1363  			"A1/B1",
  1364  			"A1/B2",
  1365  			"A1/B1/C1",
  1366  			"A1/B1/C2",
  1367  			"A1/B1/C3",
  1368  		},
  1369  		fs.GetModifyWindow(r.Fremote),
  1370  	)
  1371  
  1372  	require.NoError(t, operations.DirMove(context.Background(), r.Fremote, "A1", "A2"))
  1373  
  1374  	for i := range files {
  1375  		files[i].Path = strings.Replace(files[i].Path, "A1/", "A2/", -1)
  1376  	}
  1377  
  1378  	fstest.CheckListingWithPrecision(
  1379  		t,
  1380  		r.Fremote,
  1381  		files,
  1382  		[]string{
  1383  			"A2",
  1384  			"A2/B1",
  1385  			"A2/B2",
  1386  			"A2/B1/C1",
  1387  			"A2/B1/C2",
  1388  			"A2/B1/C3",
  1389  		},
  1390  		fs.GetModifyWindow(r.Fremote),
  1391  	)
  1392  
  1393  	// Disable DirMove
  1394  	features := r.Fremote.Features()
  1395  	oldDirMove := features.DirMove
  1396  	features.DirMove = nil
  1397  	defer func() {
  1398  		features.DirMove = oldDirMove
  1399  	}()
  1400  
  1401  	require.NoError(t, operations.DirMove(context.Background(), r.Fremote, "A2", "A3"))
  1402  
  1403  	for i := range files {
  1404  		files[i].Path = strings.Replace(files[i].Path, "A2/", "A3/", -1)
  1405  	}
  1406  
  1407  	fstest.CheckListingWithPrecision(
  1408  		t,
  1409  		r.Fremote,
  1410  		files,
  1411  		[]string{
  1412  			"A3",
  1413  			"A3/B1",
  1414  			"A3/B2",
  1415  			"A3/B1/C1",
  1416  			"A3/B1/C2",
  1417  			"A3/B1/C3",
  1418  		},
  1419  		fs.GetModifyWindow(r.Fremote),
  1420  	)
  1421  
  1422  }
  1423  
  1424  func TestGetFsInfo(t *testing.T) {
  1425  	r := fstest.NewRun(t)
  1426  	defer r.Finalise()
  1427  
  1428  	f := r.Fremote
  1429  	info := operations.GetFsInfo(f)
  1430  	assert.Equal(t, f.Name(), info.Name)
  1431  	assert.Equal(t, f.Root(), info.Root)
  1432  	assert.Equal(t, f.String(), info.String)
  1433  	assert.Equal(t, f.Precision(), info.Precision)
  1434  	hashSet := hash.NewHashSet()
  1435  	for _, hashName := range info.Hashes {
  1436  		var ht hash.Type
  1437  		require.NoError(t, ht.Set(hashName))
  1438  		hashSet.Add(ht)
  1439  	}
  1440  	assert.Equal(t, f.Hashes(), hashSet)
  1441  	assert.Equal(t, f.Features().Enabled(), info.Features)
  1442  }