这让我感到非常疯狂。假设我有以下功能:
func Map(quit <-chan struct{}, dst chan<- interface{}, src <-chan interface{}, f func(interface{}) interface{} {
for {
select {
case v, ok := <- src:
if !ok {
return
}
select {
case dst <- f(v):
case <-quit:
return
}
case <-quit:
return
}
}
}
它在dst上为从src接收的每个值v发送f(v),直到src或quit关闭并且为空或从quit接收到值。
现在,假设我想编写一个测试,证明它可以被取消:
func TestMapCancel(t *testing.T) {
var wg sync.WaitGroup
quit := make(chan struct{})
success := make(chan struct{})
wg.Add(3)
src := // channel providing arbitrary values until quit is closed
dst := make(chan interface{})
// mapper
go func() {
defer wg.Done()
defer close(dst)
Map(quit, dst, src, double)
}()
// provide a sink to consume values from dst until quit is closed
timeout(quit, 10*time.Millisecond)
wait(success, &wg)
select {
case <-success:
case <-time.After(100 * time.Millisecond):
t.Error("cancellation timed out")
}
}
这里未定义的功能并不是非常重要。请假设他们有效。 timeout
在指定的时间后关闭其频道参数,wait
在wg.Wait()
之后关闭其频道参数。
问题在于,它不能提供100%的覆盖率,因为如果两者都准备好发送/接收,则选择统一选择(伪)随机的情况。以下版本的Map
没有这个问题,但如果上游通道(src)没有关闭,则可能会无限期阻塞:
func Map(quit <-chan struct{}, dst chan<- interface{}, src <-chan interface{}, f func(interface{}) interface{}) {
for v := range src {
select {
case dst <- f(v):
case <-quit:
return
}
}
}
我可以通过重写测试在循环中重复几次来解决这个问题,这样每个分支都有机会随机选择。我已经尝试了10次迭代,并且在所有测试通过时达到了100%覆盖率(除此之外还有其他测试)。但是,如果上游渠道没有关闭并且保证给予100,那么我似乎无法编写一个不会阻止的世界最佳版本。 %测试覆盖率(不仅仅是可能)。
对我来说有什么灵感?
P.S。,如果你好奇为什么&#34;如果上游频道没有关闭则不会阻止&#34;很重要,它只是OCD的另一部分。导出此函数,这意味着如果客户端代码行为不正常,我的代码行为不端。我希望它比第一个版本更具弹性。
答案 0 :(得分:0)
好的,所以我感觉非常羞怯。当我看着这个时,我一定是被烧掉了。绝对有可能以确定的方式遍历那些选择语句:
func TestMapCancel(t *testing.T) {
src := make(chan interface{})
quit := make(chan struct{})
done := make(chan struct{})
go func() {
defer close(done)
Map(quit, nil, src, double)
}()
close(quit)
select {
case <-done:
case <-time.After(100 * time.Millisecond):
t.Error("quitting pre-send failed")
}
src = make(chan interface{})
quit = make(chan struct{})
done = make(chan struct{})
go func() {
defer close(done)
Map(quit, nil, src, double)
}()
src <- 1
close(quit)
select {
case <-done:
case <-time.After(100 * time.Millisecond):
t.Error("quitting pre-send failed")
}
}