ronantakizawa/codereview-bench huggingface dataset:
Repository: grpc/grpc-go
File: grpclog/internal/loggerv2.go (Go)

Code before:
}

func (g *loggerT) V(l int) bool {
	return l <= g.v
}

// LoggerV2Config configures the LoggerV2 implementation.
type LoggerV2Config struct {
	// Verbosity sets the verbosity level of the logger.
	Verbosity int
	// FormatJSON controls whether the logger should output logs in JSON format.
	FormatJSON bool
}

// NewLoggerV2 creates a new LoggerV2 instance with the provided configuration.
// The infoW, warningW, and errorW writers are used to write log messages of
// different severity levels.
func NewLoggerV2(infoW, warningW, errorW io.Writer, c LoggerV2Config) LoggerV2 {
	flag := log.LstdFlags
	if c.FormatJSON {
		flag = 0
	}

	infoLogW := infoW

	// Use io.Discard instead of io.MultiWriter if all loggers at a given level
	// are io.Discard.
	// The log package has significant internal optimizations around io.Discard,
	// and so does this package,
	// while io.MultiWriter doesn't (as of this writing).

	warnLogW := io.Discard
	if warningW != io.Discard || infoW != io.Discard {
		warnLogW = io.MultiWriter(infoW, warningW)
	}

	errLogW := io.Discard
	if errorW != io.Discard || warningW != io.Discard || infoW != io.Discard {
		errLogW = io.MultiWriter(infoW, warningW, errorW)
	}

	fatalLogW := errLogW

	m := []*log.Logger{
		log.New(infoLogW, "", flag),
		log.New(warnLogW, "", flag),
		log.New(errLogW, "", flag),
		log.New(fatalLogW, "", flag),
	}
	return &loggerT{m: m, v: c.Verbosity, jsonFormat: c.FormatJSON}
}

Review comment (suggestion):
no need of new lines in between sentences. Suggestion;
```
// Use io.Discard instead of io.MultiWriter when all loggers at a given level
// are set to io.Discard. Both this package and the standard log package have
// significant optimizations for io.Discard, which io.MultiWriter lacks (as of this writing).
```

Code after:
}

func (g *loggerT) Errorf(format string, args ...any) {
	g.printf(errorLog, format, args...)
}

func (g *loggerT) Fatal(args ...any) {
	g.print(fatalLog, args...)
	exit(1)
}

func (g *loggerT) Fatalln(args ...any) {
	g.println(fatalLog, args...)
	exit(1)
}

func (g *loggerT) Fatalf(format string, args ...any) {
	g.printf(fatalLog, format, args...)
	exit(1)
}

func (g *loggerT) V(l int) bool {
	return l <= g.v
}

// LoggerV2Config configures the LoggerV2 implementation.
type LoggerV2Config struct {
	// Verbosity sets the verbosity level of the logger.
	Verbosity int
	// FormatJSON controls whether the logger should output logs in JSON format.
	FormatJSON bool
}

// combineLoggers returns a combined logger for both higher & lower severity logs,
// or only one if the other is io.Discard.
//
// This uses io.Discard instead of io.MultiWriter when all loggers
// are set to io.Discard. Both this package and the standard log package have
// significant optimizations for io.Discard, which io.MultiWriter lacks (as of
// this writing).
func combineLoggers(lower, higher io.Writer) io.Writer {
	if lower == io.Discard {
		return higher
	}
	if higher == io.Discard {
		return lower
	}
	return io.MultiWriter(lower, higher)
}

// NewLoggerV2 creates a new LoggerV2 instance with the provided configuration.
Repository: grpc/grpc-go
File: grpclog/internal/loggerv2_test.go (Go)

Code before:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 internal

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"testing"
)

// check if b is in the format of:
//
//	2017/04/07 14:55:42 WARNING: WARNING
func checkLogForSeverity(s int, b []byte) error {
	expected := regexp.MustCompile(fmt.Sprintf(`^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} %s: %s\n$`, severityName[s], severityName[s]))
	if m := expected.Match(b); !m {
		return fmt.Errorf("got: %v, want string in format of: %v", string(b), severityName[s]+": 2016/10/05 17:09:26 "+severityName[s])
	}
	return nil
}

// makeLogFuncErrr returns a logger printFunc which generates a test error,
// for testing that the log func is not called
func makeLogFuncErr(t *testing.T) func() string {
	return func() string {
		t.Errorf("got: logger func called on io.Discard logger, want: Logger func to not be called for io.Discard")
		return ""
	}
}

