ronantakizawa/codereview-bench huggingface dataset:
Repository: etcd-io/etcd
File: tests/robustness/traffic/key_store.go (Go)

Code before:
		if op == model.DeleteOperation {
			k.replaceKey(permutedKeyIndexes[i])
		}
	}

	return keys
}

func (k *keyStore) GetPrefix() string {
	k.mu.Lock()
	defer k.mu.Unlock()

	return k.keyPrefix
}

func (k *keyStore) SyncKeys(resp *clientv3.GetResponse) {
	k.mu.Lock()
	defer k.mu.Unlock()

	if resp.Header.GetRevision() < k.latestRevision {
		return
	}

	k.latestRevision = resp.Header.GetRevision()

	kvs := resp.Kvs

	listKeys := make([]string, len(kvs))
	for i, kv := range kvs {
		listKeys[i] = string(kv.Key)
	}

	for _, key := range k.keys {
		if !slices.Contains(listKeys, key) {
			listKeys = append(listKeys, key)
		}
	}

	sort.Slice(listKeys, func(i, j int) bool {
		keyNumI, _ := strconv.Atoi(listKeys[i][len(k.keyPrefix):])
		keyNumJ, _ := strconv.Atoi(listKeys[j][len(k.keyPrefix):])
		return keyNumI < keyNumJ
	})

	for i := 0; i < len(k.keys); i++ {
		k.keys[i] = listKeys[i]
	}

	lastKeyNum, _ := strconv.Atoi(k.keys[len(k.keys)-1][len(k.keyPrefix):])
	k.counter = lastKeyNum + 1
}

Review comment (suggestion):
The algorithm looks correct, however it's not obvious what and why you are doing operations you do. I think this function should have proper docstring converting it.

Code after:
		if op == model.DeleteOperation {
			k.replaceKey(permutedKeyIndexes[i])
		}
	}

	return keys
}

func (k *keyStore) GetPrefix() string {
	k.mu.Lock()
	defer k.mu.Unlock()

	return k.keyPrefix
}

// SyncKeys reconciles our local key store with the keys currently in etcd.
//
// SyncKeys resolves this by:
//  1. Getting the list request result of all the keys.
//  2. Adding any keys that exist in etcd but are missing in the key store.
//  3. Maintaining the key store size by removing the higher numbered keys.
//
// Notice that higher numbered keys will eventually be added
// again into the keystore, so it is safe to temporarily remove them from the.
// key store.
func (k *keyStore) SyncKeys(resp *clientv3.GetResponse) {
	k.mu.Lock()
	defer k.mu.Unlock()

	if resp.Header.GetRevision() < k.latestRevision {
		return
	}

	k.latestRevision = resp.Header.GetRevision()

	listKeys := make([]string, len(resp.Kvs))
	for i, kv := range resp.Kvs {
		listKeys[i] = string(kv.Key)
	}

	for _, key := range k.keys {
		if !slices.Contains(listKeys, key) {
			listKeys = append(listKeys, key)
		}
	}

	sort.Slice(listKeys, func(i, j int) bool {
		keyNumI := k.getKeyNum(listKeys[i])
		keyNumJ := k.getKeyNum(listKeys[j])
		return keyNumI < keyNumJ
	})
Repository: etcd-io/etcd
File: tests/robustness/traffic/key_store.go (Go)

Code before:
	k.latestRevision = resp.Header.GetRevision()

	listKeys := make([]string, len(resp.Kvs))
	for i, kv := range resp.Kvs {
		listKeys[i] = string(kv.Key)
	}

	for _, key := range k.keys {
		if !slices.Contains(listKeys, key) {
			listKeys = append(listKeys, key)
		}
	}

	sort.Slice(listKeys, func(i, j int) bool {
		keyNumI, _ := k.getKeyNum(listKeys[i])
		keyNumJ, _ := k.getKeyNum(listKeys[j])
		return keyNumI < keyNumJ
	})

	k.keys = listKeys[:len(k.keys)]

	lastKeyNum, _ := k.getKeyNum(k.keys[len(k.keys)-1])
	k.counter = lastKeyNum + 1
}

