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 }