github.com/tencent/goom@v1.0.1/builder.go (about)

     1  // Package mocker 定义了 mock 的外层用户使用 API 定义,
     2  // 包括函数、方法、接口、未导出函数(或方法的)的 Mocker 的实现。
     3  // 当前文件实现了 Mocker 接口各实现类的构造链创建,以便通过链式构造一个 Mocker 对象。
     4  package mocker
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"runtime"
    10  	"strings"
    11  
    12  	"github.com/tencent/goom/internal/iface"
    13  	"github.com/tencent/goom/internal/logger"
    14  	"github.com/tencent/goom/internal/patch"
    15  )
    16  
    17  // Builder Mock 构建器, 负责创建一个链式构造器.
    18  type Builder struct {
    19  	pkgName string
    20  	mockers map[interface{}]Mocker
    21  }
    22  
    23  // Pkg 指定包名,当前包无需指定
    24  // 对于跨包目录的私有函数的 mock 通常都是因为代码设计可能有问题, 此功能会在未来版本中移除
    25  // 后续仅支持同包下的未导出方法的 mock
    26  // Deprecated: 对于跨包目录的私有函数的 mock 通常都是因为代码设计可能有问题
    27  func (b *Builder) Pkg(name string) *Builder {
    28  	b.pkgName = name
    29  	return b
    30  }
    31  
    32  // PkgName 返回包名
    33  // Deprecated: 对于跨包目录的私有函数的 mock 通常都是因为代码设计可能有问题
    34  func (b *Builder) PkgName() string {
    35  	return b.pkgName
    36  }
    37  
    38  // Create 创建 Mock 构建器
    39  // 非线程安全的,不能在多协程中并发地 mock 或 reset 同一个函数
    40  func Create() *Builder {
    41  	// callerDeps 当前的调用栈栈层次
    42  	const callerDeps = 2
    43  	return &Builder{
    44  		pkgName: currentPkg(callerDeps),
    45  		mockers: make(map[interface{}]Mocker, 30),
    46  	}
    47  }
    48  
    49  // Interface 指定接口类型的变量定义
    50  // iFace 必须是指针类型, 比如 i 为 interface 类型变量, iFace 传递&i
    51  func (b *Builder) Interface(iFace interface{}) *CachedInterfaceMocker {
    52  	mKey := reflect.TypeOf(iFace).String()
    53  	if mocker, ok := b.mockers[mKey]; ok && !mocker.Canceled() {
    54  		b.reset2CurPkg()
    55  		return mocker.(*CachedInterfaceMocker)
    56  	}
    57  
    58  	// 创建 InterfaceMocker
    59  	// context 和 interface 类型绑定
    60  	mocker := NewDefaultInterfaceMocker(b.pkgName, iFace, iface.NewContext())
    61  	cachedMocker := NewCachedInterfaceMocker(mocker)
    62  	b.cache(mKey, cachedMocker)
    63  	b.reset2CurPkg()
    64  	return cachedMocker
    65  }
    66  
    67  // cache 添加到缓存
    68  func (b *Builder) cache(mKey interface{}, cachedMocker Mocker) {
    69  	b.mockers[mKey] = cachedMocker
    70  }
    71  
    72  // Struct 指定结构体实例
    73  // 比如需要 mock 结构体函数 (*conn).Write(b []byte),则 name="conn"
    74  func (b *Builder) Struct(instance interface{}) *CachedMethodMocker {
    75  	mKey := reflect.ValueOf(instance).Type().String()
    76  	if mocker, ok := b.mockers[mKey]; ok && !mocker.Canceled() {
    77  		b.reset2CurPkg()
    78  		return mocker.(*CachedMethodMocker)
    79  	}
    80  
    81  	mocker := NewMethodMocker(b.pkgName, instance)
    82  	cachedMocker := NewCachedMethodMocker(mocker)
    83  	b.cache(mKey, cachedMocker)
    84  	b.reset2CurPkg()
    85  	return cachedMocker
    86  }
    87  
    88  // Func 指定函数定义
    89  // funcDef 函数,比如 foo
    90  // 方法的 mock, 比如 &Struct{}.method
    91  func (b *Builder) Func(funcDef interface{}) *DefMocker {
    92  	funcPointer := reflect.ValueOf(funcDef).Pointer()
    93  	key := runtime.FuncForPC(funcPointer).Name()
    94  	// 对于包含泛型参数的函数,可以附加函数指针作为key来区分不同泛型变量类型的函数
    95  	if patch.IsGenericsFunc(key) {
    96  		key = key + fmt.Sprintf("-%x", funcPointer)
    97  	}
    98  	if mocker, ok := b.mockers[key]; ok && !mocker.Canceled() {
    99  		b.reset2CurPkg()
   100  		return mocker.(*DefMocker)
   101  	}
   102  
   103  	mocker := NewDefMocker(b.pkgName, funcDef)
   104  	b.cache(key, mocker)
   105  	b.reset2CurPkg()
   106  	return mocker
   107  }
   108  
   109  // ExportStruct 导出私有结构体
   110  // 比如需要 mock 结构体函数 (*conn).Write(b []byte),则 name="conn"
   111  func (b *Builder) ExportStruct(name string) *CachedUnexportedMethodMocker {
   112  	if mocker, ok := b.mockers[b.pkgName+"_"+name]; ok && !mocker.Canceled() {
   113  		b.reset2CurPkg()
   114  		return mocker.(*CachedUnexportedMethodMocker)
   115  	}
   116  
   117  	structName := name
   118  	if strings.Contains(name, "*") {
   119  		structName = fmt.Sprintf("(%s)", name)
   120  	}
   121  
   122  	mocker := NewUnexportedMethodMocker(b.pkgName, structName)
   123  	cachedMocker := NewCachedUnexportedMethodMocker(mocker)
   124  	b.cache(b.pkgName+"_"+name, cachedMocker)
   125  	b.reset2CurPkg()
   126  	return cachedMocker
   127  }
   128  
   129  // ExportFunc 导出私有函数
   130  // 比如需要 mock 函数 foo(), 则 name="pkg_name.foo"
   131  // 比如需要 mock 方法, pkg_name.(*struct_name).method_name
   132  // name string foo 或者(*struct_name).method_name
   133  func (b *Builder) ExportFunc(name string) *UnexportedFuncMocker {
   134  	if name == "" {
   135  		panic("func name is empty")
   136  	}
   137  
   138  	if mocker, ok := b.mockers[b.pkgName+"_"+name]; ok && !mocker.Canceled() {
   139  		b.reset2CurPkg()
   140  		return mocker.(*UnexportedFuncMocker)
   141  	}
   142  
   143  	mocker := NewUnexportedFuncMocker(b.pkgName, name)
   144  	b.cache(b.pkgName+"_"+name, mocker)
   145  	b.reset2CurPkg()
   146  	return mocker
   147  }
   148  
   149  // Var 变量 mock, target 类型必须传递指针类型
   150  func (b *Builder) Var(v interface{}) VarMock {
   151  	cacheKey := fmt.Sprintf("var_%d", reflect.ValueOf(v).Pointer())
   152  	if mocker, ok := b.mockers[cacheKey]; ok && !mocker.Canceled() {
   153  		return mocker.(VarMock)
   154  	}
   155  
   156  	mocker := NewVarMocker(v)
   157  	b.cache(cacheKey, mocker)
   158  	return mocker
   159  }
   160  
   161  // Reset 取消当前 builder 的所有 Mock
   162  func (b *Builder) Reset() *Builder {
   163  	for _, mocker := range b.mockers {
   164  		mocker.Cancel()
   165  		// callerDeps 当前的调用栈栈层次
   166  		const callerDeps = 5
   167  		logger.Consolefc(logger.DebugLevel, "mockers [%s] resets.", logger.Caller(callerDeps), mocker.String())
   168  	}
   169  	return b
   170  }
   171  
   172  // reset2CurPkg 设置回当前的包
   173  func (b *Builder) reset2CurPkg() {
   174  	b.pkgName = currentPackage()
   175  }
   176  
   177  // currentPackage 获取当前调用的包路径
   178  func currentPackage() string {
   179  	// callerDeps 当前的调用栈栈层次
   180  	const callerDeps = 4
   181  	return currentPkg(callerDeps)
   182  }
   183  
   184  // currentPkg 获取调用者的包路径
   185  func currentPkg(skip int) string {
   186  	pc, _, _, _ := runtime.Caller(skip)
   187  	callerName := runtime.FuncForPC(pc).Name()
   188  
   189  	if i := strings.Index(callerName, ".("); i > -1 {
   190  		return callerName[:i]
   191  	}
   192  
   193  	if i := strings.LastIndex(callerName, "/"); i > -1 {
   194  		realIndex := strings.Index(callerName[i:len(callerName)-1], ".")
   195  		return callerName[:realIndex+i]
   196  	}
   197  
   198  	realIndex := strings.Index(callerName, ".")
   199  	return callerName[:realIndex]
   200  }
   201  
   202  // OpenDebug 开启 debug 模式
   203  // 也可以通过添加环境变量开启 debug: GOOM_DEBUG=true
   204  // 1.可以查看 apply 和 reset 的状态日志
   205  // 2.查看 mock 调用日志
   206  func OpenDebug() {
   207  	logger.OpenDebug()
   208  }
   209  
   210  // CloseDebug 关闭 debug 模式
   211  func CloseDebug() {
   212  	logger.CloseDebug()
   213  }
   214  
   215  // OpenTrace 打开日志跟踪
   216  func OpenTrace() {
   217  	logger.OpenTrace()
   218  }
   219  
   220  // CloseTrace 关闭日志跟踪
   221  func CloseTrace() {
   222  	logger.CloseTrace()
   223  }