github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/rc/job_test.go (about) 1 package rc 2 3 import ( 4 "context" 5 "runtime" 6 "testing" 7 "time" 8 9 "github.com/ncw/rclone/fs" 10 "github.com/pkg/errors" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestNewJobs(t *testing.T) { 16 jobs := newJobs() 17 assert.Equal(t, 0, len(jobs.jobs)) 18 } 19 20 func TestJobsKickExpire(t *testing.T) { 21 jobs := newJobs() 22 jobs.expireInterval = time.Millisecond 23 assert.Equal(t, false, jobs.expireRunning) 24 jobs.kickExpire() 25 jobs.mu.Lock() 26 assert.Equal(t, true, jobs.expireRunning) 27 jobs.mu.Unlock() 28 time.Sleep(10 * time.Millisecond) 29 jobs.mu.Lock() 30 assert.Equal(t, false, jobs.expireRunning) 31 jobs.mu.Unlock() 32 } 33 34 func TestJobsExpire(t *testing.T) { 35 wait := make(chan struct{}) 36 jobs := newJobs() 37 jobs.expireInterval = time.Millisecond 38 assert.Equal(t, false, jobs.expireRunning) 39 job := jobs.NewJob(func(ctx context.Context, in Params) (Params, error) { 40 defer close(wait) 41 return in, nil 42 }, Params{}) 43 <-wait 44 assert.Equal(t, 1, len(jobs.jobs)) 45 jobs.Expire() 46 assert.Equal(t, 1, len(jobs.jobs)) 47 jobs.mu.Lock() 48 job.mu.Lock() 49 job.EndTime = time.Now().Add(-fs.Config.RcJobExpireDuration - 60*time.Second) 50 assert.Equal(t, true, jobs.expireRunning) 51 job.mu.Unlock() 52 jobs.mu.Unlock() 53 time.Sleep(10 * time.Millisecond) 54 jobs.mu.Lock() 55 assert.Equal(t, false, jobs.expireRunning) 56 assert.Equal(t, 0, len(jobs.jobs)) 57 jobs.mu.Unlock() 58 } 59 60 var noopFn = func(ctx context.Context, in Params) (Params, error) { 61 return nil, nil 62 } 63 64 func TestJobsIDs(t *testing.T) { 65 jobs := newJobs() 66 job1 := jobs.NewJob(noopFn, Params{}) 67 job2 := jobs.NewJob(noopFn, Params{}) 68 wantIDs := []int64{job1.ID, job2.ID} 69 gotIDs := jobs.IDs() 70 require.Equal(t, 2, len(gotIDs)) 71 if gotIDs[0] != wantIDs[0] { 72 gotIDs[0], gotIDs[1] = gotIDs[1], gotIDs[0] 73 } 74 assert.Equal(t, wantIDs, gotIDs) 75 } 76 77 func TestJobsGet(t *testing.T) { 78 jobs := newJobs() 79 job := jobs.NewJob(noopFn, Params{}) 80 assert.Equal(t, job, jobs.Get(job.ID)) 81 assert.Nil(t, jobs.Get(123123123123)) 82 } 83 84 var longFn = func(ctx context.Context, in Params) (Params, error) { 85 time.Sleep(1 * time.Hour) 86 return nil, nil 87 } 88 89 var ctxFn = func(ctx context.Context, in Params) (Params, error) { 90 select { 91 case <-ctx.Done(): 92 return nil, ctx.Err() 93 } 94 } 95 96 const ( 97 sleepTime = 100 * time.Millisecond 98 floatSleepTime = float64(sleepTime) / 1E9 / 2 99 ) 100 101 // sleep for some time so job.Duration is non-0 102 func sleepJob() { 103 time.Sleep(sleepTime) 104 } 105 106 func TestJobFinish(t *testing.T) { 107 jobs := newJobs() 108 job := jobs.NewJob(longFn, Params{}) 109 sleepJob() 110 111 assert.Equal(t, true, job.EndTime.IsZero()) 112 assert.Equal(t, Params(nil), job.Output) 113 assert.Equal(t, 0.0, job.Duration) 114 assert.Equal(t, "", job.Error) 115 assert.Equal(t, false, job.Success) 116 assert.Equal(t, false, job.Finished) 117 118 wantOut := Params{"a": 1} 119 job.finish(wantOut, nil) 120 121 assert.Equal(t, false, job.EndTime.IsZero()) 122 assert.Equal(t, wantOut, job.Output) 123 assert.True(t, job.Duration >= floatSleepTime) 124 assert.Equal(t, "", job.Error) 125 assert.Equal(t, true, job.Success) 126 assert.Equal(t, true, job.Finished) 127 128 job = jobs.NewJob(longFn, Params{}) 129 sleepJob() 130 job.finish(nil, nil) 131 132 assert.Equal(t, false, job.EndTime.IsZero()) 133 assert.Equal(t, Params{}, job.Output) 134 assert.True(t, job.Duration >= floatSleepTime) 135 assert.Equal(t, "", job.Error) 136 assert.Equal(t, true, job.Success) 137 assert.Equal(t, true, job.Finished) 138 139 job = jobs.NewJob(longFn, Params{}) 140 sleepJob() 141 job.finish(wantOut, errors.New("potato")) 142 143 assert.Equal(t, false, job.EndTime.IsZero()) 144 assert.Equal(t, wantOut, job.Output) 145 assert.True(t, job.Duration >= floatSleepTime) 146 assert.Equal(t, "potato", job.Error) 147 assert.Equal(t, false, job.Success) 148 assert.Equal(t, true, job.Finished) 149 } 150 151 // We've tested the functionality of run() already as it is 152 // part of NewJob, now just test the panic catching 153 func TestJobRunPanic(t *testing.T) { 154 wait := make(chan struct{}) 155 boom := func(ctx context.Context, in Params) (Params, error) { 156 sleepJob() 157 defer close(wait) 158 panic("boom") 159 } 160 161 jobs := newJobs() 162 job := jobs.NewJob(boom, Params{}) 163 <-wait 164 runtime.Gosched() // yield to make sure job is updated 165 166 // Wait a short time for the panic to propagate 167 for i := uint(0); i < 10; i++ { 168 job.mu.Lock() 169 e := job.Error 170 job.mu.Unlock() 171 if e != "" { 172 break 173 } 174 time.Sleep(time.Millisecond << i) 175 } 176 177 job.mu.Lock() 178 assert.Equal(t, false, job.EndTime.IsZero()) 179 assert.Equal(t, Params{}, job.Output) 180 assert.True(t, job.Duration >= floatSleepTime) 181 assert.Equal(t, "panic received: boom", job.Error) 182 assert.Equal(t, false, job.Success) 183 assert.Equal(t, true, job.Finished) 184 job.mu.Unlock() 185 } 186 187 func TestJobsNewJob(t *testing.T) { 188 jobID = 0 189 jobs := newJobs() 190 job := jobs.NewJob(noopFn, Params{}) 191 assert.Equal(t, int64(1), job.ID) 192 assert.Equal(t, job, jobs.Get(1)) 193 assert.NotEmpty(t, job.Stop) 194 } 195 196 func TestStartJob(t *testing.T) { 197 jobID = 0 198 out, err := StartJob(longFn, Params{}) 199 assert.NoError(t, err) 200 assert.Equal(t, Params{"jobid": int64(1)}, out) 201 } 202 203 func TestRcJobStatus(t *testing.T) { 204 jobID = 0 205 _, err := StartJob(longFn, Params{}) 206 assert.NoError(t, err) 207 208 call := Calls.Get("job/status") 209 assert.NotNil(t, call) 210 in := Params{"jobid": 1} 211 out, err := call.Fn(context.Background(), in) 212 require.NoError(t, err) 213 require.NotNil(t, out) 214 assert.Equal(t, float64(1), out["id"]) 215 assert.Equal(t, "", out["error"]) 216 assert.Equal(t, false, out["finished"]) 217 assert.Equal(t, false, out["success"]) 218 219 in = Params{"jobid": 123123123} 220 _, err = call.Fn(context.Background(), in) 221 require.Error(t, err) 222 assert.Contains(t, err.Error(), "job not found") 223 224 in = Params{"jobidx": 123123123} 225 _, err = call.Fn(context.Background(), in) 226 require.Error(t, err) 227 assert.Contains(t, err.Error(), "Didn't find key") 228 } 229 230 func TestRcJobList(t *testing.T) { 231 jobID = 0 232 _, err := StartJob(longFn, Params{}) 233 assert.NoError(t, err) 234 235 call := Calls.Get("job/list") 236 assert.NotNil(t, call) 237 in := Params{} 238 out, err := call.Fn(context.Background(), in) 239 require.NoError(t, err) 240 require.NotNil(t, out) 241 assert.Equal(t, Params{"jobids": []int64{1}}, out) 242 } 243 244 func TestRcJobStop(t *testing.T) { 245 jobID = 0 246 _, err := StartJob(ctxFn, Params{}) 247 assert.NoError(t, err) 248 249 call := Calls.Get("job/stop") 250 assert.NotNil(t, call) 251 in := Params{"jobid": 1} 252 out, err := call.Fn(context.Background(), in) 253 require.NoError(t, err) 254 require.Empty(t, out) 255 256 in = Params{"jobid": 123123123} 257 _, err = call.Fn(context.Background(), in) 258 require.Error(t, err) 259 assert.Contains(t, err.Error(), "job not found") 260 261 in = Params{"jobidx": 123123123} 262 _, err = call.Fn(context.Background(), in) 263 require.Error(t, err) 264 assert.Contains(t, err.Error(), "Didn't find key") 265 266 time.Sleep(10 * time.Millisecond) 267 268 call = Calls.Get("job/status") 269 assert.NotNil(t, call) 270 in = Params{"jobid": 1} 271 out, err = call.Fn(context.Background(), in) 272 require.NoError(t, err) 273 require.NotNil(t, out) 274 assert.Equal(t, float64(1), out["id"]) 275 assert.Equal(t, "context canceled", out["error"]) 276 assert.Equal(t, true, out["finished"]) 277 assert.Equal(t, false, out["success"]) 278 }