first commit
This commit is contained in:
188
httpdl/segment_test.go
Normal file
188
httpdl/segment_test.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package httpdl
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMakeSegments(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
total int64
|
||||
minSplit int64
|
||||
conns int
|
||||
wantN int
|
||||
}{
|
||||
{"even split", 1000, 100, 5, 5},
|
||||
{"min-split caps count", 1000, 400, 5, 2},
|
||||
{"tiny file is one segment", 50, 100, 5, 1},
|
||||
{"single connection", 1000, 1, 1, 1},
|
||||
{"min-split exact", 1000, 200, 5, 5},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
segs := makeSegments(tc.total, tc.minSplit, tc.conns)
|
||||
if len(segs) != tc.wantN {
|
||||
t.Fatalf("got %d segments, want %d", len(segs), tc.wantN)
|
||||
}
|
||||
// Segments must be contiguous and cover the whole file exactly.
|
||||
var next int64
|
||||
for i, s := range segs {
|
||||
if s.start != next {
|
||||
t.Errorf("segment %d starts at %d, want %d", i, s.start, next)
|
||||
}
|
||||
if s.end < s.start {
|
||||
t.Errorf("segment %d has end %d < start %d", i, s.end, s.start)
|
||||
}
|
||||
next = s.end + 1
|
||||
}
|
||||
if next != tc.total {
|
||||
t.Errorf("segments cover %d bytes, want %d", next, tc.total)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSegProgress(t *testing.T) {
|
||||
s := seg{start: 100, end: 199} // length 100
|
||||
if s.length() != 100 {
|
||||
t.Fatalf("length = %d", s.length())
|
||||
}
|
||||
if s.done() {
|
||||
t.Fatalf("new segment should not be done")
|
||||
}
|
||||
s.advance(60)
|
||||
if s.offset() != 160 {
|
||||
t.Errorf("offset = %d, want 160", s.offset())
|
||||
}
|
||||
if s.remaining() != 40 {
|
||||
t.Errorf("remaining = %d, want 40", s.remaining())
|
||||
}
|
||||
s.advance(40)
|
||||
if !s.done() {
|
||||
t.Errorf("segment should be done after writing full length")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitExt(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
stem, ext string
|
||||
}{
|
||||
{"foo.txt", "foo", ".txt"},
|
||||
{"foo", "foo", ""},
|
||||
{"/dir/foo.tar.gz", "/dir/foo.tar", ".gz"},
|
||||
{"/dir/.bashrc", "/dir/.bashrc", ""}, // dotfile, no extension
|
||||
{".bashrc", ".bashrc", ""},
|
||||
{"archive.", "archive", "."},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
stem, ext := splitExt(tc.in)
|
||||
if stem != tc.stem || ext != tc.ext {
|
||||
t.Errorf("splitExt(%q) = %q,%q; want %q,%q", tc.in, stem, ext, tc.stem, tc.ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUniqueName(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// foo.txt taken -> foo.1.txt (.N inserted before the extension, item [13]).
|
||||
taken := filepath.Join(dir, "foo.txt")
|
||||
if err := os.WriteFile(taken, nil, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := uniqueName(taken)
|
||||
if err != nil {
|
||||
t.Fatalf("uniqueName: %v", err)
|
||||
}
|
||||
if want := filepath.Join(dir, "foo.1.txt"); got != want {
|
||||
t.Errorf("uniqueName(%q) = %q, want %q", taken, got, want)
|
||||
}
|
||||
|
||||
// No-extension base -> base.1.
|
||||
plain := filepath.Join(dir, "data")
|
||||
if err := os.WriteFile(plain, nil, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err = uniqueName(plain)
|
||||
if err != nil {
|
||||
t.Fatalf("uniqueName: %v", err)
|
||||
}
|
||||
if want := filepath.Join(dir, "data.1"); got != want {
|
||||
t.Errorf("uniqueName(%q) = %q, want %q", plain, got, want)
|
||||
}
|
||||
|
||||
// foo.txt and foo.1.txt both taken -> foo.2.txt.
|
||||
if err := os.WriteFile(filepath.Join(dir, "foo.1.txt"), nil, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err = uniqueName(taken)
|
||||
if err != nil {
|
||||
t.Fatalf("uniqueName: %v", err)
|
||||
}
|
||||
if want := filepath.Join(dir, "foo.2.txt"); got != want {
|
||||
t.Errorf("uniqueName second pass = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
// A candidate that exists but has a .got control file is acceptable (it is a
|
||||
// resumable partial).
|
||||
ctrlBase := filepath.Join(dir, "r.bin")
|
||||
cand1 := filepath.Join(dir, "r.1.bin")
|
||||
if err := os.WriteFile(ctrlBase, nil, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(cand1, nil, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(controlPath(cand1), nil, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err = uniqueName(ctrlBase)
|
||||
if err != nil {
|
||||
t.Fatalf("uniqueName: %v", err)
|
||||
}
|
||||
if got != cand1 {
|
||||
t.Errorf("uniqueName with control sidecar = %q, want %q", got, cand1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameFromContentDisposition(t *testing.T) {
|
||||
tests := []struct {
|
||||
cd, want string
|
||||
}{
|
||||
{`attachment; filename="report.pdf"`, "report.pdf"},
|
||||
{`attachment; filename=report.pdf`, "report.pdf"},
|
||||
{`inline; filename*=UTF-8''na%C3%AFve.txt`, "naïve.txt"},
|
||||
{`attachment; filename="../../etc/passwd"`, "passwd"}, // path stripped
|
||||
{`attachment`, ""},
|
||||
{`garbage`, ""},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
if got := nameFromContentDisposition(tc.cd); got != tc.want {
|
||||
t.Errorf("nameFromContentDisposition(%q) = %q, want %q", tc.cd, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeedSegmentsFromPrefix(t *testing.T) {
|
||||
segs := makeSegments(1000, 100, 5) // five 200-byte segments
|
||||
seedSegmentsFromPrefix(segs, 450) // 2 full segments + 50 bytes of the third
|
||||
wants := []int64{200, 200, 50, 0, 0}
|
||||
for i := range segs {
|
||||
if segs[i].written != wants[i] {
|
||||
t.Errorf("segment %d written = %d, want %d", i, segs[i].written, wants[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseContentRange(t *testing.T) {
|
||||
start, end, total, ok := parseContentRange("bytes 0-0/12345")
|
||||
if !ok || start != 0 || end != 0 || total != 12345 {
|
||||
t.Errorf("parseContentRange = %d,%d,%d,%v", start, end, total, ok)
|
||||
}
|
||||
if _, _, _, ok := parseContentRange("garbage"); ok {
|
||||
t.Errorf("parseContentRange(garbage) ok = true, want false")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user