func (k *keyStore) getKeyNum(key string) (int, error) {
	if len(key) < len(k.keyPrefix) {
		return -1, fmt.Errorf("key %s is shorter than prefix %s", key, k.keyPrefix)
	}

	numStr := key[len(k.keyPrefix):]
	num, err := strconv.Atoi(numStr)
	if err != nil {
		return -1, fmt.Errorf("key %s does not have a valid number after prefix %s: %w", key, k.keyPrefix, err)
	}

	return num, nil
}

func (k *keyStore) replaceKey(index int) {
	if rand.Intn(100) < 90 {
		k.keys[index] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
		k.counter++
	}
}


Review comment (bug):
Why return error here if you always ignore it. Better just ignore it here and return 0.

Code after:

	for _, key := range k.keys {
		if !slices.Contains(listKeys, key) {
			listKeys = append(listKeys, key)
		}
	}

	sort.Slice(listKeys, func(i, j int) bool {
		keyNumI := k.getKeyNum(listKeys[i])
		keyNumJ := k.getKeyNum(listKeys[j])
		return keyNumI < keyNumJ
	})

	k.keys = listKeys[:len(k.keys)]

	lastKeyNum := k.getKeyNum(k.keys[len(k.keys)-1])
	k.counter = lastKeyNum + 1
}

func (k *keyStore) getKeyNum(key string) int {
	if len(key) < len(k.keyPrefix) {
		return 0
	}

	numStr := key[len(k.keyPrefix):]
	num, _ := strconv.Atoi(numStr)
	return num
}

func (k *keyStore) replaceKey(index int) {
	if rand.Intn(100) < 90 {
		k.keys[index] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
		k.counter++
	}
}
Repository: etcd-io/etcd
File: tests/antithesis/test-template/robustness/traffic/main.go (Go)

Code before:
			defer wg.Done()
			defer c.Close()

			traffic.EtcdAntithesis.RunTrafficLoop(ctx, c, limiter,
				ids,
				storage,
				concurrencyLimiter,
				keyStore,
				finish,
			)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c)
	}
	wg.Add(1)
	compactClient := connect(hosts, ids, baseTime)
	go func(c *client.RecordingClient) {
		defer wg.Done()
		defer c.Close()
		traffic.EtcdAntithesis.RunCompactLoop(ctx, c, traffic.DefaultCompactionPeriod, finish)
		mux.Lock()
		reports = append(reports, c.Report())
		mux.Unlock()
	}(compactClient)
	dd := time.Duration(robustnessrand.RandRange(3, 7)) * traffic.DefaultCompactionPeriod
	lg.Info("Defragment run", zap.Duration("period", dd))
	for _, h := range hosts {
		c := connect([]string{h}, ids, baseTime)
		wg.Add(1)
		go func(c *client.RecordingClient) {
			defer wg.Done()
			defer c.Close()
			runDefragLoop(ctx, c, dd, finish)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c)
	}
	wg.Wait()
	return reports
}

func runDefragLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
	ticker := time.NewTicker(period)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-finish:

Review comment (suggestion):
There should be more variability and we should introduce some jitter or all defrags will be executed at the same time.  

Code after:
			defer wg.Done()
			defer c.Close()

			traffic.EtcdPutDeleteLease.RunTrafficLoop(ctx, c, limiter,
				ids,
				storage,
				concurrencyLimiter,
				keyStore,
				finish,
			)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c)
	}
	wg.Add(1)
	compactClient := connect(hosts, ids, baseTime)
	go func(c *client.RecordingClient) {
		defer wg.Done()
		defer c.Close()
		traffic.EtcdPutDeleteLease.RunCompactLoop(ctx, c, traffic.DefaultCompactionPeriod, finish)
		mux.Lock()
		reports = append(reports, c.Report())
		mux.Unlock()
	}(compactClient)
	defragPeriod := traffic.DefaultCompactionPeriod * time.Duration(len(hosts))
	for _, h := range hosts {
		c := connect([]string{h}, ids, baseTime)
		wg.Add(1)
		go func(c *client.RecordingClient) {
			defer wg.Done()
			defer c.Close()
			runDefragLoop(ctx, c, defragPeriod, finish)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c)
	}
	wg.Wait()
	return reports
}

func runDefragLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
	jittered := time.Duration(robustnessrand.RandRange(int64(period-period/2), int64(period+period/2)))
	ticker := time.NewTicker(jittered)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-finish:
Repository: etcd-io/etcd
File: tests/antithesis/test-template/robustness/traffic/main.go (Go)

Code before:
			defer wg.Done()
			defer c.Close()

			traffic.EtcdPutDeleteLease.RunTrafficLoop(ctx, c, limiter,
				ids,
				storage,
				concurrencyLimiter,
				keyStore,
				finish,
			)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c)
	}
	wg.Add(1)
	compactClient := connect(hosts, ids, baseTime)
	go func(c *client.RecordingClient) {
		defer wg.Done()
		defer c.Close()
		traffic.EtcdPutDeleteLease.RunCompactLoop(ctx, c, traffic.DefaultCompactionPeriod, finish)
		mux.Lock()
		reports = append(reports, c.Report())
		mux.Unlock()
	}(compactClient)
	dd := time.Duration(robustnessrand.RandRange(3, 37)) * traffic.DefaultCompactionPeriod
	for _, h := range hosts {
		c := connect([]string{h}, ids, baseTime)
		wg.Add(1)
		go func(c *client.RecordingClient, lg *zap.Logger) {
			defer wg.Done()
			defer c.Close()
			runDefragLoop(ctx, lg, c, dd, finish)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c, lg)
	}
	wg.Wait()
	return reports
}

func runDefragLoop(ctx context.Context, lg *zap.Logger, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
	for {
		jitter := robustnessrand.RandRange(int64(period-period/2), int64(period+period/2))
		nextPeriod := period + time.Duration(jitter)
		lg.Info("next defragment run", zap.String("host", c.Endpoints()[0]), zap.Time("at", time.Now().Add(nextPeriod)))
		ticker := time.NewTicker(nextPeriod)
		defer ticker.Stop()

		select {

Review comment (bug):
`3-37` Where those number came from? 
The `DefaultCompactionPeriod` is 200ms, so what's the purpose of running it every 8s?

Code after:
			defer wg.Done()
			defer c.Close()

			traffic.EtcdPutDeleteLease.RunTrafficLoop(ctx, c, limiter,
				ids,
				storage,
				concurrencyLimiter,
				keyStore,
				finish,
			)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c)
	}
	wg.Add(1)
	compactClient := connect(hosts, ids, baseTime)
	go func(c *client.RecordingClient) {
		defer wg.Done()
		defer c.Close()
		traffic.EtcdPutDeleteLease.RunCompactLoop(ctx, c, traffic.DefaultCompactionPeriod, finish)
		mux.Lock()
		reports = append(reports, c.Report())
		mux.Unlock()
	}(compactClient)
	defragPeriod := traffic.DefaultCompactionPeriod * time.Duration(len(hosts))
	for _, h := range hosts {
		c := connect([]string{h}, ids, baseTime)
		wg.Add(1)
		go func(c *client.RecordingClient) {
			defer wg.Done()
			defer c.Close()
			runDefragLoop(ctx, c, defragPeriod, finish)
			mux.Lock()
			reports = append(reports, c.Report())
			mux.Unlock()
		}(c)
	}
	wg.Wait()
	return reports
}

func runDefragLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
	jittered := time.Duration(robustnessrand.RandRange(int64(period-period/2), int64(period+period/2)))
	ticker := time.NewTicker(jittered)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-finish:
Repository: etcd-io/etcd
File: pkg/report/report.go (Go)

Code before:
	n := len(nums)
	for i := 0; i < n && j < len(pctls); i++ {
		current := float64(i) * 100.0 / float64(n)
		if current >= pctls[j] {
			data[j] = nums[i]
			j++
		}
	}
	return data
}

func (r *report) sprintLatencies() string {
	data := percentiles(r.stats.Lats)
	s := "\nLatency distribution:\n"
	for i := 0; i < len(pctls); i++ {
		if data[i] > 0 {
			s += fmt.Sprintf("  %v%% in %s.\n", pctls[i], r.sec2str(data[i]))
		}
	}
	if r.generateJsonReport {
		r.generateJsonBenchmarkReport()
	}
	return s
}

