Add tparallel task
This commit is contained in:
parent
f96155ec96
commit
43cf8dcd0e
3 changed files with 242 additions and 0 deletions
23
tparallel/README.md
Normal file
23
tparallel/README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# tparallel
|
||||||
|
|
||||||
|
Реализуйте api, дублирующее поведение `t.Parallel()`.
|
||||||
|
|
||||||
|
Тест - это функция с сигнатурой `func(*T)`. Вам нужно реализовать функцию `Run(topTests []func(*T))`,
|
||||||
|
запускающую множество top-level тестов.
|
||||||
|
|
||||||
|
Каждый тест получает аргументом уникальный объект `*T`.
|
||||||
|
|
||||||
|
В начате все тесты выполняются последовательно. Если тест вызывает `t.Parallel()`, то он становится
|
||||||
|
паралельным и блокируется на этом вызове. После этого, запускается следующий последовательный тест.
|
||||||
|
|
||||||
|
Когда все последовательные тесты завершились, все паралельные тесты разблокируются и продолжают своё
|
||||||
|
исполнения.
|
||||||
|
|
||||||
|
Функция `Run` выходит, когда завершились все тесты.
|
||||||
|
|
||||||
|
Тест может запускать под-тесты вызывая `t.Run`. Тест считается завершённым, когда завершился он сам и
|
||||||
|
завершились все его подтесты.
|
||||||
|
|
||||||
|
Под-тест начинает своё выполнение последовательно, блокирую вызов Run. Подтест может стать параллельным,
|
||||||
|
вызвав `t.Parallel()`. Такой тест должен продолжить исполнение, после того как функция родительского теста
|
||||||
|
завершилась.
|
18
tparallel/tparallel.go
Normal file
18
tparallel/tparallel.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// +build !solution
|
||||||
|
|
||||||
|
package tparallel
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *T) Parallel() {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *T) Run(subtest func(t *T)) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(topTests []func(t *T)) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
201
tparallel/tparallel_test.go
Normal file
201
tparallel/tparallel_test.go
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package tparallel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConcurrencyChecker struct {
|
||||||
|
t *testing.T
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
nextStage int
|
||||||
|
waitCount int
|
||||||
|
barrier chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConcurrencyChecker) Sequential(stage int) {
|
||||||
|
c.t.Helper()
|
||||||
|
c.t.Logf("Sequential(%d)", stage)
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if stage != c.nextStage {
|
||||||
|
c.t.Errorf("testing method is executed out of sequence: expected=%d, got=%d", c.nextStage, stage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.nextStage++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConcurrencyChecker) Parallel(stage, count int) {
|
||||||
|
c.t.Helper()
|
||||||
|
c.t.Logf("Parallel(%d, %d)", stage, count)
|
||||||
|
|
||||||
|
var barrier chan struct{}
|
||||||
|
|
||||||
|
func() {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if stage != c.nextStage {
|
||||||
|
c.t.Errorf("testing method is executed out of sequence: expected=%d, got=%d", c.nextStage, stage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.waitCount == 0 {
|
||||||
|
c.barrier = make(chan struct{})
|
||||||
|
}
|
||||||
|
barrier = c.barrier
|
||||||
|
|
||||||
|
c.waitCount++
|
||||||
|
|
||||||
|
if c.waitCount == count {
|
||||||
|
c.waitCount = 0
|
||||||
|
c.nextStage++
|
||||||
|
close(c.barrier)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-barrier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConcurrencyChecker) Finish(total int) {
|
||||||
|
if total != c.nextStage {
|
||||||
|
c.t.Errorf("wrong number of stages executed: expected=%d, got=%d", total, c.nextStage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequentialExecution(t *testing.T) {
|
||||||
|
check := &ConcurrencyChecker{t: t}
|
||||||
|
defer check.Finish(3)
|
||||||
|
|
||||||
|
Run([]func(*T){
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(0)
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(1)
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(2)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParallelExecution(t *testing.T) {
|
||||||
|
check := &ConcurrencyChecker{t: t}
|
||||||
|
defer check.Finish(4)
|
||||||
|
|
||||||
|
Run([]func(*T){
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(0)
|
||||||
|
t.Parallel()
|
||||||
|
check.Parallel(3, 3)
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(1)
|
||||||
|
t.Parallel()
|
||||||
|
check.Parallel(3, 3)
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(2)
|
||||||
|
t.Parallel()
|
||||||
|
check.Parallel(3, 3)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequentialSubTests(t *testing.T) {
|
||||||
|
check := &ConcurrencyChecker{t: t}
|
||||||
|
defer check.Finish(5)
|
||||||
|
|
||||||
|
Run([]func(*T){
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(0)
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Sequential(1)
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Sequential(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Sequential(3)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(4)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParallelGroup(t *testing.T) {
|
||||||
|
check := &ConcurrencyChecker{t: t}
|
||||||
|
defer check.Finish(17)
|
||||||
|
|
||||||
|
Run([]func(*T){
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(0)
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(1)
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Sequential(2)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Sequential(3 + i)
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
check.Parallel(14, 10)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
check.Sequential(13)
|
||||||
|
})
|
||||||
|
|
||||||
|
check.Sequential(15)
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(16)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoParallelSequences(t *testing.T) {
|
||||||
|
check := &ConcurrencyChecker{t: t}
|
||||||
|
defer check.Finish(4)
|
||||||
|
|
||||||
|
Run([]func(*T){
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(0)
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Parallel(2, 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Parallel(3, 2)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
func(t *T) {
|
||||||
|
check.Sequential(1)
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Parallel(2, 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run(func(t *T) {
|
||||||
|
check.Parallel(3, 2)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue