mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
378 lines
9.1 KiB
Go
378 lines
9.1 KiB
Go
// Copyright (c) 2019 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gcs
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"io"
|
|
"testing"
|
|
)
|
|
|
|
// TestBitWriter ensures the bit writer and all associated methods work as
|
|
// expected including all corner cases at byte boundaries.
|
|
func TestBitWriter(t *testing.T) {
|
|
// perWriterTest describes a test to run against the same bitstream writer.
|
|
type perWriterTest struct {
|
|
name string // test description
|
|
bits uint64 // bits to write one at a time
|
|
nBits uint // number of individual bits to write
|
|
val uint64 // uint64 value from which to write nValBits
|
|
nValBits uint // number of bits of the val to write
|
|
wantBytes string // expected bytes in writer as of this test
|
|
}
|
|
|
|
tests := []struct {
|
|
name string // test description
|
|
perWriterTests []perWriterTest // tests to run against same writer
|
|
}{{
|
|
name: "1 bit - 0",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "1st bit",
|
|
bits: 0x0,
|
|
nBits: 1,
|
|
wantBytes: "00",
|
|
}},
|
|
}, {
|
|
name: "1 bit - 1",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "1st bit",
|
|
bits: 0x1,
|
|
nBits: 1,
|
|
wantBytes: "80",
|
|
}},
|
|
}, {
|
|
name: "2 bits - 00",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "1st and 2nd bits",
|
|
bits: 0x0,
|
|
nBits: 2,
|
|
wantBytes: "00",
|
|
}},
|
|
}, {
|
|
name: "2 bits - 01",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "1st and 2nd bits",
|
|
bits: 0x1,
|
|
nBits: 2,
|
|
wantBytes: "40",
|
|
}},
|
|
}, {
|
|
name: "9 bits - 010101010",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "first 9 bits",
|
|
bits: 0x0aa,
|
|
nBits: 9,
|
|
wantBytes: "5500",
|
|
}},
|
|
}, {
|
|
name: "9 bits - 010101011",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "first 9 bits",
|
|
bits: 0x0ab,
|
|
nBits: 9,
|
|
wantBytes: "5580",
|
|
}},
|
|
}, {
|
|
name: "2 bits, 3 bits of uint64 - 01 110",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "first 2 bits",
|
|
bits: 0x1,
|
|
nBits: 2,
|
|
wantBytes: "40",
|
|
}, {
|
|
name: "3 bits of uint64",
|
|
val: 6,
|
|
nValBits: 3,
|
|
wantBytes: "70",
|
|
}},
|
|
}, {
|
|
name: "7 bits, 2 bits of uint64 - 1111111 00",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "first 7 bits",
|
|
bits: 0x7f,
|
|
nBits: 7,
|
|
wantBytes: "fe",
|
|
}, {
|
|
name: "2 bits of uint64",
|
|
val: 0,
|
|
nValBits: 2,
|
|
wantBytes: "fe00",
|
|
}},
|
|
}, {
|
|
name: "7 bits, 11 bits of uint64 - 1111111 00100000101",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "first 7 bits",
|
|
bits: 0x7f,
|
|
nBits: 7,
|
|
wantBytes: "fe",
|
|
}, {
|
|
name: "11 bits of uint64",
|
|
val: 261,
|
|
nValBits: 11,
|
|
wantBytes: "fe4140",
|
|
}},
|
|
}, {
|
|
name: "7 bits of uint64, 2 bits - 1010101 11",
|
|
perWriterTests: []perWriterTest{{
|
|
name: "7 bits of uint64",
|
|
val: 85,
|
|
nValBits: 7,
|
|
wantBytes: "aa",
|
|
}, {
|
|
name: "last 2 bits",
|
|
bits: 0x3,
|
|
nBits: 2,
|
|
wantBytes: "ab80",
|
|
}},
|
|
}}
|
|
|
|
nextTest:
|
|
for _, test := range tests {
|
|
var w bitWriter
|
|
for _, pwTest := range test.perWriterTests {
|
|
// Start by writing the specified individual bits one a time.
|
|
bitSource := pwTest.bits
|
|
for i := uint(0); i < pwTest.nBits; i++ {
|
|
switch (bitSource >> (pwTest.nBits - 1 - i)) & 0x1 {
|
|
case 0:
|
|
w.writeZero()
|
|
case 1:
|
|
w.writeOne()
|
|
}
|
|
}
|
|
|
|
// Write the specified number of bits from the uint64.
|
|
w.writeNBits(pwTest.val, pwTest.nValBits)
|
|
|
|
// Parse the expected serialized bytes and ensure they match.
|
|
wantBytes, err := hex.DecodeString(pwTest.wantBytes)
|
|
if err != nil {
|
|
t.Errorf("%q-%q: unexpected err parsing want bytes hex: %v",
|
|
test.name, pwTest.name, err)
|
|
continue nextTest
|
|
}
|
|
resultBytes := w.bytes
|
|
if !bytes.Equal(resultBytes, wantBytes) {
|
|
t.Errorf("%q-%q: mismatched bytes -- got %x, want %x",
|
|
test.name, pwTest.name, resultBytes, wantBytes)
|
|
continue nextTest
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestBitReader ensures the bit reader and all associated methods work as
|
|
// expected including expected errors and corner cases at byte boundaries.
|
|
func TestBitReader(t *testing.T) {
|
|
// perReaderTest describes a test to run against the same bitstream reader.
|
|
type perReaderTest struct {
|
|
name string // test description
|
|
doUnary bool // whether or not to perform a unary read
|
|
wantUnary uint64 // expected number of consecutive ones
|
|
unaryErr error // expected error on unary read
|
|
nValBits uint // number of bits to read from bitstream as uint64
|
|
wantVal uint64 // expected value from nValBits read
|
|
bitsErr error // expected error on bits read
|
|
}
|
|
|
|
tests := []struct {
|
|
name string // test description
|
|
bytes string // bytes to use as the bitstream
|
|
perReaderTests []perReaderTest // tests to run against same reader
|
|
}{{
|
|
name: "unary read on empty bytes error",
|
|
bytes: "",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "unary read",
|
|
doUnary: true,
|
|
wantUnary: 0,
|
|
unaryErr: io.EOF,
|
|
}},
|
|
}, {
|
|
name: "0 bits read on empty bytes (no error)",
|
|
bytes: "",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "0 bit read",
|
|
nValBits: 0,
|
|
wantVal: 0,
|
|
}},
|
|
}, {
|
|
name: "1 bit read on empty bytes error",
|
|
bytes: "",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "1 bit read",
|
|
nValBits: 1,
|
|
bitsErr: io.EOF,
|
|
}},
|
|
}, {
|
|
name: "9 bit read on single byte error (straddle byte boundary)",
|
|
bytes: "0f",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "9 bit read",
|
|
nValBits: 9,
|
|
bitsErr: io.EOF,
|
|
}},
|
|
}, {
|
|
name: "16 bit read on single byte error (byte boundary)",
|
|
bytes: "0f",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "16 bit read",
|
|
nValBits: 16,
|
|
bitsErr: io.EOF,
|
|
}},
|
|
}, {
|
|
name: "0 bits followed by 8 bits ",
|
|
bytes: "ff",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "0 bit read",
|
|
nValBits: 0,
|
|
wantVal: 0,
|
|
}, {
|
|
name: "8 bit read",
|
|
nValBits: 8,
|
|
wantVal: 0xff,
|
|
}},
|
|
}, {
|
|
name: "unary 1",
|
|
bytes: "80",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "first unary read",
|
|
doUnary: true,
|
|
wantUnary: 1,
|
|
}},
|
|
}, {
|
|
name: "unary 2",
|
|
bytes: "c0",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "first unary read",
|
|
doUnary: true,
|
|
wantUnary: 2,
|
|
}},
|
|
}, {
|
|
name: "unary 9 (more than one byte)",
|
|
bytes: "ff80",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "first unary read",
|
|
doUnary: true,
|
|
wantUnary: 9,
|
|
}},
|
|
}, {
|
|
name: "unary 0, 1 bit read",
|
|
bytes: "40",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "unary read",
|
|
doUnary: true,
|
|
wantUnary: 0,
|
|
}, {
|
|
name: "1 bit read",
|
|
nValBits: 1,
|
|
wantVal: 1,
|
|
}},
|
|
}, {
|
|
name: "unary 0, 8 bits read (straddle byte)",
|
|
bytes: "5a80",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "unary read",
|
|
doUnary: true,
|
|
wantUnary: 0,
|
|
}, {
|
|
name: "8 bit read",
|
|
nValBits: 8,
|
|
wantVal: 0xb5,
|
|
}},
|
|
}, {
|
|
name: "unary 0, 15 bits read (byte boundary)",
|
|
bytes: "5ac5",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "unary read",
|
|
doUnary: true,
|
|
wantUnary: 0,
|
|
}, {
|
|
name: "15 bit read",
|
|
nValBits: 15,
|
|
wantVal: 0x5ac5,
|
|
}},
|
|
}, {
|
|
name: "unary 0, 16 bits read (straddle 2nd byte boundary)",
|
|
bytes: "5ac580",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "unary read",
|
|
doUnary: true,
|
|
wantUnary: 0,
|
|
}, {
|
|
name: "16 bit read",
|
|
nValBits: 16,
|
|
wantVal: 0xb58b,
|
|
}},
|
|
}, {
|
|
name: "unary 3, 15 bits read, unary 2",
|
|
bytes: "eac518",
|
|
perReaderTests: []perReaderTest{{
|
|
name: "first unary read",
|
|
doUnary: true,
|
|
wantUnary: 3,
|
|
}, {
|
|
name: "15 bit read",
|
|
nValBits: 15,
|
|
wantVal: 0x5628,
|
|
}, {
|
|
name: "second unary read",
|
|
doUnary: true,
|
|
wantUnary: 2,
|
|
}},
|
|
}}
|
|
|
|
nextTest:
|
|
for _, test := range tests {
|
|
// Parse the specified bytes to read and create a bitstream reader from
|
|
// them.
|
|
testBytes, err := hex.DecodeString(test.bytes)
|
|
if err != nil {
|
|
t.Errorf("%q: unexpected err parsing bytes hex: %v", test.name, err)
|
|
continue
|
|
}
|
|
r := newBitReader(testBytes)
|
|
|
|
for _, prTest := range test.perReaderTests {
|
|
// Read unary and ensure expected result if requested.
|
|
if prTest.doUnary {
|
|
gotUnary, err := r.readUnary()
|
|
if err != prTest.unaryErr {
|
|
t.Errorf("%q-%q: unexpected unary err -- got %v, want %v",
|
|
test.name, prTest.name, err, prTest.unaryErr)
|
|
continue nextTest
|
|
}
|
|
if err != nil {
|
|
continue nextTest
|
|
}
|
|
|
|
if gotUnary != prTest.wantUnary {
|
|
t.Errorf("%q-%q: unexpected unary value -- got %x, want %x",
|
|
test.name, prTest.name, gotUnary, prTest.wantUnary)
|
|
continue nextTest
|
|
}
|
|
}
|
|
|
|
// Read specified number of bits as uint64 and ensure expected
|
|
// result.
|
|
gotVal, err := r.readNBits(prTest.nValBits)
|
|
if err != prTest.bitsErr {
|
|
t.Errorf("%q-%q: unexpected nbits err -- got %v, want %v",
|
|
test.name, prTest.name, err, prTest.bitsErr)
|
|
continue nextTest
|
|
}
|
|
if err != nil {
|
|
continue nextTest
|
|
}
|
|
if gotVal != prTest.wantVal {
|
|
t.Errorf("%q-%q: mismatched value -- got %x, want %x",
|
|
test.name, prTest.name, gotVal, prTest.wantVal)
|
|
continue nextTest
|
|
}
|
|
}
|
|
}
|
|
}
|