// logFuncStr is a string used via testLogFuncOk and
// testCheckLogContainsFuncStr to test the logger printFunc.
const logFuncStr = "called-func"

// logFuncOk is a logger printFunc to write a constant string
func logFuncOk() string {

Review comment (suggestion):
```
// makeLogFuncErr returns a logger printFunc that generates a test error.
// It is used to test that the log function is not called.
```

Code after:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 internal

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"reflect"
	"regexp"
	"strings"
	"testing"
)

// logFuncStr is a string used via testCheckLogContainsFuncStr to test the
// logger output.
const logFuncStr = "called-func"

func makeSprintfErr(t *testing.T) func(format string, a ...any) string {
	return func(string, ...any) string {
		t.Errorf("got: sprintf called on io.Discard logger, want: expensive sprintf to not be called for io.Discard")
		return ""
	}
}

func makeSprintErr(t *testing.T) func(a ...any) string {
	return func(...any) string {
		t.Errorf("got: sprint called on io.Discard logger, want: expensive sprint to not be called for io.Discard")
		return ""
	}
}

// checkLogContainsFuncStr checks that the logger buffer logBuf contains
// logFuncStr.
func checkLogContainsFuncStr(t *testing.T, logBuf []byte) {
	if !bytes.Contains(logBuf, []byte(logFuncStr)) {
		t.Errorf("got '%v', want logger func to be called and print '%v'", string(logBuf), logFuncStr)
	}
}
Repository: grpc/grpc-go
File: grpclog/internal/loggerv2.go (Go)

Code before:
	// warningLog indicates Warning severity.
	warningLog
	// errorLog indicates Error severity.
	errorLog
	// fatalLog indicates Fatal severity.
	fatalLog
)

// severityName contains the string representation of each severity.
var severityName = []string{
	infoLog:    "INFO",
	warningLog: "WARNING",
	errorLog:   "ERROR",
	fatalLog:   "FATAL",
}

// loggerT is the default logger used by grpclog.
type loggerT struct {
	m          []*log.Logger
	v          int
	jsonFormat bool
}

// printFunc prints the output of fn to the logger of the given severity.
// If the logger at given severity is io.Discard, fn is not called.
func (g *loggerT) printFunc(severity int, fn func() string) {
	lg := g.m[severity]
	if lg.Writer() == io.Discard {
		return
	}

	sevStr := severityName[severity]

	if !g.jsonFormat {
		lg.Output(2, sevStr+": "+fn())
		return
	}

	// TODO: we can also include the logging component, but that needs more
	// (API) changes.
	b, _ := json.Marshal(map[string]string{
		"severity": sevStr,
		"message":  fn(),
	})
	lg.Output(2, string(b))
}

func (g *loggerT) printf(severity int, format string, args ...any) {
	g.printFunc(severity, func() string { return fmt.Sprintf(format, args...) })
}


Review comment (performance):
The closure here seems unfortunate, and I think this approach has performance impacts for non-Discard use cases (creating the closure and the call to concatenate the strings inside `if !g.jsonFormat`).

Can we keep `output` and add an `outputf` that duplicates the `io.Discard` check, and avoids the closure and different formatting?

Code after:
	// warningLog indicates Warning severity.
	warningLog
	// errorLog indicates Error severity.
	errorLog
	// fatalLog indicates Fatal severity.
	fatalLog
)

// severityName contains the string representation of each severity.
var severityName = []string{
	infoLog:    "INFO",
	warningLog: "WARNING",
	errorLog:   "ERROR",
	fatalLog:   "FATAL",
}

// sprintf is fmt.Sprintf.
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
var sprintf = fmt.Sprintf

// sprint is fmt.Sprint.
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
var sprint = fmt.Sprint

// sprintln is fmt.Sprintln.
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
var sprintln = fmt.Sprintln

// exit is os.Exit.
// This var exists to make it possible to test functions calling os.Exit.
var exit = os.Exit

// loggerT is the default logger used by grpclog.
type loggerT struct {
	m          []*log.Logger
	v          int
	jsonFormat bool
}

