github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/googlephotos/pattern_test.go (about)

     1  package googlephotos
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/rclone/rclone/backend/googlephotos/api"
    10  	"github.com/rclone/rclone/fs"
    11  	"github.com/rclone/rclone/fs/dirtree"
    12  	"github.com/rclone/rclone/fstest"
    13  	"github.com/rclone/rclone/fstest/mockobject"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  // time for directories
    19  var startTime = fstest.Time("2019-06-24T15:53:05.999999999Z")
    20  
    21  // mock Fs for testing patterns
    22  type testLister struct {
    23  	t        *testing.T
    24  	albums   *albums
    25  	names    []string
    26  	uploaded dirtree.DirTree
    27  }
    28  
    29  // newTestLister makes a mock for testing
    30  func newTestLister(t *testing.T) *testLister {
    31  	return &testLister{
    32  		t:        t,
    33  		albums:   newAlbums(),
    34  		uploaded: dirtree.New(),
    35  	}
    36  }
    37  
    38  // mock listDir for testing
    39  func (f *testLister) listDir(ctx context.Context, prefix string, filter api.SearchFilter) (entries fs.DirEntries, err error) {
    40  	for _, name := range f.names {
    41  		entries = append(entries, mockobject.New(prefix+name))
    42  	}
    43  	return entries, nil
    44  }
    45  
    46  // mock listAlbums for testing
    47  func (f *testLister) listAlbums(ctx context.Context, shared bool) (all *albums, err error) {
    48  	return f.albums, nil
    49  }
    50  
    51  // mock listUploads for testing
    52  func (f *testLister) listUploads(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
    53  	entries, _ = f.uploaded[dir]
    54  	return entries, nil
    55  }
    56  
    57  // mock dirTime for testing
    58  func (f *testLister) dirTime() time.Time {
    59  	return startTime
    60  }
    61  
    62  // mock startYear for testing
    63  func (f *testLister) startYear() int {
    64  	return 2000
    65  }
    66  
    67  func TestPatternMatch(t *testing.T) {
    68  	for testNumber, test := range []struct {
    69  		// input
    70  		root     string
    71  		itemPath string
    72  		isFile   bool
    73  		// expected output
    74  		wantMatch   []string
    75  		wantPrefix  string
    76  		wantPattern *dirPattern
    77  	}{
    78  		{
    79  			root:        "",
    80  			itemPath:    "",
    81  			isFile:      false,
    82  			wantMatch:   []string{""},
    83  			wantPrefix:  "",
    84  			wantPattern: &patterns[0],
    85  		},
    86  		{
    87  			root:        "",
    88  			itemPath:    "",
    89  			isFile:      true,
    90  			wantMatch:   nil,
    91  			wantPrefix:  "",
    92  			wantPattern: nil,
    93  		},
    94  		{
    95  			root:        "upload",
    96  			itemPath:    "",
    97  			isFile:      false,
    98  			wantMatch:   []string{"upload", ""},
    99  			wantPrefix:  "",
   100  			wantPattern: &patterns[1],
   101  		},
   102  		{
   103  			root:        "upload/dir",
   104  			itemPath:    "",
   105  			isFile:      false,
   106  			wantMatch:   []string{"upload/dir", "dir"},
   107  			wantPrefix:  "",
   108  			wantPattern: &patterns[1],
   109  		},
   110  		{
   111  			root:        "upload/file.jpg",
   112  			itemPath:    "",
   113  			isFile:      true,
   114  			wantMatch:   []string{"upload/file.jpg", "file.jpg"},
   115  			wantPrefix:  "",
   116  			wantPattern: &patterns[2],
   117  		},
   118  		{
   119  			root:        "media",
   120  			itemPath:    "",
   121  			isFile:      false,
   122  			wantMatch:   []string{"media"},
   123  			wantPrefix:  "",
   124  			wantPattern: &patterns[3],
   125  		},
   126  		{
   127  			root:        "",
   128  			itemPath:    "media",
   129  			isFile:      false,
   130  			wantMatch:   []string{"media"},
   131  			wantPrefix:  "media/",
   132  			wantPattern: &patterns[3],
   133  		},
   134  		{
   135  			root:        "media/all",
   136  			itemPath:    "",
   137  			isFile:      false,
   138  			wantMatch:   []string{"media/all"},
   139  			wantPrefix:  "",
   140  			wantPattern: &patterns[4],
   141  		},
   142  		{
   143  			root:        "media",
   144  			itemPath:    "all",
   145  			isFile:      false,
   146  			wantMatch:   []string{"media/all"},
   147  			wantPrefix:  "all/",
   148  			wantPattern: &patterns[4],
   149  		},
   150  		{
   151  			root:        "media/all",
   152  			itemPath:    "file.jpg",
   153  			isFile:      true,
   154  			wantMatch:   []string{"media/all/file.jpg", "file.jpg"},
   155  			wantPrefix:  "file.jpg/",
   156  			wantPattern: &patterns[5],
   157  		},
   158  		{
   159  			root:        "",
   160  			itemPath:    "feature",
   161  			isFile:      false,
   162  			wantMatch:   []string{"feature"},
   163  			wantPrefix:  "feature/",
   164  			wantPattern: &patterns[23],
   165  		},
   166  		{
   167  			root:        "feature/favorites",
   168  			itemPath:    "",
   169  			isFile:      false,
   170  			wantMatch:   []string{"feature/favorites"},
   171  			wantPrefix:  "",
   172  			wantPattern: &patterns[24],
   173  		},
   174  		{
   175  			root:        "feature",
   176  			itemPath:    "favorites",
   177  			isFile:      false,
   178  			wantMatch:   []string{"feature/favorites"},
   179  			wantPrefix:  "favorites/",
   180  			wantPattern: &patterns[24],
   181  		},
   182  		{
   183  			root:        "feature/favorites",
   184  			itemPath:    "file.jpg",
   185  			isFile:      true,
   186  			wantMatch:   []string{"feature/favorites/file.jpg", "file.jpg"},
   187  			wantPrefix:  "file.jpg/",
   188  			wantPattern: &patterns[25],
   189  		},
   190  	} {
   191  		t.Run(fmt.Sprintf("#%d,root=%q,itemPath=%q,isFile=%v", testNumber, test.root, test.itemPath, test.isFile), func(t *testing.T) {
   192  			gotMatch, gotPrefix, gotPattern := patterns.match(test.root, test.itemPath, test.isFile)
   193  			assert.Equal(t, test.wantMatch, gotMatch)
   194  			assert.Equal(t, test.wantPrefix, gotPrefix)
   195  			assert.Equal(t, test.wantPattern, gotPattern)
   196  		})
   197  	}
   198  }
   199  
   200  func TestPatternMatchToEntries(t *testing.T) {
   201  	ctx := context.Background()
   202  	f := newTestLister(t)
   203  	f.names = []string{"file.jpg"}
   204  	f.albums.add(&api.Album{
   205  		ID:    "1",
   206  		Title: "sub/one",
   207  	})
   208  	f.albums.add(&api.Album{
   209  		ID:    "2",
   210  		Title: "sub",
   211  	})
   212  	f.uploaded.AddEntry(mockobject.New("upload/file1.jpg"))
   213  	f.uploaded.AddEntry(mockobject.New("upload/dir/file2.jpg"))
   214  
   215  	for testNumber, test := range []struct {
   216  		// input
   217  		root     string
   218  		itemPath string
   219  		// expected output
   220  		wantMatch  []string
   221  		wantPrefix string
   222  		remotes    []string
   223  	}{
   224  		{
   225  			root:       "",
   226  			itemPath:   "",
   227  			wantMatch:  []string{""},
   228  			wantPrefix: "",
   229  			remotes:    []string{"media/", "album/", "shared-album/", "upload/"},
   230  		},
   231  		{
   232  			root:       "upload",
   233  			itemPath:   "",
   234  			wantMatch:  []string{"upload", ""},
   235  			wantPrefix: "",
   236  			remotes:    []string{"upload/file1.jpg", "upload/dir/"},
   237  		},
   238  		{
   239  			root:       "upload",
   240  			itemPath:   "dir",
   241  			wantMatch:  []string{"upload/dir", "dir"},
   242  			wantPrefix: "dir/",
   243  			remotes:    []string{"upload/dir/file2.jpg"},
   244  		},
   245  		{
   246  			root:       "media",
   247  			itemPath:   "",
   248  			wantMatch:  []string{"media"},
   249  			wantPrefix: "",
   250  			remotes:    []string{"all/", "by-year/", "by-month/", "by-day/"},
   251  		},
   252  		{
   253  			root:       "media/all",
   254  			itemPath:   "",
   255  			wantMatch:  []string{"media/all"},
   256  			wantPrefix: "",
   257  			remotes:    []string{"file.jpg"},
   258  		},
   259  		{
   260  			root:       "media",
   261  			itemPath:   "all",
   262  			wantMatch:  []string{"media/all"},
   263  			wantPrefix: "all/",
   264  			remotes:    []string{"all/file.jpg"},
   265  		},
   266  		{
   267  			root:       "media/by-year",
   268  			itemPath:   "",
   269  			wantMatch:  []string{"media/by-year"},
   270  			wantPrefix: "",
   271  			remotes:    []string{"2000/", "2001/", "2002/", "2003/"},
   272  		},
   273  		{
   274  			root:       "media/by-year/2000",
   275  			itemPath:   "",
   276  			wantMatch:  []string{"media/by-year/2000", "2000"},
   277  			wantPrefix: "",
   278  			remotes:    []string{"file.jpg"},
   279  		},
   280  		{
   281  			root:       "media/by-month",
   282  			itemPath:   "",
   283  			wantMatch:  []string{"media/by-month"},
   284  			wantPrefix: "",
   285  			remotes:    []string{"2000/", "2001/", "2002/", "2003/"},
   286  		},
   287  		{
   288  			root:       "media/by-month/2001",
   289  			itemPath:   "",
   290  			wantMatch:  []string{"media/by-month/2001", "2001"},
   291  			wantPrefix: "",
   292  			remotes:    []string{"2001-01/", "2001-02/", "2001-03/", "2001-04/"},
   293  		},
   294  		{
   295  			root:       "media/by-month/2001/2001-01",
   296  			itemPath:   "",
   297  			wantMatch:  []string{"media/by-month/2001/2001-01", "2001", "01"},
   298  			wantPrefix: "",
   299  			remotes:    []string{"file.jpg"},
   300  		},
   301  		{
   302  			root:       "media/by-day",
   303  			itemPath:   "",
   304  			wantMatch:  []string{"media/by-day"},
   305  			wantPrefix: "",
   306  			remotes:    []string{"2000/", "2001/", "2002/", "2003/"},
   307  		},
   308  		{
   309  			root:       "media/by-day/2001",
   310  			itemPath:   "",
   311  			wantMatch:  []string{"media/by-day/2001", "2001"},
   312  			wantPrefix: "",
   313  			remotes:    []string{"2001-01-01/", "2001-01-02/", "2001-01-03/", "2001-01-04/"},
   314  		},
   315  		{
   316  			root:       "media/by-day/2001/2001-01-02",
   317  			itemPath:   "",
   318  			wantMatch:  []string{"media/by-day/2001/2001-01-02", "2001", "01", "02"},
   319  			wantPrefix: "",
   320  			remotes:    []string{"file.jpg"},
   321  		},
   322  		{
   323  			root:       "album",
   324  			itemPath:   "",
   325  			wantMatch:  []string{"album"},
   326  			wantPrefix: "",
   327  			remotes:    []string{"sub/"},
   328  		},
   329  		{
   330  			root:       "album/sub",
   331  			itemPath:   "",
   332  			wantMatch:  []string{"album/sub", "sub"},
   333  			wantPrefix: "",
   334  			remotes:    []string{"one/", "file.jpg"},
   335  		},
   336  		{
   337  			root:       "album/sub/one",
   338  			itemPath:   "",
   339  			wantMatch:  []string{"album/sub/one", "sub/one"},
   340  			wantPrefix: "",
   341  			remotes:    []string{"file.jpg"},
   342  		},
   343  		{
   344  			root:       "shared-album",
   345  			itemPath:   "",
   346  			wantMatch:  []string{"shared-album"},
   347  			wantPrefix: "",
   348  			remotes:    []string{"sub/"},
   349  		},
   350  		{
   351  			root:       "shared-album/sub",
   352  			itemPath:   "",
   353  			wantMatch:  []string{"shared-album/sub", "sub"},
   354  			wantPrefix: "",
   355  			remotes:    []string{"one/", "file.jpg"},
   356  		},
   357  		{
   358  			root:       "shared-album/sub/one",
   359  			itemPath:   "",
   360  			wantMatch:  []string{"shared-album/sub/one", "sub/one"},
   361  			wantPrefix: "",
   362  			remotes:    []string{"file.jpg"},
   363  		},
   364  	} {
   365  		t.Run(fmt.Sprintf("#%d,root=%q,itemPath=%q", testNumber, test.root, test.itemPath), func(t *testing.T) {
   366  			match, prefix, pattern := patterns.match(test.root, test.itemPath, false)
   367  			assert.Equal(t, test.wantMatch, match)
   368  			assert.Equal(t, test.wantPrefix, prefix)
   369  			assert.NotNil(t, pattern)
   370  			assert.NotNil(t, pattern.toEntries)
   371  
   372  			entries, err := pattern.toEntries(ctx, f, prefix, match)
   373  			assert.NoError(t, err)
   374  			var remotes = []string{}
   375  			for _, entry := range entries {
   376  				remote := entry.Remote()
   377  				if _, isDir := entry.(fs.Directory); isDir {
   378  					remote += "/"
   379  				}
   380  				remotes = append(remotes, remote)
   381  				if len(remotes) >= 4 {
   382  					break // only test first 4 entries
   383  				}
   384  			}
   385  			assert.Equal(t, test.remotes, remotes)
   386  		})
   387  	}
   388  }
   389  
   390  func TestPatternYears(t *testing.T) {
   391  	f := newTestLister(t)
   392  	entries, err := years(context.Background(), f, "potato/", nil)
   393  	require.NoError(t, err)
   394  
   395  	year := 2000
   396  	for _, entry := range entries {
   397  		assert.Equal(t, "potato/"+fmt.Sprint(year), entry.Remote())
   398  		year++
   399  	}
   400  }
   401  
   402  func TestPatternMonths(t *testing.T) {
   403  	f := newTestLister(t)
   404  	entries, err := months(context.Background(), f, "potato/", []string{"", "2020"})
   405  	require.NoError(t, err)
   406  
   407  	assert.Equal(t, 12, len(entries))
   408  	for i, entry := range entries {
   409  		assert.Equal(t, fmt.Sprintf("potato/2020-%02d", i+1), entry.Remote())
   410  	}
   411  }
   412  
   413  func TestPatternDays(t *testing.T) {
   414  	f := newTestLister(t)
   415  	entries, err := days(context.Background(), f, "potato/", []string{"", "2020"})
   416  	require.NoError(t, err)
   417  
   418  	assert.Equal(t, 366, len(entries))
   419  	assert.Equal(t, "potato/2020-01-01", entries[0].Remote())
   420  	assert.Equal(t, "potato/2020-12-31", entries[len(entries)-1].Remote())
   421  }
   422  
   423  func TestPatternYearMonthDayFilter(t *testing.T) {
   424  	ctx := context.Background()
   425  	f := newTestLister(t)
   426  
   427  	// Years
   428  	sf, err := yearMonthDayFilter(ctx, f, []string{"", "2000"})
   429  	require.NoError(t, err)
   430  	assert.Equal(t, api.SearchFilter{
   431  		Filters: &api.Filters{
   432  			DateFilter: &api.DateFilter{
   433  				Dates: []api.Date{
   434  					{
   435  						Year: 2000,
   436  					},
   437  				},
   438  			},
   439  		},
   440  	}, sf)
   441  
   442  	_, err = yearMonthDayFilter(ctx, f, []string{"", "potato"})
   443  	require.Error(t, err)
   444  	_, err = yearMonthDayFilter(ctx, f, []string{"", "999"})
   445  	require.Error(t, err)
   446  	_, err = yearMonthDayFilter(ctx, f, []string{"", "4000"})
   447  	require.Error(t, err)
   448  
   449  	// Months
   450  	sf, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01"})
   451  	require.NoError(t, err)
   452  	assert.Equal(t, api.SearchFilter{
   453  		Filters: &api.Filters{
   454  			DateFilter: &api.DateFilter{
   455  				Dates: []api.Date{
   456  					{
   457  						Month: 1,
   458  						Year:  2000,
   459  					},
   460  				},
   461  			},
   462  		},
   463  	}, sf)
   464  
   465  	_, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "potato"})
   466  	require.Error(t, err)
   467  	_, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "0"})
   468  	require.Error(t, err)
   469  	_, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "13"})
   470  	require.Error(t, err)
   471  
   472  	// Days
   473  	sf, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "02"})
   474  	require.NoError(t, err)
   475  	assert.Equal(t, api.SearchFilter{
   476  		Filters: &api.Filters{
   477  			DateFilter: &api.DateFilter{
   478  				Dates: []api.Date{
   479  					{
   480  						Day:   2,
   481  						Month: 1,
   482  						Year:  2000,
   483  					},
   484  				},
   485  			},
   486  		},
   487  	}, sf)
   488  
   489  	_, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "potato"})
   490  	require.Error(t, err)
   491  	_, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "0"})
   492  	require.Error(t, err)
   493  	_, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "32"})
   494  	require.Error(t, err)
   495  }
   496  
   497  func TestPatternAlbumsToEntries(t *testing.T) {
   498  	f := newTestLister(t)
   499  	ctx := context.Background()
   500  
   501  	_, err := albumsToEntries(ctx, f, false, "potato/", "sub")
   502  	assert.Equal(t, fs.ErrorDirNotFound, err)
   503  
   504  	f.albums.add(&api.Album{
   505  		ID:    "1",
   506  		Title: "sub/one",
   507  	})
   508  
   509  	entries, err := albumsToEntries(ctx, f, false, "potato/", "sub")
   510  	assert.NoError(t, err)
   511  	assert.Equal(t, 1, len(entries))
   512  	assert.Equal(t, "potato/one", entries[0].Remote())
   513  	_, ok := entries[0].(fs.Directory)
   514  	assert.Equal(t, true, ok)
   515  
   516  	f.albums.add(&api.Album{
   517  		ID:    "1",
   518  		Title: "sub",
   519  	})
   520  	f.names = []string{"file.jpg"}
   521  
   522  	entries, err = albumsToEntries(ctx, f, false, "potato/", "sub")
   523  	assert.NoError(t, err)
   524  	assert.Equal(t, 2, len(entries))
   525  	assert.Equal(t, "potato/one", entries[0].Remote())
   526  	_, ok = entries[0].(fs.Directory)
   527  	assert.Equal(t, true, ok)
   528  	assert.Equal(t, "potato/file.jpg", entries[1].Remote())
   529  	_, ok = entries[1].(fs.Object)
   530  	assert.Equal(t, true, ok)
   531  
   532  }