
接着我们看下第二种情况也就是非根上下文的场景,第一步也是通过discovery获取参数列表,初始化suit,这里和根context一样。
func (ctx *context) Convey(items ...interface{}) {
entry := discover(items)
// we're a branch, or leaf (on the wind)
if entry.Test != nil {
conveyPanic(extraGoTest)
}
if ctx.focus && !entry.Focus {
return
}
var inner_ctx *context
if ctx.executedOnce {
var ok bool
inner_ctx, ok = ctx.children[entry.Situation]
if !ok {
conveyPanic(differentConveySituations, entry.Situation)
}
} else {
if _, ok := ctx.children[entry.Situation]; ok {
conveyPanic(multipleIdenticalConvey, entry.Situation)
}
inner_ctx = &context{
reporter: ctx.reporter,
children: make(map[string]*context),
expectChildRun: ctx.expectChildRun,
focus: entry.Focus,
failureMode: ctx.failureMode.combine(entry.FailMode),
stackMode: ctx.stackMode.combine(entry.StackMode),
}
ctx.children[entry.Situation] = inner_ctx
}
if inner_ctx.shouldVisit() {
ctxMgr.SetValues(gls.Values{nodeKey: inner_ctx}, func() {
inner_ctx.conveyInner(entry.Situation, entry.Func)
})
}
}然后通过用例名称获取子上下文,最后复制给当前context的children字段,接着就是把调用作为value存在tls存储中。
接着看下So方法,它先获取当前上下文,然后调用上下文的So方法
func So(actual interface{}, assert Assertion, expected ...interface{}) {
mustGetCurrentContext().So(actual, assert, expected...)
}如果获取上下文失败就panic,说明So必须在Convey内部,获取上下文和调用Convey的时候用的同一个方法:
func mustGetCurrentContext() *context {
ctx := getCurrentContext()
if ctx == nil {
conveyPanic(noStackContext)
}
return ctx
}接着看下具体的So方法,如果成功就上报成功,失败上报堆栈:
func (ctx *context) So(actual interface{}, assert Assertion, expected ...interface{}) {
if result := assert(actual, expected...); result == assertionSuccess {
ctx.assertionReport(reporting.NewSuccessReport())
} else {
ctx.assertionReport(reporting.NewFailureReport(result, ctx.shouldShowStack()))
}
}其中的Assertion是一个返回string的方法,如果成功的话返回空
type Assertion func(actual interface{}, expected ...interface{}) stringconst assertionSuccess = ""除了So方法还有SoMsg和SkipSo方法
func SoMsg(msg string, actual interface{}, assert Assertion, expected ...interface{}) {
mustGetCurrentContext().SoMsg(msg, actual, assert, expected...)
}func SkipSo(stuff ...interface{}) {
mustGetCurrentContext().SkipSo()
}前者在So的基础上多了一个提示msg,会上报这个msg
func (ctx *context) SoMsg(msg string, actual interface{}, assert Assertion, expected ...interface{}) {
if result := assert(actual, expected...); result == assertionSuccess {
ctx.assertionReport(reporting.NewSuccessReport())
return
} else {
ctx.reporter.Enter(reporting.NewScopeReport(msg))
defer ctx.reporter.Exit()
ctx.assertionReport(reporting.NewFailureReport(result, ctx.shouldShowStack()))
}
}后者不执行直接上报
func (ctx *context) SoMsg(msg string, actual interface{}, assert Assertion, expected ...interface{}) {
if result := assert(actual, expected...); result == assertionSuccess {
ctx.assertionReport(reporting.NewSuccessReport())
return
} else {
ctx.reporter.Enter(reporting.NewScopeReport(msg))
defer ctx.reporter.Exit()
ctx.assertionReport(reporting.NewFailureReport(result, ctx.shouldShowStack()))
}
}Reset方法就简单了,直接方法入队列
func (ctx *context) Reset(action func()) {
/* TODO: Failure mode configuration */
ctx.resets = append(ctx.resets, action)
}需要注意的是,ctx的SetValue和普通的的Set不同,它在set的时候会执行传入的函数,子ctx会继承父上下文的值并copy到当前上下文,这样就做到了不同上下文的隔离,如果没有传入值,就直接执行函数。
func (m *ContextManager) SetValues(new_values Values, context_call func()) {
if len(new_values) == 0 {
context_call()
return
}
mutated_keys := make([]interface{}, 0, len(new_values))
mutated_vals := make(Values, len(new_values))
EnsureGoroutineId(func(gid uint) {
m.mtx.Lock()
state, found := m.values[gid]
if !found {
state = make(Values, len(new_values))
m.values[gid] = state
}
m.mtx.Unlock()
for key, new_val := range new_values {
mutated_keys = append(mutated_keys, key)
if old_val, ok := state[key]; ok {
mutated_vals[key] = old_val
}
state[key] = new_val
}
defer func() {
if !found {
m.mtx.Lock()
delete(m.values, gid)
m.mtx.Unlock()
return
}
for _, key := range mutated_keys {
if val, ok := mutated_vals[key]; ok {
state[key] = val
} else {
delete(state, key)
}
}
}()
context_call()
})
}去过获取groutine Id成功直接执行这个函数,否则,重新获取一个,然后执行。
func EnsureGoroutineId(cb func(gid uint)) {
if gid, ok := readStackTag(); ok {
cb(gid)
return
}
gid := stackTagPool.Acquire()
defer stackTagPool.Release(gid)
addStackTag(gid, func() { cb(gid) })
}通过封装保证了平级的Convey间相互隔离互不影响。
本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!