// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package block import ( "bytes" "fmt" "io" "testing" "testing/iotest" ) // Simple "pseudo-random" stream for testing. type incStream struct { buf []byte n byte } func newIncStream(blockSize int) *incStream { x := new(incStream) x.buf = make([]byte, blockSize) return x } func (x *incStream) Next() []byte { x.n++ for i := range x.buf { x.buf[i] = x.n x.n++ } return x.buf } func testXorWriter(t *testing.T, maxio int) { var plain, crypt [256]byte for i := 0; i < len(plain); i++ { plain[i] = byte(i) } b := new(bytes.Buffer) for block := 1; block <= 64 && block <= maxio; block *= 2 { // compute encrypted version n := byte(0) for i := 0; i < len(crypt); i++ { if i%block == 0 { n++ } crypt[i] = plain[i] ^ n n++ } for frag := 0; frag < 2; frag++ { test := fmt.Sprintf("block=%d frag=%d maxio=%d", block, frag, maxio) b.Reset() r := bytes.NewBuffer(plain[0:]) s := newIncStream(block) w := newXorWriter(s, b) // copy plain into w in increasingly large chunks: 1, 1, 2, 4, 8, ... // if frag != 0, move the 1 to the end to cause fragmentation. if frag == 0 { _, err := io.Copyn(w, r, 1) if err != nil { t.Errorf("%s: first Copyn: %s", test, err) continue } } for n := 1; n <= len(plain)/2; n *= 2 { _, err := io.Copyn(w, r, int64(n)) if err != nil { t.Errorf("%s: Copyn %d: %s", test, n, err) } } // check output crypt := crypt[0 : len(crypt)-frag] data := b.Bytes() if len(data) != len(crypt) { t.Errorf("%s: want %d bytes, got %d", test, len(crypt), len(data)) continue } if string(data) != string(crypt) { t.Errorf("%s: want %x got %x", test, data, crypt) } } } } func TestXorWriter(t *testing.T) { // Do shorter I/O sizes first; they're easier to debug. for n := 1; n <= 256 && !t.Failed(); n *= 2 { testXorWriter(t, n) } } func testXorReader(t *testing.T, maxio int) { var readers = []func(io.Reader) io.Reader{ func(r io.Reader) io.Reader { return r }, iotest.OneByteReader, iotest.HalfReader, } var plain, crypt [256]byte for i := 0; i < len(plain); i++ { plain[i] = byte(255 - i) } b := new(bytes.Buffer) for block := 1; block <= 64 && block <= maxio; block *= 2 { // compute encrypted version n := byte(0) for i := 0; i < len(crypt); i++ { if i%block == 0 { n++ } crypt[i] = plain[i] ^ n n++ } for mode := 0; mode < len(readers); mode++ { for frag := 0; frag < 2; frag++ { test := fmt.Sprintf("block=%d mode=%d frag=%d maxio=%d", block, mode, frag, maxio) s := newIncStream(block) b.Reset() r := newXorReader(s, readers[mode](bytes.NewBuffer(crypt[0:maxio]))) // read from crypt in increasingly large chunks: 1, 1, 2, 4, 8, ... // if frag == 1, move the 1 to the end to cause fragmentation. if frag == 0 { _, err := io.Copyn(b, r, 1) if err != nil { t.Errorf("%s: first Copyn: %s", test, err) continue } } for n := 1; n <= maxio/2; n *= 2 { _, err := io.Copyn(b, r, int64(n)) if err != nil { t.Errorf("%s: Copyn %d: %s", test, n, err) } } // check output data := b.Bytes() crypt := crypt[0 : maxio-frag] plain := plain[0 : maxio-frag] if len(data) != len(plain) { t.Errorf("%s: want %d bytes, got %d", test, len(plain), len(data)) continue } if string(data) != string(plain) { t.Errorf("%s: input=%x want %x got %x", test, crypt, plain, data) } } } } } func TestXorReader(t *testing.T) { // Do shorter I/O sizes first; they're easier to debug. for n := 1; n <= 256 && !t.Failed(); n *= 2 { testXorReader(t, n) } } // TODO(rsc): Test handling of writes after write errors.