これは私をOCDに夢中にさせている。私は次の関数があるとします。保証の100%テストカバレッジのために、このselectステートメントを書き換えるにはどうすればよいですか?
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
}
}
}
これは、各値vのDSTにF(V)を送信SRCまたはquitのいずれかが閉じられ、空や値が終了から受信されるまで、SRCから受け取りました。
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
は、指定された時間後にchannelパラメータを閉じ、の後に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.なぜ上流チャネルが閉じていないとブロックしないのかが不思議であれば、これはOCDのもう1つのビットです。この関数はエクスポートされます。つまり、クライアントコードが正しく動作していないと、コードが正しく動作しません。私はそれが最初のバージョンであるよりも弾力的であることを望みます。
複数のテスト関数に分割します。また、テストしていない2つの終了ケースがあります: 'src'から受信し、' dst'_then_閉じる 'quit'を送信し、' src'が閉じられたので終了します。 https://play.golang.org/p/KI9OJLsHdc – Kaedys
これらはすでに他のテスト機能で処理されています。私が決して受信しないようにチャンネルを無制限に設定することによって、selectステートメントを確定的にトラバースすることができなかったので、この特定のものは私に適していました。 – burfl
正直言って、それらをnilに設定する必要はありません。受信を呼び出さないバッファーのないチャンネルを作ることは、チャンネルがゼロであることと機械的に同じです。バッファなしの 'dst'チャンネルを作成し、それを' Map'に渡して、 '<-dst'を実行するのに気を使うことがなければ、あなたのテストはまったく変わりません。 – Kaedys