func (r *report) generateJsonBenchmarkReport() {
	pcls, data := Percentiles(r.stats.Lats)
	pclsData := make(map[float64]float64)
	for i := 0; i < len(pcls); i++ {
		pclsData[pcls[i]] = data[i] * 1000 // Since the reported data is in seconds, convert to ms.
	}
	report := perfdashFormattedReport{
		Version: "v1",
		DataItems: []DataItem{
			{
				Data: Metrics{
					Perc50: math.Round(pclsData[50]*10000) / 10000,
					Perc90: math.Round(pclsData[90]*10000) / 10000,
					Perc99: math.Round(pclsData[99]*10000) / 10000,
				},
				Unit: "ms",
				Labels: Labels{
					Metric: "APIResponsiveness",
				},
			},
		},
	}
	reportB, _ := json.MarshalIndent(report, "", "  ")

	artifactsDir := os.Getenv("ARTIFACTS")
	if artifactsDir == "" {

Review comment (suggestion):
This function looks like something also that should be moved to `perfdash.go` file instead of living in `report.go`.

Code after:
	return s
}

func (r *report) histogram() string {
	bc := 10
	buckets := make([]float64, bc+1)
	counts := make([]int, bc+1)
	bs := (r.stats.Slowest - r.stats.Fastest) / float64(bc)
	for i := 0; i < bc; i++ {
		buckets[i] = r.stats.Fastest + bs*float64(i)
	}
	buckets[bc] = r.stats.Slowest
	var bi int
	var max int
	for i := 0; i < len(r.stats.Lats); {
		if r.stats.Lats[i] <= buckets[bi] {
			i++
			counts[bi]++
			if max < counts[bi] {
				max = counts[bi]
			}
		} else if bi < len(buckets)-1 {
			bi++
		}
	}
	s := "\nResponse time histogram:\n"
	for i := 0; i < len(buckets); i++ {
		// Normalize bar lengths.
		var barLen int
		if max > 0 {
			barLen = counts[i] * 40 / max
		}
		s += fmt.Sprintf("  "+r.precision+" [%v]\t|%v\n", buckets[i], counts[i], strings.Repeat(barChar, barLen))
	}
	return s
}

func (r *report) errors() string {
	s := "\nError distribution:\n"
	for err, num := range r.stats.ErrorDist {
		s += fmt.Sprintf("  [%d]\t%s\n", num, err)
	}
	return s
}
Repository: etcd-io/etcd
File: tests/robustness/client/client.go (Go)

Code before:

func (cs *ClientSet) NewClient(endpoints []string) (*RecordingClient, error) {
	if cs.closed {
		return nil, errors.New("The ClientSet is already closed")
	}
	cli, err := NewRecordingClient(endpoints, cs.idProvider, cs.baseTime)
	if err != nil {
		return nil, err
	}
	cs.clients = append(cs.clients, cli)
	return cli, nil
}

func (cs *ClientSet) Reports() []report.ClientReport {
	if !cs.closed {
		cs.Close()
	}
	return cs.reports
}

func (cs *ClientSet) Close() {
	if cs.closed {
		return
	}
	cs.closed = true
	cs.reports = make([]report.ClientReport, 0, len(cs.clients))
	for _, c := range cs.clients {
		cs.reports = append(cs.reports, c.Report())
		c.Close()
	}
}

func (cs *ClientSet) IdentityProvider() identity.Provider {
	return cs.idProvider
}


Review comment (bug):
It is incorrect to calculate reports only on `Close` as it's always called after `Reports`. Reports is called within function while Close is deferred. 

Solution: Reports should also close.

Code after:
}

func (cs *ClientSet) NewClient(endpoints []string) (*RecordingClient, error) {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if cs.closed {
		return nil, errors.New("the clientset is already closed")
	}
	cli, err := NewRecordingClient(endpoints, cs.idProvider, cs.baseTime)
	if err != nil {
		return nil, err
	}
	cs.clients = append(cs.clients, cli)
	return cli, nil
}

func (cs *ClientSet) Reports() []report.ClientReport {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if !cs.closed {
		cs.close()
	}
	if cs.reports == nil {
		reports := cs.generateReports()
		cs.reports = reports
	}
	return cs.reports
}

func (cs *ClientSet) Close() {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	cs.close()
}

func (cs *ClientSet) close() {
	if cs.closed {
		return
	}
	for _, c := range cs.clients {
		c.Close()
	}
	cs.closed = true
}

