ronantakizawa/codereview-bench huggingface dataset:
Repository: uber-go/zap
File: zapcore/clock_test.go (Go)

Code before:
	"github.com/benbjohnson/clock"
	"github.com/stretchr/testify/assert"
)

type controlledClock struct {
	mockClock *clock.Mock
}

func (c *controlledClock) Now() time.Time {
	return c.mockClock.Now()
}

func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.mockClock.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n int32
	ctrlMock := &controlledClock{mockClock: clock.NewMock()}

	// Create a channel to increment every microsecond.
	go func() {
		ticker := ctrlMock.NewTicker(1 * time.Microsecond)
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	time.Sleep(1 * time.Microsecond)

	// Move clock forward.
	ctrlMock.mockClock.Add(10 * time.Microsecond)
	assert.Equal(t, atomic.LoadInt32(&n), int32(10))
}


Review comment (suggestion):
This is a goroutine leak. If we use go.uber.org/goleak here (which we do in #782).
We need to create a second `quit` channel that this `select`s on.

```
for {
  select {
  case <-quit:
    ticker.Stop()
    return
    
  case <-ticker.C:
    // ...
  }
}
```

This might also have to use a second channel to indicate that the goroutine has finished to wait for it to exit.

```
done := make(chan struct{})
go func() {
    defer close(done)
    // ...
}()
defer func() { <-done }() // wait for end
```


Code after:
	"github.com/stretchr/testify/assert"
	"go.uber.org/atomic"
)

type controlledClock struct{ *clock.Mock }

func newControlledClock() *controlledClock {
	return &controlledClock{clock.NewMock()}
}
func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n atomic.Int32
	ctrlMock := newControlledClock()

	done := make(chan struct{})
	defer func() { <-done }() // wait for end

	quit := make(chan struct{})
	// Create a channel to increment every microsecond.
	go func(ticker *time.Ticker) {
		defer close(done)
		for {
			select {
			case <-quit:
				ticker.Stop()
				return
			case <-ticker.C:
				n.Inc()
			}
		}
	}(ctrlMock.NewTicker(time.Microsecond))

	// Move clock forward.
	ctrlMock.Add(2 * time.Microsecond)
	assert.Equal(t, int32(2), n.Load())
	close(quit)
}
Repository: uber-go/zap
File: zapcore/clock_test.go (Go)

Code before:

type controlledClock struct {
	mockClock *clock.Mock
}

func (c *controlledClock) Now() time.Time {
	return c.mockClock.Now()
}

func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.mockClock.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n int32
	ctrlMock := &controlledClock{mockClock: clock.NewMock()}

	// Create a channel to increment every microsecond.
	go func() {
		ticker := ctrlMock.NewTicker(1 * time.Microsecond)
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	time.Sleep(1 * time.Microsecond)

	// Move clock forward.
	ctrlMock.mockClock.Add(10 * time.Microsecond)
	assert.Equal(t, atomic.LoadInt32(&n), int32(10))
}


Review comment (suggestion):
This shouldn't be necessary. My guess is that it's to wait for the ticker to start up.
We can probably get rid of this by starting the ticker right above the `go func()`, or as an argument to `go func`.

```
go func(ticker *time.Ticker) {
  // ...
}(ctrlMock.NewTicker(..))
```

Code after:

type controlledClock struct{ *clock.Mock }

func newControlledClock() *controlledClock {
	return &controlledClock{clock.NewMock()}
}
func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n atomic.Int32
	ctrlMock := newControlledClock()

	done := make(chan struct{})
	defer func() { <-done }() // wait for end

	quit := make(chan struct{})
	// Create a channel to increment every microsecond.
	go func(ticker *time.Ticker) {
		defer close(done)
		for {
			select {
			case <-quit:
				ticker.Stop()
				return
			case <-ticker.C:
				n.Inc()
			}
		}
	}(ctrlMock.NewTicker(time.Microsecond))

	// Move clock forward.
	ctrlMock.Add(2 * time.Microsecond)
	assert.Equal(t, int32(2), n.Load())
	close(quit)
}
Repository: uber-go/zap
File: zapcore/clock_test.go (Go)

Code before:
	"testing"
	"time"

	"github.com/benbjohnson/clock"
	"github.com/stretchr/testify/assert"
)

type controlledClock struct {
	mockClock *clock.Mock
}

func (c *controlledClock) Now() time.Time {
	return c.mockClock.Now()
}

