Source file src/crypto/internal/cryptotest/methods.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cryptotest
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"slices"
    11  	"testing"
    12  )
    13  
    14  // NoExtraMethods checks that the concrete type of *ms has no exported methods
    15  // beyond the methods of the interface type of *ms, and any others specified in
    16  // the allowed list.
    17  //
    18  // These methods are accessible through interface upgrades, so they end up part
    19  // of the API even if undocumented per Hyrum's Law.
    20  //
    21  // ms must be a pointer to a non-nil interface.
    22  func NoExtraMethods(t *testing.T, ms interface{}, allowed ...string) {
    23  	t.Helper()
    24  	extraMethods, err := extraMethods(ms)
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	for _, m := range extraMethods {
    29  		if slices.Contains(allowed, m) {
    30  			continue
    31  		}
    32  		t.Errorf("unexpected method %q", m)
    33  	}
    34  }
    35  
    36  func extraMethods(ip interface{}) ([]string, error) {
    37  	v := reflect.ValueOf(ip)
    38  	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Interface || v.Elem().IsNil() {
    39  		return nil, fmt.Errorf("argument must be a pointer to a non-nil interface")
    40  	}
    41  
    42  	interfaceType := v.Elem().Type()
    43  	concreteType := v.Elem().Elem().Type()
    44  
    45  	interfaceMethods := make(map[string]bool)
    46  	for i := range interfaceType.NumMethod() {
    47  		interfaceMethods[interfaceType.Method(i).Name] = true
    48  	}
    49  
    50  	var extraMethods []string
    51  	for i := range concreteType.NumMethod() {
    52  		m := concreteType.Method(i)
    53  		if !m.IsExported() {
    54  			continue
    55  		}
    56  		if !interfaceMethods[m.Name] {
    57  			extraMethods = append(extraMethods, m.Name)
    58  		}
    59  	}
    60  
    61  	return extraMethods, nil
    62  }
    63  

View as plain text