func (g *loggerT) output(severity int, s string) {
	sevStr := severityName[severity]
	if !g.jsonFormat {
		g.m[severity].Output(2, sevStr+": "+s)
		return
	}
	// TODO: we can also include the logging component, but that needs more
	// (API) changes.
	b, _ := json.Marshal(map[string]string{
		"severity": sevStr,
		"message":  s,
	})
Repository: grpc/grpc-go
File: internal/status/status.go (Go)

Code before:
// Proto returns s's status as an spb.Status proto message.
func (s *Status) Proto() *spb.Status {
	if s == nil {
		return nil
	}
	return proto.Clone(s.s).(*spb.Status)
}

// Err returns an immutable error representing s; returns nil if s.Code() is OK.
func (s *Status) Err() error {
	if s.Code() == codes.OK {
		return nil
	}
	return &Error{s: s}
}

// WithDetails returns a new status with the provided details messages appended to the status.
// If any errors are encountered, it returns nil and the first error encountered.
func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {
	if s.Code() == codes.OK {
		return nil, errors.New("no error details for status with code OK")
	}
	// s.Code() != OK implies that s.Proto() != nil.
	p := s.Proto()
	for _, detail := range details {
		anyMsg, err := anypb.New(protoadapt.MessageV2Of(detail))
		if err != nil {
			return nil, err
		}
		p.Details = append(p.Details, anyMsg)
	}
	return &Status{s: p}, nil
}

// Details returns a slice of details messages attached to the status.
// If a detail cannot be decoded, the error is returned in place of the detail.
func (s *Status) Details() []any {
	if s == nil || s.s == nil {
		return nil
	}
	details := make([]any, 0, len(s.s.Details))
	for _, any := range s.s.Details {
		detail, err := any.UnmarshalNew()
		if err != nil {
			details = append(details, err)
			continue
		}
		details = append(details, detail)
	}
	return details
}

Review comment (suggestion):
```suggestion
		m, err := anypb.New(protoadapt.MessageV2Of(detail))
```

Code after:
// Proto returns s's status as an spb.Status proto message.
func (s *Status) Proto() *spb.Status {
	if s == nil {
		return nil
	}
	return proto.Clone(s.s).(*spb.Status)
}

// Err returns an immutable error representing s; returns nil if s.Code() is OK.
func (s *Status) Err() error {
	if s.Code() == codes.OK {
		return nil
	}
	return &Error{s: s}
}

// WithDetails returns a new status with the provided details messages appended to the status.
// If any errors are encountered, it returns nil and the first error encountered.
func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {
	if s.Code() == codes.OK {
		return nil, errors.New("no error details for status with code OK")
	}
	// s.Code() != OK implies that s.Proto() != nil.
	p := s.Proto()
	for _, detail := range details {
		m, err := anypb.New(protoadapt.MessageV2Of(detail))
		if err != nil {
			return nil, err
		}
		p.Details = append(p.Details, m)
	}
	return &Status{s: p}, nil
}

// Details returns a slice of details messages attached to the status.
// If a detail cannot be decoded, the error is returned in place of the detail.
func (s *Status) Details() []any {
	if s == nil || s.s == nil {
		return nil
	}
	details := make([]any, 0, len(s.s.Details))
	for _, any := range s.s.Details {
		detail, err := any.UnmarshalNew()
		if err != nil {
			details = append(details, err)
			continue
		}
		details = append(details, detail)
	}
	return details
}
Repository: grpc/grpc-go
File: examples/features/advancedtls/client/main.go (Go)

Code before:
/*
 *
 * Copyright 2024 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 *
 */

// Binary client is an example client demonstrating use of advancedtls,
// to set up a secure gRPC client connection with various TLS authentication
// methods.
package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"time"

	pb "google.golang.org/grpc/examples/features/proto/echo"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/credentials/tls/certprovider"
	"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
	"google.golang.org/grpc/security/advancedtls"
)

const credRefreshInterval = 1 * time.Minute
const serverAddr = "localhost"
const goodServerPort string = "50051"

Review comment (security):
nit:

```suggestion
// Binary client is an example client demonstrating use of advancedtls, to set
// up a secure gRPC client connection with various TLS authentication methods.
```

Code after:
/*
 *
 * Copyright 2024 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 *
 */

// Binary client is an example client demonstrating use of advancedtls, to set
// up a secure gRPC client connection with various TLS authentication methods.
package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"time"

	pb "google.golang.org/grpc/examples/features/proto/echo"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/credentials/tls/certprovider"
	"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
	"google.golang.org/grpc/security/advancedtls"
)

