package files import ( "bytes" "io" "testing" ) // Der tailWriter ist die Stelle, an der ein beschädigter Download noch // aufzuhalten ist. Was er falsch macht, merkt man sonst erst an einer Datei, // die heil aussieht und keine ist. func TestTailWriterHaeltDenSchlussZurueck(t *testing.T) { var raus bytes.Buffer tw := &tailWriter{w: &raus, keep: 4} if _, err := tw.Write([]byte("0123456789")); err != nil { t.Fatal(err) } if got := raus.String(); got != "012345" { t.Fatalf("durchgereicht %q, erwartet %q", got, "012345") } if err := tw.Flush(); err != nil { t.Fatal(err) } if got := raus.String(); got != "0123456789" { t.Fatalf("nach Flush %q", got) } } // Der Fall, auf den es ankommt: Ohne Flush darf der Empfänger die Datei // **nicht** vollständig haben. func TestOhneFlushBleibtEsUnvollstaendig(t *testing.T) { var raus bytes.Buffer tw := &tailWriter{w: &raus, keep: 4} io.Copy(tw, bytes.NewReader([]byte("0123456789"))) if raus.Len() != 6 { t.Fatalf("%d Bytes draußen, erwartet 6", raus.Len()) } } // Eine Datei, die kleiner ist als der zurückgehaltene Schluss, darf ohne // Flush gar nichts hinauslassen. func TestKleineDateiBleibtGanzZurueck(t *testing.T) { var raus bytes.Buffer tw := &tailWriter{w: &raus, keep: 32} tw.Write([]byte("kurz")) if raus.Len() != 0 { t.Fatalf("%d Bytes zu früh draußen", raus.Len()) } tw.Flush() if raus.String() != "kurz" { t.Fatalf("nach Flush %q", raus.String()) } } // Viele kleine Schreibvorgänge dürfen sich nicht anders verhalten als einer // großer – io.Copy zerlegt in Häppchen. func TestHaeppchenweiseGleichesErgebnis(t *testing.T) { var raus bytes.Buffer tw := &tailWriter{w: &raus, keep: 5} for _, c := range []byte("abcdefghijklmnop") { if _, err := tw.Write([]byte{c}); err != nil { t.Fatal(err) } } if raus.String() != "abcdefghijk" { t.Fatalf("durchgereicht %q", raus.String()) } tw.Flush() if raus.String() != "abcdefghijklmnop" { t.Fatalf("nach Flush %q", raus.String()) } } // Der Zwischenspeicher darf nicht mitwachsen: Bei einer 5-GB-Datei läge sonst // alles im Arbeitsspeicher. func TestZwischenspeicherWaechstNicht(t *testing.T) { tw := &tailWriter{w: io.Discard, keep: 1024} block := make([]byte, 32*1024) for range 100 { tw.Write(block) if len(tw.buf) > len(block)+tw.keep { t.Fatalf("Zwischenspeicher auf %d Bytes gewachsen", len(tw.buf)) } } } // Bricht die Gegenstelle weg, muss der Fehler nach oben durchkommen – sonst // liefe io.Copy weiter und läse die ganze Datei ins Nichts. type kaputterWriter struct{} func (kaputterWriter) Write([]byte) (int, error) { return 0, io.ErrClosedPipe } func TestSchreibfehlerKommtDurch(t *testing.T) { tw := &tailWriter{w: kaputterWriter{}, keep: 2} if _, err := tw.Write([]byte("abcdef")); err == nil { t.Fatal("Schreibfehler wurde verschluckt") } }