...

Source file src/go.etcd.io/etcd/pkg/v3/ioutil/pagewriter_test.go

Documentation: go.etcd.io/etcd/pkg/v3/ioutil

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ioutil
    16  
    17  import (
    18  	"math/rand"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  func TestPageWriterRandom(t *testing.T) {
    25  	// smaller buffer for stress testing
    26  	defaultBufferBytes = 8 * 1024
    27  	pageBytes := 128
    28  	buf := make([]byte, 4*defaultBufferBytes)
    29  	cw := &checkPageWriter{pageBytes: pageBytes, t: t}
    30  	w := NewPageWriter(cw, pageBytes, 0)
    31  	n := 0
    32  	for i := 0; i < 4096; i++ {
    33  		c, err := w.Write(buf[:rand.Intn(len(buf))])
    34  		if err != nil {
    35  			t.Fatal(err)
    36  		}
    37  		n += c
    38  	}
    39  	if cw.writeBytes > n {
    40  		t.Fatalf("wrote %d bytes to io.Writer, but only wrote %d bytes", cw.writeBytes, n)
    41  	}
    42  	if maxPendingBytes := pageBytes + defaultBufferBytes; n-cw.writeBytes > maxPendingBytes {
    43  		t.Fatalf("got %d bytes pending, expected less than %d bytes", n-cw.writeBytes, maxPendingBytes)
    44  	}
    45  	t.Logf("total writes: %d", cw.writes)
    46  	t.Logf("total write bytes: %d (of %d)", cw.writeBytes, n)
    47  }
    48  
    49  // TestPageWriterPartialSlack tests the case where a write overflows the buffer
    50  // but there is not enough data to complete the slack write.
    51  func TestPageWriterPartialSlack(t *testing.T) {
    52  	defaultBufferBytes = 1024
    53  	pageBytes := 128
    54  	buf := make([]byte, defaultBufferBytes)
    55  	cw := &checkPageWriter{pageBytes: 64, t: t}
    56  	w := NewPageWriter(cw, pageBytes, 0)
    57  	// put writer in non-zero page offset
    58  	if _, err := w.Write(buf[:64]); err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	if err := w.Flush(); err != nil {
    62  		t.Fatal(err)
    63  	}
    64  	if cw.writes != 1 {
    65  		t.Fatalf("got %d writes, expected 1", cw.writes)
    66  	}
    67  	// nearly fill buffer
    68  	if _, err := w.Write(buf[:1022]); err != nil {
    69  		t.Fatal(err)
    70  	}
    71  	// overflow buffer, but without enough to write as aligned
    72  	if _, err := w.Write(buf[:8]); err != nil {
    73  		t.Fatal(err)
    74  	}
    75  	if cw.writes != 1 {
    76  		t.Fatalf("got %d writes, expected 1", cw.writes)
    77  	}
    78  	// finish writing slack space
    79  	if _, err := w.Write(buf[:128]); err != nil {
    80  		t.Fatal(err)
    81  	}
    82  	if cw.writes != 2 {
    83  		t.Fatalf("got %d writes, expected 2", cw.writes)
    84  	}
    85  }
    86  
    87  // TestPageWriterOffset tests if page writer correctly repositions when offset is given.
    88  func TestPageWriterOffset(t *testing.T) {
    89  	defaultBufferBytes = 1024
    90  	pageBytes := 128
    91  	buf := make([]byte, defaultBufferBytes)
    92  	cw := &checkPageWriter{pageBytes: 64, t: t}
    93  	w := NewPageWriter(cw, pageBytes, 0)
    94  	if _, err := w.Write(buf[:64]); err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	if err := w.Flush(); err != nil {
    98  		t.Fatal(err)
    99  	}
   100  	if w.pageOffset != 64 {
   101  		t.Fatalf("w.pageOffset expected 64, got %d", w.pageOffset)
   102  	}
   103  
   104  	w = NewPageWriter(cw, w.pageOffset, pageBytes)
   105  	if _, err := w.Write(buf[:64]); err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	if err := w.Flush(); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  	if w.pageOffset != 0 {
   112  		t.Fatalf("w.pageOffset expected 0, got %d", w.pageOffset)
   113  	}
   114  }
   115  
   116  func TestPageWriterPageBytes(t *testing.T) {
   117  	cases := []struct {
   118  		name        string
   119  		pageBytes   int
   120  		expectPanic bool
   121  	}{
   122  		{
   123  			name:        "normal page bytes",
   124  			pageBytes:   4096,
   125  			expectPanic: false,
   126  		},
   127  		{
   128  			name:        "negative page bytes",
   129  			pageBytes:   -1,
   130  			expectPanic: true,
   131  		},
   132  		{
   133  			name:        "zero page bytes",
   134  			pageBytes:   0,
   135  			expectPanic: true,
   136  		},
   137  	}
   138  
   139  	for _, tc := range cases {
   140  		t.Run(tc.name, func(t *testing.T) {
   141  			defaultBufferBytes = 1024
   142  			cw := &checkPageWriter{pageBytes: tc.pageBytes, t: t}
   143  			if tc.expectPanic {
   144  				assert.Panicsf(t, func() {
   145  					NewPageWriter(cw, tc.pageBytes, 0)
   146  				}, "expected panic when pageBytes is %d", tc.pageBytes)
   147  			} else {
   148  				pw := NewPageWriter(cw, tc.pageBytes, 0)
   149  				assert.NotEqual(t, pw, nil)
   150  			}
   151  		})
   152  	}
   153  }
   154  
   155  // checkPageWriter implements an io.Writer that fails a test on unaligned writes.
   156  type checkPageWriter struct {
   157  	pageBytes  int
   158  	writes     int
   159  	writeBytes int
   160  	t          *testing.T
   161  }
   162  
   163  func (cw *checkPageWriter) Write(p []byte) (int, error) {
   164  	if len(p)%cw.pageBytes != 0 {
   165  		cw.t.Fatalf("got write len(p) = %d, expected len(p) == k*cw.pageBytes", len(p))
   166  	}
   167  	cw.writes++
   168  	cw.writeBytes += len(p)
   169  	return len(p), nil
   170  }
   171  

View as plain text