func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.mockClock.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n int32
	ctrlMock := &controlledClock{mockClock: clock.NewMock()}

	// Create a channel to increment every microsecond.
	go func() {
		ticker := ctrlMock.NewTicker(1 * time.Microsecond)
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	time.Sleep(1 * time.Microsecond)

	// Move clock forward.
	ctrlMock.mockClock.Add(10 * time.Microsecond)
	assert.Equal(t, atomic.LoadInt32(&n), int32(10))
}


Review comment (suggestion):
code style: can be just `time.Microsecond` instead of `1 * time.Microsecond`.

Code after:
	"time"

	"github.com/benbjohnson/clock"
	"github.com/stretchr/testify/assert"
	"go.uber.org/atomic"
)

type controlledClock struct{ *clock.Mock }

func newControlledClock() *controlledClock {
	return &controlledClock{clock.NewMock()}
}
func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n atomic.Int32
	ctrlMock := newControlledClock()

	done := make(chan struct{})
	defer func() { <-done }() // wait for end

	quit := make(chan struct{})
	// Create a channel to increment every microsecond.
	go func(ticker *time.Ticker) {
		defer close(done)
		for {
			select {
			case <-quit:
				ticker.Stop()
				return
			case <-ticker.C:
				n.Inc()
			}
		}
	}(ctrlMock.NewTicker(time.Microsecond))

	// Move clock forward.
	ctrlMock.Add(2 * time.Microsecond)
	assert.Equal(t, int32(2), n.Load())
	close(quit)
}
Repository: uber-go/zap
File: zapcore/clock_test.go (Go)

Code before:

package zapcore

import (
	"sync/atomic"
	"testing"
	"time"

	"github.com/benbjohnson/clock"
	"github.com/stretchr/testify/assert"
)

type controlledClock struct {
	mockClock *clock.Mock
}

func (c *controlledClock) Now() time.Time {
	return c.mockClock.Now()
}

func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.mockClock.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n int32
	ctrlMock := &controlledClock{mockClock: clock.NewMock()}

	// Create a channel to increment every microsecond.
	go func() {
		ticker := ctrlMock.NewTicker(1 * time.Microsecond)
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	time.Sleep(1 * time.Microsecond)

	// Move clock forward.
	ctrlMock.mockClock.Add(10 * time.Microsecond)
	assert.Equal(t, atomic.LoadInt32(&n), int32(10))
}


Review comment (suggestion):
Instead of using `sync/atomic`, we should use [go.uber.org/atomic](https://pkg.go.dev/go.uber.org/atomic#Int32) here.


```
var n atomic.Int32

// ...

<-ticker.C
n.Inc()

// ...

assert.Equal(t, 42, n.Load())
```

See also https://github.com/uber-go/guide/blob/master/style.md#use-gouberorgatomic

Code after:

package zapcore

import (
	"testing"
	"time"

	"github.com/benbjohnson/clock"
	"github.com/stretchr/testify/assert"
	"go.uber.org/atomic"
)

type controlledClock struct{ *clock.Mock }

func newControlledClock() *controlledClock {
	return &controlledClock{clock.NewMock()}
}
func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n atomic.Int32
	ctrlMock := newControlledClock()

	done := make(chan struct{})
	defer func() { <-done }() // wait for end

	quit := make(chan struct{})
	// Create a channel to increment every microsecond.
	go func(ticker *time.Ticker) {
		defer close(done)
		for {
			select {
			case <-quit:
				ticker.Stop()
				return
			case <-ticker.C:
				n.Inc()
			}
		}
	}(ctrlMock.NewTicker(time.Microsecond))

	// Move clock forward.
	ctrlMock.Add(2 * time.Microsecond)
	assert.Equal(t, int32(2), n.Load())
	close(quit)
}
Repository: uber-go/zap
File: zapcore/clock_test.go (Go)

Code before:
}

func (c *controlledClock) Now() time.Time {
	return c.mockClock.Now()
}

func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.mockClock.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n int32
	ctrlMock := &controlledClock{mockClock: clock.NewMock()}

	// Create a channel to increment every microsecond.
	go func() {
		ticker := ctrlMock.NewTicker(1 * time.Microsecond)
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	time.Sleep(1 * time.Microsecond)

	// Move clock forward.
	ctrlMock.mockClock.Add(10 * time.Microsecond)
	assert.Equal(t, atomic.LoadInt32(&n), int32(10))
}


Review comment (suggestion):
If we embed clock.MockClock inside ctrlMock as suggested above, this `.mockClock` can be dropped.

