Source file src/compress/lzw/writer_test.go

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package lzw
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"io"
    12  	"math"
    13  	"os"
    14  	"runtime"
    15  	"testing"
    16  )
    17  
    18  var filenames = []string{
    19  	"../testdata/gettysburg.txt",
    20  	"../testdata/e.txt",
    21  	"../testdata/pi.txt",
    22  }
    23  
    24  // testFile tests that compressing and then decompressing the given file with
    25  // the given options yields equivalent bytes to the original file.
    26  func testFile(t *testing.T, fn string, order Order, litWidth int) {
    27  	// Read the file, as golden output.
    28  	golden, err := os.Open(fn)
    29  	if err != nil {
    30  		t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err)
    31  		return
    32  	}
    33  	defer golden.Close()
    34  
    35  	// Read the file again, and push it through a pipe that compresses at the write end, and decompresses at the read end.
    36  	raw, err := os.Open(fn)
    37  	if err != nil {
    38  		t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err)
    39  		return
    40  	}
    41  
    42  	piper, pipew := io.Pipe()
    43  	defer piper.Close()
    44  	go func() {
    45  		defer raw.Close()
    46  		defer pipew.Close()
    47  		lzww := NewWriter(pipew, order, litWidth)
    48  		defer lzww.Close()
    49  		var b [4096]byte
    50  		for {
    51  			n, err0 := raw.Read(b[:])
    52  			if err0 != nil && err0 != io.EOF {
    53  				t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err0)
    54  				return
    55  			}
    56  			_, err1 := lzww.Write(b[:n])
    57  			if err1 != nil {
    58  				t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1)
    59  				return
    60  			}
    61  			if err0 == io.EOF {
    62  				break
    63  			}
    64  		}
    65  	}()
    66  	lzwr := NewReader(piper, order, litWidth)
    67  	defer lzwr.Close()
    68  
    69  	// Compare the two.
    70  	b0, err0 := io.ReadAll(golden)
    71  	b1, err1 := io.ReadAll(lzwr)
    72  	if err0 != nil {
    73  		t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err0)
    74  		return
    75  	}
    76  	if err1 != nil {
    77  		t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1)
    78  		return
    79  	}
    80  	if len(b1) != len(b0) {
    81  		t.Errorf("%s (order=%d litWidth=%d): length mismatch %d != %d", fn, order, litWidth, len(b1), len(b0))
    82  		return
    83  	}
    84  	for i := 0; i < len(b0); i++ {
    85  		if b1[i] != b0[i] {
    86  			t.Errorf("%s (order=%d litWidth=%d): mismatch at %d, 0x%02x != 0x%02x\n", fn, order, litWidth, i, b1[i], b0[i])
    87  			return
    88  		}
    89  	}
    90  }
    91  
    92  func TestWriter(t *testing.T) {
    93  	for _, filename := range filenames {
    94  		for _, order := range [...]Order{LSB, MSB} {
    95  			// The test data "2.71828 etcetera" is ASCII text requiring at least 6 bits.
    96  			for litWidth := 6; litWidth <= 8; litWidth++ {
    97  				if filename == "../testdata/gettysburg.txt" && litWidth == 6 {
    98  					continue
    99  				}
   100  				testFile(t, filename, order, litWidth)
   101  			}
   102  		}
   103  		if testing.Short() && testenv.Builder() == "" {
   104  			break
   105  		}
   106  	}
   107  }
   108  
   109  func TestWriterReset(t *testing.T) {
   110  	for _, order := range [...]Order{LSB, MSB} {
   111  		t.Run(fmt.Sprintf("Order %d", order), func(t *testing.T) {
   112  			for litWidth := 6; litWidth <= 8; litWidth++ {
   113  				t.Run(fmt.Sprintf("LitWidth %d", litWidth), func(t *testing.T) {
   114  					var data []byte
   115  					if litWidth == 6 {
   116  						data = []byte{1, 2, 3}
   117  					} else {
   118  						data = []byte(`lorem ipsum dolor sit amet`)
   119  					}
   120  					var buf bytes.Buffer
   121  					w := NewWriter(&buf, order, litWidth)
   122  					if _, err := w.Write(data); err != nil {
   123  						t.Errorf("write: %v: %v", string(data), err)
   124  					}
   125  
   126  					if err := w.Close(); err != nil {
   127  						t.Errorf("close: %v", err)
   128  					}
   129  
   130  					b1 := buf.Bytes()
   131  					buf.Reset()
   132  
   133  					w.(*Writer).Reset(&buf, order, litWidth)
   134  
   135  					if _, err := w.Write(data); err != nil {
   136  						t.Errorf("write: %v: %v", string(data), err)
   137  					}
   138  
   139  					if err := w.Close(); err != nil {
   140  						t.Errorf("close: %v", err)
   141  					}
   142  					b2 := buf.Bytes()
   143  
   144  					if !bytes.Equal(b1, b2) {
   145  						t.Errorf("bytes written were not same")
   146  					}
   147  				})
   148  			}
   149  		})
   150  	}
   151  }
   152  
   153  func TestWriterReturnValues(t *testing.T) {
   154  	w := NewWriter(io.Discard, LSB, 8)
   155  	n, err := w.Write([]byte("asdf"))
   156  	if n != 4 || err != nil {
   157  		t.Errorf("got %d, %v, want 4, nil", n, err)
   158  	}
   159  }
   160  
   161  func TestSmallLitWidth(t *testing.T) {
   162  	w := NewWriter(io.Discard, LSB, 2)
   163  	if _, err := w.Write([]byte{0x03}); err != nil {
   164  		t.Fatalf("write a byte < 1<<2: %v", err)
   165  	}
   166  	if _, err := w.Write([]byte{0x04}); err == nil {
   167  		t.Fatal("write a byte >= 1<<2: got nil error, want non-nil")
   168  	}
   169  }
   170  
   171  func TestStartsWithClearCode(t *testing.T) {
   172  	// A literal width of 7 bits means that the code width starts at 8 bits,
   173  	// which makes it easier to visually inspect the output (provided that the
   174  	// output is short so codes don't get longer). Each byte is a code:
   175  	//  - ASCII bytes are literal codes,
   176  	//  - 0x80 is the clear code,
   177  	//  - 0x81 is the end code.
   178  	//  - 0x82 and above are copy codes (unused in this test case).
   179  	for _, empty := range []bool{false, true} {
   180  		var buf bytes.Buffer
   181  		w := NewWriter(&buf, LSB, 7)
   182  		if !empty {
   183  			w.Write([]byte("Hi"))
   184  		}
   185  		w.Close()
   186  		got := buf.String()
   187  
   188  		want := "\x80\x81"
   189  		if !empty {
   190  			want = "\x80Hi\x81"
   191  		}
   192  
   193  		if got != want {
   194  			t.Errorf("empty=%t: got %q, want %q", empty, got, want)
   195  		}
   196  	}
   197  }
   198  
   199  func BenchmarkEncoder(b *testing.B) {
   200  	buf, err := os.ReadFile("../testdata/e.txt")
   201  	if err != nil {
   202  		b.Fatal(err)
   203  	}
   204  	if len(buf) == 0 {
   205  		b.Fatalf("test file has no data")
   206  	}
   207  
   208  	for e := 4; e <= 6; e++ {
   209  		n := int(math.Pow10(e))
   210  		buf0 := buf
   211  		buf1 := make([]byte, n)
   212  		for i := 0; i < n; i += len(buf0) {
   213  			if len(buf0) > n-i {
   214  				buf0 = buf0[:n-i]
   215  			}
   216  			copy(buf1[i:], buf0)
   217  		}
   218  		buf0 = nil
   219  		runtime.GC()
   220  		b.Run(fmt.Sprint("1e", e), func(b *testing.B) {
   221  			b.SetBytes(int64(n))
   222  			for i := 0; i < b.N; i++ {
   223  				w := NewWriter(io.Discard, LSB, 8)
   224  				w.Write(buf1)
   225  				w.Close()
   226  			}
   227  		})
   228  		b.Run(fmt.Sprint("1e-Reuse", e), func(b *testing.B) {
   229  			b.SetBytes(int64(n))
   230  			w := NewWriter(io.Discard, LSB, 8)
   231  			for i := 0; i < b.N; i++ {
   232  				w.Write(buf1)
   233  				w.Close()
   234  				w.(*Writer).Reset(io.Discard, LSB, 8)
   235  			}
   236  		})
   237  	}
   238  }
   239  

View as plain text