func (cs *ClientSet) generateReports() []report.ClientReport {
	reports := make([]report.ClientReport, 0, len(cs.clients))
	for _, c := range cs.clients {
		reports = append(reports, c.Report())
	}
	return reports
Repository: etcd-io/etcd
File: tests/robustness/client/client.go (Go)

Code before:
			ModRevision: event.PrevKv.ModRevision,
			Version:     event.PrevKv.Version,
		}
	}
	watch.IsCreate = event.IsCreate()

	switch event.Type {
	case mvccpb.PUT:
		watch.Type = model.PutOperation
	case mvccpb.DELETE:
		watch.Type = model.DeleteOperation
	default:
		panic(fmt.Sprintf("Unexpected event type: %s", event.Type))
	}
	return watch
}

type ClientSet struct {
	mux        sync.Mutex
	idProvider identity.Provider
	baseTime   time.Time

	closed  bool
	clients []*RecordingClient
	reports []report.ClientReport
}

func NewSet(ids identity.Provider, baseTime time.Time) *ClientSet {
	return &ClientSet{
		idProvider: ids,
		baseTime:   baseTime,

		clients: []*RecordingClient{},
	}
}

func (cs *ClientSet) NewClient(endpoints []string) (*RecordingClient, error) {
	if cs.closed {
		return nil, errors.New("the clientset is already closed")
	}
	cli, err := NewRecordingClient(endpoints, cs.idProvider, cs.baseTime)
	if err != nil {
		return nil, err
	}
	cs.mux.Lock()
	cs.clients = append(cs.clients, cli)
	cs.mux.Unlock()
	return cli, nil
}

func (cs *ClientSet) Reports() []report.ClientReport {

Review comment (suggestion):
```suggestion
type ClientSet struct {
	idProvider identity.Provider
	baseTime   time.Time

	mux        sync.Mutex
	closed  bool
	clients []*RecordingClient
	reports []report.ClientReport
}
```

It good to have mutex over the variables it's meant to protect. We don't need to protect idProvider or baseTime as they are never changed.

Code after:
			ModRevision: event.PrevKv.ModRevision,
			Version:     event.PrevKv.Version,
		}
	}
	watch.IsCreate = event.IsCreate()

	switch event.Type {
	case mvccpb.PUT:
		watch.Type = model.PutOperation
	case mvccpb.DELETE:
		watch.Type = model.DeleteOperation
	default:
		panic(fmt.Sprintf("Unexpected event type: %s", event.Type))
	}
	return watch
}

type ClientSet struct {
	idProvider identity.Provider
	baseTime   time.Time

	mux     sync.Mutex
	closed  bool
	clients []*RecordingClient
	reports []report.ClientReport
}

func NewSet(ids identity.Provider, baseTime time.Time) *ClientSet {
	return &ClientSet{
		idProvider: ids,
		baseTime:   baseTime,

		clients: []*RecordingClient{},
	}
}

func (cs *ClientSet) NewClient(endpoints []string) (*RecordingClient, error) {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if cs.closed {
		return nil, errors.New("the clientset is already closed")
	}
	cli, err := NewRecordingClient(endpoints, cs.idProvider, cs.baseTime)
	if err != nil {
		return nil, err
	}
	cs.clients = append(cs.clients, cli)
	return cli, nil
}