Code after:
func newControlledClock() *controlledClock {
	return &controlledClock{clock.NewMock()}
}
func (c *controlledClock) NewTicker(d time.Duration) *time.Ticker {
	return &time.Ticker{C: c.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	var n atomic.Int32
	ctrlMock := newControlledClock()

	done := make(chan struct{})
	defer func() { <-done }() // wait for end

	quit := make(chan struct{})
	// Create a channel to increment every microsecond.
	go func(ticker *time.Ticker) {
		defer close(done)
		for {
			select {
			case <-quit:
				ticker.Stop()
				return
			case <-ticker.C:
				n.Inc()
			}
		}
	}(ctrlMock.NewTicker(time.Microsecond))

	// Move clock forward.
	ctrlMock.Add(2 * time.Microsecond)
	assert.Equal(t, int32(2), n.Load())
	close(quit)
}
Repository: uber-go/zap
File: zapcore/clock_test.go (Go)

Code before:
	return &time.Ticker{C: c.Ticker(d).C}
}

func TestMockClock(t *testing.T) {
	defer goleak.VerifyNone(t)
	var n atomic.Int32
	ctrlMock := newControlledClock()

	quit := make(chan bool)
	// Create a channel to increment every microsecond.
	go func(ticker *time.Ticker) {
		for {
			select {
			case <-quit:
				ticker.Stop()
				return
			case <-ticker.C:
				n.Inc()
			}
		}
	}(ctrlMock.NewTicker(time.Microsecond))

	// Move clock forward.
	ctrlMock.Add(2 * time.Microsecond)
	assert.Equal(t, int32(2), n.Load())
	quit <- true
}


Review comment (suggestion):
For channels that'll only ever fire once, `close` the channel instead of posting to it. (See also [this](https://abhinavg.net/posts/fire-once-antipattern/).)

Code after:
}

func TestMockClock(t *testing.T) {
	var n atomic.Int32
	ctrlMock := newControlledClock()

	done := make(chan struct{})
	defer func() { <-done }() // wait for end

	quit := make(chan struct{})
	// Create a channel to increment every microsecond.
	go func(ticker *time.Ticker) {
		defer close(done)
		for {
			select {
			case <-quit:
				ticker.Stop()
				return
			case <-ticker.C:
				n.Inc()
			}
		}
	}(ctrlMock.NewTicker(time.Microsecond))

	// Move clock forward.
	ctrlMock.Add(2 * time.Microsecond)
	assert.Equal(t, int32(2), n.Load())
	close(quit)
}
Repository: uber-go/zap
File: zapcore/clock.go (Go)

Code before:
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"time"
)

// Clock is a source of time for logged entries.
type Clock interface {
	// Now returns the current local time.
	Now() time.Time
	// NewTicker returns *time.Ticker that holds a channel
	// that delivers ``ticks'' of a clock.
	NewTicker(time.Duration) *time.Ticker
}


Review comment (bug):
super nit: 
```suggestion
	// that delivers "ticks" of a clock.
```
Not sure what kind of quotes you wanted here, but the current ticks don't look correct. Ignore me if I'm wrong. 

Code after:
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"time"
)

// Clock is a source of time for logged entries.
type Clock interface {
	// Now returns the current local time.
	Now() time.Time
	// NewTicker returns *time.Ticker that holds a channel
	// that delivers "ticks" of a clock.
	NewTicker(time.Duration) *time.Ticker
}
Repository: uber-go/zap
File: zapcore/buffered_write_syncer.go (Go)

Code before:

const (
	// _defaultBufferSize specifies the default size used by Buffer.
	_defaultBufferSize = 256 * 1024 // 256 kB

	// _defaultFlushInterval specifies the default flush interval for
	// Buffer.
	_defaultFlushInterval = 30 * time.Second
)

func (s *BufferedWriteSyncer) loadConfig() {
	size := s.Size
	if size == 0 {
		size = _defaultBufferSize
	}

	flushInterval := s.FlushInterval
	if flushInterval == 0 {
		flushInterval = _defaultFlushInterval
	}

	if s.Clock != nil {
		s.ticker = s.Clock.NewTicker(flushInterval)
	} else {
		s.ticker = DefaultClock.NewTicker(flushInterval)
	}

	s.writer = bufio.NewWriterSize(s.WriteSyncer, size)
	s.stop = make(chan struct{})
	s.initialized = true
	go s.flushLoop()
}

