acme-dns/vendor/github.com/klauspost/compress/fse/fse_test.go
2018-08-10 16:51:32 +03:00

311 lines
9.3 KiB
Go

// Copyright 2018 Klaus Post. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Based on work Copyright (c) 2013, Yann Collet, released under BSD License.
package fse
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
type inputFn func() ([]byte, error)
var testfiles = []struct {
name string
fn inputFn
err error
}{
// gettysburg.txt is a small plain text.
{name: "gettysburg", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/gettysburg.txt") }},
// Digits is the digits of the irrational number e. Its decimal representation
// does not repeat, but there are only 10 possible digits, so it should be
// reasonably compressible.
{name: "digits", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/e.txt") }},
// Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
{name: "twain", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/Mark.Twain-Tom.Sawyer.txt") }},
// Random bytes
{name: "random", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/sharnd.out") }, err: ErrIncompressible},
// Low entropy
{name: "low-ent", fn: func() ([]byte, error) { return []byte(strings.Repeat("1221", 10000)), nil }},
// Super Low entropy
{name: "superlow-ent", fn: func() ([]byte, error) { return []byte(strings.Repeat("1", 10000) + strings.Repeat("2", 500)), nil }},
// Zero bytes
{name: "zeroes", fn: func() ([]byte, error) { return make([]byte, 10000), nil }, err: ErrUseRLE},
{name: "crash1", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/crash1.bin") }, err: ErrIncompressible},
{name: "crash2", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/crash2.bin") }, err: ErrIncompressible},
{name: "crash3", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/crash3.bin") }, err: ErrIncompressible},
{name: "endzerobits", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/endzerobits.bin") }, err: nil},
{name: "endnonzero", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/endnonzero.bin") }, err: ErrIncompressible},
{name: "case1", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/case1.bin") }, err: ErrIncompressible},
{name: "case2", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/case2.bin") }, err: ErrIncompressible},
{name: "case3", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/case3.bin") }, err: ErrIncompressible},
{name: "pngdata.001", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/pngdata.bin") }, err: nil},
{name: "normcount2", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/normcount2.bin") }, err: nil},
}
var decTestfiles = []struct {
name string
fn inputFn
err string
}{
// gettysburg.txt is a small plain text.
{name: "hang1", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/dec-hang1.bin") }, err: "corruption detected (bitCount 252 > 32)"},
{name: "hang2", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/dec-hang2.bin") }, err: "newState (0) == oldState (0) and no bits"},
{name: "hang3", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/dec-hang3.bin") }, err: "maxSymbolValue too small"},
{name: "symlen1", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/dec-symlen1.bin") }, err: "symbolLen (257) too big"},
{name: "crash4", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/crash4.bin") }, err: "symbolLen (1) too small"},
{name: "crash5", fn: func() ([]byte, error) { return ioutil.ReadFile("../testdata/crash5.bin") }, err: "symbolLen (1) too small"},
}
func TestCompress(t *testing.T) {
for _, test := range testfiles {
t.Run(test.name, func(t *testing.T) {
var s Scratch
buf0, err := test.fn()
if err != nil {
t.Fatal(err)
}
b, err := Compress(buf0, &s)
if err != test.err {
t.Errorf("want error %v (%T), got %v (%T)", test.err, test.err, err, err)
}
if b == nil {
t.Log(test.name + ": not compressible")
return
}
t.Logf("%s: %d -> %d bytes (%.2f:1)", test.name, len(buf0), len(b), float64(len(buf0))/float64(len(b)))
})
}
}
func ExampleCompress() {
// Read data
data, err := ioutil.ReadFile("../testdata/e.txt")
if err != nil {
panic(err)
}
// Create re-usable scratch buffer.
var s Scratch
b, err := Compress(data, &s)
if err != nil {
panic(err)
}
fmt.Printf("Compress: %d -> %d bytes (%.2f:1)\n", len(data), len(b), float64(len(data))/float64(len(b)))
// OUTPUT: Compress: 100003 -> 41564 bytes (2.41:1)
}
func TestDecompress(t *testing.T) {
for _, test := range decTestfiles {
t.Run(test.name, func(t *testing.T) {
var s Scratch
buf0, err := test.fn()
if err != nil {
t.Fatal(err)
}
b, err := Decompress(buf0, &s)
if fmt.Sprint(err) != test.err {
t.Errorf("want error %q, got %q (%T)", test.err, err, err)
return
}
if err != nil {
return
}
if len(b) == 0 {
t.Error(test.name + ": no output")
return
}
t.Logf("%s: %d -> %d bytes (1:%.2f)", test.name, len(buf0), len(b), float64(len(buf0))/float64(len(b)))
})
}
}
func ExampleDecompress() {
// Read data
data, err := ioutil.ReadFile("../testdata/e.txt")
if err != nil {
panic(err)
}
// Create re-usable scratch buffer.
var s Scratch
b, err := Compress(data, &s)
if err != nil {
panic(err)
}
// Since we use the output of compression, it cannot be used as output for decompression.
s.Out = make([]byte, 0, len(data))
d, err := Decompress(b, &s)
if err != nil {
panic(err)
}
fmt.Printf("Input matches: %t\n", bytes.Equal(d, data))
// OUTPUT: Input matches: true
}
func TestGenCorpus(t *testing.T) {
t.Skip("only for generating decompress corpus")
filepath.Walk("fuzz/compress/corpus", func(path string, info os.FileInfo, err error) error {
t.Run(path, func(t *testing.T) {
var s Scratch
buf0, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
b, err := Compress(buf0, &s)
if err != nil {
t.Skip("skipping")
return
}
t.Logf("%s: %d -> %d bytes (%.2f:1)", path, len(buf0), len(b), float64(len(buf0))/float64(len(b)))
dstP := strings.Replace(path, "compress", "decompress", 1)
ioutil.WriteFile(dstP, b, os.ModePerm)
})
return nil
})
}
func BenchmarkCompress(b *testing.B) {
for _, tt := range testfiles {
test := tt
b.Run(test.name, func(b *testing.B) {
var s Scratch
buf0, err := test.fn()
if err != nil {
b.Fatal(err)
}
_, err = Compress(buf0, &s)
if err != test.err {
b.Fatal("unexpected error:", err)
}
if err != nil {
b.Skip("skipping benchmark: ", err)
return
}
b.ResetTimer()
b.ReportAllocs()
b.SetBytes(int64(len(buf0)))
for i := 0; i < b.N; i++ {
_, _ = Compress(buf0, &s)
}
})
}
}
func TestReadNCount(t *testing.T) {
for i := range testfiles {
var s Scratch
test := testfiles[i]
t.Run(test.name, func(t *testing.T) {
name := test.name + ": "
buf0, err := testfiles[i].fn()
if err != nil {
t.Fatal(err)
}
b, err := Compress(buf0, &s)
if err != test.err {
t.Error(err)
return
}
if err != nil {
t.Skip(name + err.Error())
return
}
t.Logf("%s: %d -> %d bytes (%.2f:1)", test.name, len(buf0), len(b), float64(len(buf0))/float64(len(b)))
//t.Logf("%v", b)
var s2 Scratch
dc, err := Decompress(b, &s2)
if err != nil {
t.Fatal(err)
}
want := s.norm[:s.symbolLen]
got := s2.norm[:s2.symbolLen]
if !cmp.Equal(want, got) {
if s.actualTableLog != s2.actualTableLog {
t.Errorf(name+"norm table, want tablelog: %d, got %d", s.actualTableLog, s2.actualTableLog)
}
if s.symbolLen != s2.symbolLen {
t.Errorf(name+"norm table, want size: %d, got %d", s.symbolLen, s2.symbolLen)
}
t.Errorf(name+"norm table, got delta: \n%s", cmp.Diff(want, got))
return
}
for i, dec := range s2.decTable {
dd := dec.symbol
ee := s.ct.tableSymbol[i]
if dd != ee {
t.Errorf("table symbol mismatch. idx %d, enc: %v, dec:%v", i, ee, dd)
break
}
}
if dc != nil {
if len(buf0) != len(dc) {
t.Errorf(name+"decompressed, want size: %d, got %d", len(buf0), len(dc))
if len(buf0) > len(dc) {
buf0 = buf0[:len(dc)]
} else {
dc = dc[:len(buf0)]
}
if !cmp.Equal(buf0, dc) {
t.Errorf(name+"decompressed, got delta: (in) %v != (out) %v\n", buf0, dc)
}
return
}
if !cmp.Equal(buf0, dc) {
t.Errorf(name+"decompressed, got delta: \n%s", cmp.Diff(buf0, dc))
}
if !t.Failed() {
t.Log("... roundtrip ok!")
}
}
})
}
}
func BenchmarkDecompress(b *testing.B) {
for _, tt := range testfiles {
test := tt
b.Run(test.name, func(b *testing.B) {
var s, s2 Scratch
buf0, err := test.fn()
if err != nil {
b.Fatal(err)
}
out, err := Compress(buf0, &s)
if err != test.err {
b.Fatal(err)
}
if err != nil {
b.Skip(test.name + ": " + err.Error())
return
}
got, err := Decompress(out, &s2)
if err != nil {
b.Fatal(err)
}
if !bytes.Equal(buf0, got) {
b.Fatal("output mismatch")
}
b.ResetTimer()
b.ReportAllocs()
b.SetBytes(int64(len(buf0)))
for i := 0; i < b.N; i++ {
_, err = Decompress(out, &s2)
if err != nil {
b.Fatal(err)
}
}
})
}
}