You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: reader_test.go
    path of file: ./repos/ebpf/perf
    
    the code of the file until where you have to start completion: package perf

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"os"
	"syscall"
	"testing"
	"time"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/asm"
	"github.com/cilium/ebpf/internal"
	"github.com/cilium/ebpf/internal/testutils"
	"github.com/cilium/ebpf/internal/testutils/fdtrace"
	"github.com/cilium/ebpf/internal/unix"

	"github.com/go-quicktest/qt"
)

var (
	readTimeout = 250 * time.Millisecond
)

func TestMain(m *testing.M) {
	fdtrace.TestMain(m)
}

func TestPerfReader(t *testing.T) {
	events := perfEventArray(t)

	rd, err := NewReader(events, 4096)
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	qt.Assert(t, qt.Equals(rd.BufferSize(), 4096))

	outputSamples(t, events, 5, 5)

	_, rem := checkRecord(t, rd)
	qt.Assert(t, qt.IsTrue(rem >= 5), qt.Commentf("expected at least 5 Remaining"))

	_, rem = checkRecord(t, rd)
	qt.Assert(t, qt.Equals(rem, 0), qt.Commentf("expected zero Remaining"))

	rd.SetDeadline(time.Now().Add(4 * time.Milliseco
    
func outputSamplesProg(tb testing.TB, events *ebpf.Map, sampleSizes ...byte) *ebpf.Program {
	tb.Helper()

	// Requires at least 4.9 (0515e5999a46 "bpf: introduce BPF_PROG_TYPE_PERF_EVENT program type")
	testutils.SkipOnOldKernel(tb, "4.9", "perf events support")

	const bpfFCurrentCPU = 0xffffffff

	var maxSampleSize byte
	for _, sampleSize := range sampleSizes {
		if sampleSize < 2 {
			tb.Fatalf("Sample size %d is too small to contain size and counter", sampleSize)
		}
		if sampleSize > maxSampleSize {
			maxSampleSize = sampleSize
		}
	}

	// Fill a buffer on the stack, and stash context somewhere
	insns := asm.Instructions{
		asm.LoadImm(asm.R0, ^int64(0), asm.DWord),
		asm.Mov.Reg(asm.R9, asm.R1),
	}

	bufDwords := int(maxSampleSize/8) + 1
	for i := 0; i < bufDwords; i++ {
		insns = append(insns,
			asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord),
		)
	}

	for i, sampleSize := range sampleSizes {
		insns = append(insns,
			// Restore stashed context.
			asm.Mov.Reg(asm.R1, asm.R9),
			// map
			asm.LoadMapPtr(asm.R2, events.FD()),
			// flags
			asm.LoadImm(asm.R3, bpfFCurrentCPU, asm.DWord),
			// buffer
			asm.Mov.Reg(asm.R4, asm.RFP),
			asm.Add.Imm(asm.R4, int32(bufDwords*-8)),
			// buffer[0] = size
			asm.StoreImm(asm.R4, 0, int64(sampleSize), asm.Byte),
			// buffer[1] = i
			asm.StoreImm(asm.R4, 1, int64(i&math.MaxUint8), asm.Byte),
			// size
			asm.Mov.Imm(asm.R5, int32(sampleSize)),
			asm.FnPerfEventOutput.Call(),
		)
	}

	insns = append(insns, asm.Return())

	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
		License:      "GPL",
		Type:         ebpf.XDP,
		Instructions: insns,
	})
	if err != nil {
		tb.Fatal(err)
	}
	tb.Cleanup(func() { prog.Close() })

	return prog
}