@@ -2762,6 +2762,81 @@ func TestSyncConcurrentTruncate(t *testing.T) {
27622762 testSyncConcurrent (t , "truncate" )
27632763}
27642764
2765+ // Test that sync replaces dir modtimes in dst if they've changed
2766+ func testSyncReplaceDirModTime (t * testing.T , copyEmptySrcDirs bool ) {
2767+ accounting .GlobalStats ().ResetCounters ()
2768+ ctx , _ := fs .AddConfig (context .Background ())
2769+ r := fstest .NewRun (t )
2770+
2771+ file1 := r .WriteFile ("file1" , "file1" , t2 )
2772+ file2 := r .WriteFile ("test_dir1/file2" , "file2" , t2 )
2773+ file3 := r .WriteFile ("test_dir2/sub_dir/file3" , "file3" , t2 )
2774+ r .CheckLocalItems (t , file1 , file2 , file3 )
2775+
2776+ _ , err := operations .MkdirModTime (ctx , r .Flocal , "empty_dir" , t2 )
2777+ require .NoError (t , err )
2778+
2779+ // A directory that's empty on both src and dst
2780+ _ , err = operations .MkdirModTime (ctx , r .Flocal , "empty_on_remote" , t2 )
2781+ require .NoError (t , err )
2782+ _ , err = operations .MkdirModTime (ctx , r .Fremote , "empty_on_remote" , t2 )
2783+ require .NoError (t , err )
2784+
2785+ // set logging
2786+ // (this checks log output as DirModtime operations do not yet have stats, and r.CheckDirectoryModTimes also does not tell us what actions were taken)
2787+ oldLogLevel := fs .GetConfig (context .Background ()).LogLevel
2788+ defer func () { fs .GetConfig (context .Background ()).LogLevel = oldLogLevel }() // reset to old val after test
2789+ // need to do this as fs.Infof only respects the globalConfig
2790+ fs .GetConfig (context .Background ()).LogLevel = fs .LogLevelInfo
2791+
2792+ // First run
2793+ accounting .GlobalStats ().ResetCounters ()
2794+ ctx = predictDstFromLogger (ctx )
2795+ output := bilib .CaptureOutput (func () {
2796+ err := CopyDir (ctx , r .Fremote , r .Flocal , copyEmptySrcDirs )
2797+ require .NoError (t , err )
2798+ })
2799+ require .NotNil (t , output )
2800+ testLoggerVsLsf (ctx , r .Fremote , operations .GetLoggerOpt (ctx ).JSON , t )
2801+
2802+ // Save all dirs
2803+ dirs := []string {"test_dir1" , "test_dir2" , "test_dir2/sub_dir" , "empty_on_remote" }
2804+ if copyEmptySrcDirs {
2805+ dirs = append (dirs , "empty_dir" )
2806+ }
2807+
2808+ // Change dir modtimes
2809+ for _ , dir := range dirs {
2810+ _ , err := operations .SetDirModTime (ctx , r .Flocal , nil , dir , t1 )
2811+ if err != nil && ! errors .Is (err , fs .ErrorNotImplemented ) {
2812+ require .NoError (t , err )
2813+ }
2814+ }
2815+
2816+ // Run again
2817+ accounting .GlobalStats ().ResetCounters ()
2818+ ctx = predictDstFromLogger (ctx )
2819+ output = bilib .CaptureOutput (func () {
2820+ err := CopyDir (ctx , r .Fremote , r .Flocal , copyEmptySrcDirs )
2821+ require .NoError (t , err )
2822+ })
2823+ require .NotNil (t , output )
2824+ testLoggerVsLsf (ctx , r .Fremote , operations .GetLoggerOpt (ctx ).JSON , t )
2825+ r .CheckLocalItems (t , file1 , file2 , file3 )
2826+ r .CheckRemoteItems (t , file1 , file2 , file3 )
2827+
2828+ // Check that the modtimes of the directories are as expected
2829+ r .CheckDirectoryModTimes (t , dirs ... )
2830+ }
2831+
2832+ func TestSyncReplaceDirModTime (t * testing.T ) {
2833+ testSyncReplaceDirModTime (t , false )
2834+ }
2835+
2836+ func TestSyncReplaceDirModTimeWithEmptyDirs (t * testing.T ) {
2837+ testSyncReplaceDirModTime (t , true )
2838+ }
2839+
27652840// Tests that nothing is transferred when src and dst already match
27662841// Run the same sync twice, ensure no action is taken the second time
27672842func testNothingToTransfer (t * testing.T , copyEmptySrcDirs bool ) {
0 commit comments