func (cs *ClientSet) Reports() []report.ClientReport {
Repository: etcd-io/etcd
File: tests/robustness/client/client.go (Go)

Code before:

func NewSet(ids identity.Provider, baseTime time.Time) *ClientSet {
	return &ClientSet{
		idProvider: ids,
		baseTime:   baseTime,

		clients: []*RecordingClient{},
	}
}

func (cs *ClientSet) NewClient(endpoints []string) (*RecordingClient, error) {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if cs.closed {
		return nil, errors.New("the clientset is already closed")
	}
	cli, err := NewRecordingClient(endpoints, cs.idProvider, cs.baseTime)
	if err != nil {
		return nil, err
	}
	cs.clients = append(cs.clients, cli)
	return cli, nil
}

func (cs *ClientSet) Reports() []report.ClientReport {
	cs.Close()
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if cs.reports == nil {
		reports := cs.generateReports()
		cs.reports = reports
	}
	return cs.reports
}

func (cs *ClientSet) Close() {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if cs.closed {
		return
	}
	for _, c := range cs.clients {
		c.Close()
	}
	cs.closed = true
}

func (cs *ClientSet) generateReports() []report.ClientReport {
	reports := make([]report.ClientReport, 0, len(cs.clients))
	for _, c := range cs.clients {
		reports = append(reports, c.Report())

Review comment (suggestion):
You could avoid using mutex twice by having 2 functions `Close` that takes mutex and calls `close` that just closes assuming that mutex is already taken. That would allow `Reports` to take mutex once can just call `close`.

Code after:

func NewSet(ids identity.Provider, baseTime time.Time) *ClientSet {
	return &ClientSet{
		idProvider: ids,
		baseTime:   baseTime,

		clients: []*RecordingClient{},
	}
}

func (cs *ClientSet) NewClient(endpoints []string) (*RecordingClient, error) {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if cs.closed {
		return nil, errors.New("the clientset is already closed")
	}
	cli, err := NewRecordingClient(endpoints, cs.idProvider, cs.baseTime)
	if err != nil {
		return nil, err
	}
	cs.clients = append(cs.clients, cli)
	return cli, nil
}

func (cs *ClientSet) Reports() []report.ClientReport {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	if !cs.closed {
		cs.close()
	}
	if cs.reports == nil {
		reports := cs.generateReports()
		cs.reports = reports
	}
	return cs.reports
}

func (cs *ClientSet) Close() {
	cs.mux.Lock()
	defer cs.mux.Unlock()
	cs.close()
}

func (cs *ClientSet) close() {
	if cs.closed {
		return
	}
	for _, c := range cs.clients {
		c.Close()
	}
	cs.closed = true
Repository: etcd-io/etcd
File: tests/robustness/coverage/coverage_test.go (Go)

Code before:
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package coverage_test

import (
	"encoding/json"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"go.opentelemetry.io/collector/pdata/ptrace"
)

func TestInterfaceUse(t *testing.T) {
	files, err := os.ReadDir("testdata")
	if err != nil {
		t.Fatal(err)
	}

	for _, file := range files {
		filename := file.Name()
		t.Run(filename, func(t *testing.T) { testInterfaceUse(t, filename) })
	}
}

func testInterfaceUse(t *testing.T, filename string) {
	b, err := os.ReadFile(filepath.Join("testdata", filename))
	if err != nil {
		t.Fatalf("read test data: %v", err)
	}
	var dump Dump
	err = json.Unmarshal(b, &dump)
	if err != nil {
		t.Fatalf("unmarshal testdata %s: %v", filename, err)
	}
	traces := dump.Result
	t.Log("Traces found:", traces.ResourceSpans().Len())

	callsByOperationName := make(map[string]int)
	for _, trace := range traces.ResourceSpans().All() {
		serviceName := getServiceName(trace)
		if serviceName != "etcd" {
			continue
		}
		opName := getOperationName(trace)

Review comment (suggestion):
Any reason this is a test? For reporting list of methods used normal binary would do and have more flexibility. Not everything in `tests` directory needs to be a test.


Code after:
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package coverage_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/olekukonko/tablewriter"
	"github.com/olekukonko/tablewriter/tw"
	traceservice "go.opentelemetry.io/proto/otlp/collector/trace/v1"
	tracev1 "go.opentelemetry.io/proto/otlp/trace/v1"
	"google.golang.org/protobuf/encoding/protojson"
)

func TestInterfaceUse(t *testing.T) {
	files, err := os.ReadDir("testdata")
	if err != nil {
		t.Fatal(err)
	}

	for _, file := range files {
		filename := file.Name()
		if filename == ".gitignore" {
			continue
		}
		t.Run(filename, func(t *testing.T) { testInterfaceUse(t, filename) })
	}
}

func testInterfaceUse(t *testing.T, filename string) {
	b, err := os.ReadFile(filepath.Join("testdata", filename))
	if err != nil {
		t.Fatalf("read test data: %v", err)
	}
	var dump Dump
	err = json.Unmarshal(b, &dump)
	if err != nil {
		t.Fatalf("unmarshal testdata %s: %v", filename, err)
	}
	if dump.Result == nil {