@@ -11,6 +11,7 @@ import (
1111 "strconv"
1212 "strings"
1313 "sync/atomic"
14+ "syscall"
1415 "testing"
1516 "time"
1617)
@@ -283,3 +284,65 @@ func TestE2EGlobalRateLimit(t *testing.T) {
283284 t .Fatalf ("download mismatch or error: %v" , err )
284285 }
285286}
287+
288+ func TestE2EInterruptCancelsAndSavesState (t * testing.T ) {
289+ displayProgress = false
290+ restoreCwd := withTempCwd (t )
291+ defer restoreCwd ()
292+ restoreDF := withTestDataFolder (t )
293+ defer restoreDF ()
294+
295+ content := makeContent (5 * 1024 * 1024 ) // 5MB
296+ path := "/sigint.bin"
297+ ts , _ := startTestServer (t , content , true , true , true , true , path )
298+ url := ts .URL + path
299+
300+ // Send SIGINT shortly after starting the download to simulate Ctrl+C.
301+ doneSig := make (chan struct {})
302+ go func () {
303+ time .Sleep (200 * time .Millisecond )
304+ _ = syscall .Kill (os .Getpid (), syscall .SIGINT )
305+ close (doneSig )
306+ }()
307+
308+ start := time .Now ()
309+ Execute (url , nil , 4 , false , "" , "50KB" )
310+ <- doneSig
311+ dur := time .Since (start )
312+
313+ // We expect Execute to return promptly after the interrupt.
314+ if dur > 10 * time .Second {
315+ t .Fatalf ("interrupt handling too slow: %v" , dur )
316+ }
317+
318+ // State should be saved, and final joined file should not exist.
319+ usr , _ := user .Current ()
320+ folder := filepath .Join (usr .HomeDir , dataFolder , TaskFromURL (url ))
321+ statePath := filepath .Join (folder , stateFileName )
322+ if _ , err := os .Stat (statePath ); err != nil {
323+ t .Fatalf ("expected state saved at %s, got err=%v" , statePath , err )
324+ }
325+ if _ , err := os .Stat (TaskFromURL (url )); err == nil {
326+ t .Fatalf ("unexpected final file created despite interrupt" )
327+ }
328+
329+ // At least one part file should exist in the folder.
330+ entries , err := os .ReadDir (folder )
331+ if err != nil {
332+ t .Fatalf ("failed to read folder: %v" , err )
333+ }
334+ foundPart := false
335+ for _ , e := range entries {
336+ if e .IsDir () {
337+ continue
338+ }
339+ name := e .Name ()
340+ if strings .HasPrefix (name , TaskFromURL (url )+ ".part" ) {
341+ foundPart = true
342+ break
343+ }
344+ }
345+ if ! foundPart {
346+ t .Fatalf ("expected part files in %s after interrupt" , folder )
347+ }
348+ }
0 commit comments