package download import ( "context" "errors" "strings" "testing" "time" ) // A peer that has queued us is judged by where we are in its queue, not // only by a timer (#275). func queuedThen(polls int, final string) [][]slskdTransfer { queued := []slskdTransfer{ {ID: "t0", Filename: `\s\Album\01 A.flac`, State: "Queued, Remotely"}, {ID: "t1", Filename: `\s\Album\02 B.flac`, State: "Queued, Remotely"}, } out := make([][]slskdTransfer, 0, polls+1) for range polls { out = append(out, queued) } return append(out, []slskdTransfer{ {ID: "t0", Filename: `\s\Album\01 A.flac`, State: final, BytesTransferred: 500}, {ID: "t1", Filename: `\s\Album\02 B.flac`, State: final, BytesTransferred: 500}, }) } func descending(from int) []int { out := make([]int, 0, from) for p := from; p >= 1; p-- { out = append(out, p) } return out } // Two readings far back in the queue give the peer up at once, rather // than after the stall timer. func TestSlskdGivesUpOnALongQueue(t *testing.T) { t.Parallel() stub := newSlskdStub(t) stub.transfers = queuedThen(100_000, "Completed, Succeeded") stub.positions = []int{400} s, _ := newStubSlskd(t, stub) s.stallAfter = time.Hour started := time.Now() _, err := s.Grab(context.Background(), slskdAlbum(t, stub, "peer"), t.TempDir(), nil) if !errors.Is(err, ErrSlskdTimeout) || !strings.Contains(err.Error(), "position 400") { t.Fatalf("Grab = %v, want a queue-position give-up", err) } if time.Since(started) > 5*time.Second { t.Error("the give-up waited on something other than the position") } if len(stub.cancelledURIs()) == 0 { t.Error("the queued transfers were not cancelled") } } // One far reading is not enough: slskd says the figure can be wildly // wrong. func TestSlskdOneBadPositionIsNotEnough(t *testing.T) { t.Parallel() stub := newSlskdStub(t) stub.transfers = queuedThen(20, "Completed, Succeeded") stub.positions = []int{400, 3} s, _ := newStubSlskd(t, stub) s.positionPoll = 0 if _, err := s.Grab( context.Background(), slskdAlbum(t, stub, "peer", "01 A.flac", "02 B.flac"), t.TempDir(), nil, ); err != nil { t.Fatalf("Grab: %v", err) } } // A queue that is moving is progress: the grab outlives the stall timer // while its position improves. func TestSlskdAMovingQueueIsProgress(t *testing.T) { t.Parallel() stub := newSlskdStub(t) // Near enough to wait for, with more improving readings (45) than // there are queued polls (30), so the queue outlives the stall timer // (30 polls of at least 2 ms against 40 ms) while still improving, // however slowly the machine runs the loop. stub.transfers = queuedThen(30, "Completed, Succeeded") stub.positions = descending(slskdMaxQueuePosition - 5) s, _ := newStubSlskd(t, stub) s.transferPoll = 2 * time.Millisecond s.positionPoll = 0 s.stallAfter = 40 * time.Millisecond if _, err := s.Grab( context.Background(), slskdAlbum(t, stub, "peer", "01 A.flac", "02 B.flac"), t.TempDir(), nil, ); err != nil { t.Fatalf("Grab: %v; a moving queue was treated as a stall", err) } } // However steadily the queue moves, waiting in it has a ceiling. func TestSlskdQueueHasACeiling(t *testing.T) { t.Parallel() stub := newSlskdStub(t) stub.transfers = queuedThen(100_000, "Completed, Succeeded") stub.positions = descending(100_000) s, _ := newStubSlskd(t, stub) s.stallAfter = time.Hour s.queueCeiling = 50 * time.Millisecond _, err := s.Grab(context.Background(), slskdAlbum(t, stub, "peer"), t.TempDir(), nil) if !errors.Is(err, ErrSlskdTimeout) { t.Fatalf("Grab = %v, want the queue ceiling", err) } }