const credRefreshInterval = 1 * time.Minute
const serverAddr = "localhost"
const goodServerPort string = "50051"
const revokedServerPort string = "50053"
Repository: grpc/grpc-go
File: internal/channelz/channel.go (Go)

Code before:
	// Non-zero traceRefCount means the trace of this channel cannot be deleted.
	traceRefCount int32

	// ChannelMetrics holds connectivity state, target and call metrics for the
	// channel within channelz.
	ChannelMetrics ChannelMetrics
}

// Implemented to make Channel implement the Identifier interface used for
// nesting.
func (c *Channel) channelzIdentifier() {}

// String returns a string representation of the Channel, including its parent
// entity and ID.
func (c *Channel) String() string {
	if c.Parent == nil {
		return fmt.Sprintf("Channel #%d", c.ID)
	}
	return fmt.Sprintf("%s Channel #%d", c.Parent, c.ID)
}

func (c *Channel) id() int64 {
	return c.ID
}

// SubChans returns a copy of the map of sub-channels associated with the Channel.
func (c *Channel) SubChans() map[int64]string {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return copyMap(c.subChans)
}

// NestedChans returns a copy of the map of nested channels associated with the Channel.
func (c *Channel) NestedChans() map[int64]string {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return copyMap(c.nestedChans)
}

// Trace returns a copy of the Channel's trace data.
func (c *Channel) Trace() *ChannelTrace {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return c.trace.copy()
}

// ChannelMetrics holds connectivity state, target and call metrics for the
// channel within channelz.
type ChannelMetrics struct {
	// The current connectivity state of the channel.
	State atomic.Pointer[connectivity.State]

Review comment (suggestion):
pls wrap at 80 col:

```suggestion
// SubChans returns a copy of the map of sub-channels associated with the
// Channel.
```

Code after:
	// Non-zero traceRefCount means the trace of this channel cannot be deleted.
	traceRefCount int32

	// ChannelMetrics holds connectivity state, target and call metrics for the
	// channel within channelz.
	ChannelMetrics ChannelMetrics
}

// Implemented to make Channel implement the Identifier interface used for
// nesting.
func (c *Channel) channelzIdentifier() {}

// String returns a string representation of the Channel, including its parent
// entity and ID.
func (c *Channel) String() string {
	if c.Parent == nil {
		return fmt.Sprintf("Channel #%d", c.ID)
	}
	return fmt.Sprintf("%s Channel #%d", c.Parent, c.ID)
}

func (c *Channel) id() int64 {
	return c.ID
}

// SubChans returns a copy of the map of sub-channels associated with the
// Channel.
func (c *Channel) SubChans() map[int64]string {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return copyMap(c.subChans)
}

// NestedChans returns a copy of the map of nested channels associated with the
// Channel.
func (c *Channel) NestedChans() map[int64]string {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return copyMap(c.nestedChans)
}

// Trace returns a copy of the Channel's trace data.
func (c *Channel) Trace() *ChannelTrace {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return c.trace.copy()
}

// ChannelMetrics holds connectivity state, target and call metrics for the
// channel within channelz.
type ChannelMetrics struct {
Repository: grpc/grpc-go
File: test/xds/xds_client_custom_dialer_test.go (Go)

Code before:

	// Configure client side xDS resources on the management server.
	const serviceName = "my-service-client-side-xds"
	resources := e2e.DefaultClientResources(e2e.ResourceParams{
		DialTarget: serviceName,
		NodeID:     nodeID,
		Host:       "localhost",
		Port:       testutils.ParsePort(t, server.Address),
		SecLevel:   e2e.SecurityLevelNone,
	})
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	if err := mgmtServer.Update(ctx, resources); err != nil {
		t.Fatal(err)
	}

	// Create a ClientConn and make a successful RPC. The insecure transport credentials passed into
	// the gRPC.NewClient is the credentials for the data plane communication with the test backend.
	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder))
	if err != nil {
		t.Fatalf("failed to dial local test server: %v", err)
	}
	defer cc.Close()

	client := testgrpc.NewTestServiceClient(cc)
	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
		t.Fatalf("EmptyCall() failed: %v", err)
	}

	if !customDialerCalled {
		t.Fatalf("xDS client transport custom dialer called = false, want true")
	}
}


Review comment (bug):
I don't think (?) this should require `WaitForReady` -- we shouldn't be expecting connections to fail as we start the server before the client.