// Write writes log data into buffer syncer directly, multiple Write calls will be batched,
// and log data will be flushed to disk when the buffer is full or periodically.
func (s *BufferedWriteSyncer) Write(bs []byte) (int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if !s.initialized {
		s.loadConfig()
	}

	// To avoid partial writes from being flushed, we manually flush the existing buffer if:
	// * The current write doesn't fit into the buffer fully, and
	// * The buffer is not empty (since bufio will not split large writes when the buffer is empty)
	if len(bs) > s.writer.Available() && s.writer.Buffered() > 0 {
		if err := s.writer.Flush(); err != nil {
			return 0, err
		}
	}

Review comment (suggestion):
small optional nit: I would set s.Clock instead and leave s.Ticker out of the if-else. We can mutate the exported fields of this struct once Write is called because the contract is basically that it's "ours" now.

```suggestion
	if s.Clock == nil {
		s.Clock = DefaultClock
	}
	s.ticker = s.clock.NewTicker(flushInterval)
```

Code after:
	Clock Clock

	// unexported fields for state
	ws          WriteSyncer
	mu          sync.Mutex
	writer      *bufio.Writer
	ticker      *time.Ticker
	stop        chan struct{}
	initialized bool
}

const (
	// _defaultBufferSize specifies the default size used by Buffer.
	_defaultBufferSize = 256 * 1024 // 256 kB

	// _defaultFlushInterval specifies the default flush interval for
	// Buffer.
	_defaultFlushInterval = 30 * time.Second
)

func (s *BufferedWriteSyncer) initialize() {
	s.ws = s.WriteSyncer

	size := s.Size
	if size == 0 {
		size = _defaultBufferSize
	}

	flushInterval := s.FlushInterval
	if flushInterval == 0 {
		flushInterval = _defaultFlushInterval
	}

	if s.Clock == nil {
		s.Clock = DefaultClock
	}
	s.ticker = s.Clock.NewTicker(flushInterval)

	writer := s.WriteSyncer
	if w, ok := writer.(*lockedWriteSyncer); ok {
		writer = w.ws
	} // don't double lock
	s.writer = bufio.NewWriterSize(writer, size)

	s.stop = make(chan struct{})
	s.initialized = true
	go s.flushLoop()
}

// Write writes log data into buffer syncer directly, multiple Write calls will be batched,
// and log data will be flushed to disk when the buffer is full or periodically.
Repository: uber-go/zap
File: zapcore/buffered_write_syncer_bench_test.go (Go)

Code before:
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func BenchmarkBufferedWriteSyncer(b *testing.B) {
	b.Run("write file with buffer", func(b *testing.B) {
		file, err := ioutil.TempFile("", "log")
		assert.NoError(b, err)
		defer file.Close()
		defer os.Remove(file.Name())

		w := &BufferedWriteSyncer{
			WriteSyncer: AddSync(file),
		}
		defer w.Close()
		b.ResetTimer()
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				w.Write([]byte("foobarbazbabble"))
			}
		})
	})
}


Review comment (refactor):
optional: can use b.TempDir to not worry about clean up:

```suggestion
file, err := os.Create(filepath.Join(b.TempDir(), "log"))
require.NoError(t, err)
defer file.Close()
```

Code after:
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func BenchmarkBufferedWriteSyncer(b *testing.B) {
	b.Run("write file with buffer", func(b *testing.B) {
		file, err := ioutil.TempFile("", "log")
		require.NoError(b, err)

		defer func() {
			assert.NoError(b, file.Close())
			assert.NoError(b, os.Remove(file.Name()))
		}()

		w := &BufferedWriteSyncer{
			WriteSyncer: AddSync(file),
		}
		defer w.Close()
		b.ResetTimer()
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				w.Write([]byte("foobarbazbabble"))
			}
		})
	})
}
Repository: uber-go/zap
File: zapcore/buffered_write_syncer_bench_test.go (Go)

Code before:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func BenchmarkBufferedWriteSyncer(b *testing.B) {
	b.Run("write file with buffer", func(b *testing.B) {
		file, err := ioutil.TempFile("", "log")
		assert.NoError(b, err)
		defer file.Close()
		defer os.Remove(file.Name())

		w := &BufferedWriteSyncer{
			WriteSyncer: AddSync(file),
		}
		defer w.Close()
		b.ResetTimer()
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				w.Write([]byte("foobarbazbabble"))
			}
		})
	})
}


Review comment (suggestion):
This should be a `require` so that we don't proceed with the rest of the code here if we can't get a file.

```suggestion
		require.NoError(b, err)
```

Code after:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func BenchmarkBufferedWriteSyncer(b *testing.B) {
	b.Run("write file with buffer", func(b *testing.B) {
		file, err := ioutil.TempFile("", "log")
		require.NoError(b, err)

		defer func() {
			assert.NoError(b, file.Close())
			assert.NoError(b, os.Remove(file.Name()))
		}()

		w := &BufferedWriteSyncer{
			WriteSyncer: AddSync(file),
		}
		defer w.Close()
		b.ResetTimer()
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				w.Write([]byte("foobarbazbabble"))
			}
		})
	})
}