Code after:
		Port:       testutils.ParsePort(t, server.Address),
		SecLevel:   e2e.SecurityLevelNone,
	})
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	if err := mgmtServer.Update(ctx, resources); err != nil {
		t.Fatal(err)
	}

	// Create a ClientConn and make a successful RPC. The insecure transport credentials passed into
	// the gRPC.NewClient is the credentials for the data plane communication with the test backend.
	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder))
	if err != nil {
		t.Fatalf("failed to dial local test server: %v", err)
	}
	defer cc.Close()

	client := testgrpc.NewTestServiceClient(cc)
	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
		t.Fatalf("EmptyCall() failed: %v", err)
	}

	// Verify that the custom dialer was called.
	select {
	case <-ctx.Done():
		t.Fatalf("Timeout when waiting for custom dialer to be called")
	case <-credsBuilder.dialerCalled:
	}
}
Repository: grpc/grpc-go
File: internal/credentials/xds/handshake_info_test.go (Go)

Code before:
		{
			desc: "contains match ignore case",
			sanMatchers: []matcher.StringMatcher{
				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("GRPC"), nil, true),
			},
		},
	}

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			hi := NewHandshakeInfo(nil, nil, test.sanMatchers, false)

			if !hi.MatchingSANExists(inputCert) {
				t.Fatalf("hi.MatchingSANExists(%+v) with SAN matchers +%v failed when expected to succeed", inputCert, test.sanMatchers)
			}
		})
	}
}

func newStringP(s string) *string {
	return &s
}

func TestEqual(t *testing.T) {
	mockProvider1 := &mockCertProvider{id: 1}
	mockProvider2 := &mockCertProvider{id: 2}

	tests := []struct {
		desc      string
		hi1       *HandshakeInfo
		hi2       *HandshakeInfo
		wantMatch bool
	}{
		{
			desc:      "both HandshakeInfo are nil",
			hi1:       nil,
			hi2:       nil,
			wantMatch: true,
		},
		{
			desc:      "one HandshakeInfo is nil",
			hi1:       nil,
			hi2:       NewHandshakeInfo(mockProvider1, nil, nil, false),
			wantMatch: false,
		},
		{
			desc:      "different root providers",
			hi1:       NewHandshakeInfo(mockProvider1, nil, nil, false),
			hi2:       NewHandshakeInfo(mockProvider2, nil, nil, false),
			wantMatch: false,
		},

Review comment (suggestion):
Can we please avoid declaring variables outside of the `t.Run()` block. Currently the way things stand, these variables are shared by the different sub-tests and that makes them non-hermetic. Just inline these wherever required.

Code after:

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			hi := NewHandshakeInfo(nil, nil, test.sanMatchers, false)

			if !hi.MatchingSANExists(inputCert) {
				t.Fatalf("hi.MatchingSANExists(%+v) with SAN matchers +%v failed when expected to succeed", inputCert, test.sanMatchers)
			}
		})
	}
}

func newStringP(s string) *string {
	return &s
}

func TestEqual(t *testing.T) {
	tests := []struct {
		desc      string
		hi1       *HandshakeInfo
		hi2       *HandshakeInfo
		wantMatch bool
	}{
		{
			desc:      "both HandshakeInfo are nil",
			hi1:       nil,
			hi2:       nil,
			wantMatch: true,
		},
		{
			desc:      "one HandshakeInfo is nil",
			hi1:       nil,
			hi2:       NewHandshakeInfo(&testCertProvider{}, nil, nil, false),
			wantMatch: false,
		},
		{
			desc:      "different root providers",
			hi1:       NewHandshakeInfo(&testCertProvider{}, nil, nil, false),
			hi2:       NewHandshakeInfo(&testCertProvider{}, nil, nil, false),
			wantMatch: false,
		},
		{
			desc: "same providers, same SAN matchers",
			hi1: NewHandshakeInfo(testCertProvider{}, testCertProvider{}, []matcher.StringMatcher{
				matcher.StringMatcherForTesting(newStringP("foo.com"), nil, nil, nil, nil, false),
			}, false),
			hi2: NewHandshakeInfo(testCertProvider{}, testCertProvider{}, []matcher.StringMatcher{
				matcher.StringMatcherForTesting(newStringP("foo.com"), nil, nil, nil, nil, false),
			}, false),
			wantMatch: true,
		},