Skip to content

Commit

Permalink
Separate test helpers from tests
Browse files Browse the repository at this point in the history
  • Loading branch information
g41797 committed Jul 12, 2023
1 parent 5e34de0 commit 37fbb56
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 241 deletions.
121 changes: 121 additions & 0 deletions dumbBlock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package sputnik_test

import (
"github.com/g41797/kissngoqueue"
"github.com/g41797/sputnik"
)

// Satellite block
type dumbBlock struct {
// Block controller
bc sputnik.BlockController
// Main queue of test
q *kissngoqueue.Queue[sputnik.Msg]
// Used for synchronization
// between finish and run
// This pattern may be used in real application
stop chan struct{}
done chan struct{}
}

// Block factory:
func (tb *testBlocks) dbFact() *sputnik.Block {
dmb := new(dumbBlock)
tb.dbl = append(tb.dbl, dmb)
return sputnik.NewBlock(
sputnik.WithInit(dmb.init),
sputnik.WithRun(dmb.run),
sputnik.WithFinish(dmb.finish),

sputnik.WithOnMsg(dmb.eventReceived),
sputnik.WithOnConnect(dmb.serverConnected),
sputnik.WithOnDisConnect(dmb.serverDisConnected),
)
}

// dumbBlock support all callbacks of Block:
//
// Init
func (dmb *dumbBlock) init(cnf any) error {
dmb.stop = make(chan struct{}, 1)
return nil
}

// Run:
func (dmb *dumbBlock) run(bc sputnik.BlockController) {

// Save controller for further communication
// with blocks
dmb.bc = bc

dmb.done = make(chan struct{})
defer close(dmb.done)

// select isn't required for one channel
// in real application you can add "listening"
// on another channels here e.g. timeouts or
// redirected OnMsg|OnConnect|etc
select {
case <-dmb.stop:
return
}

return
}

// Finish:
func (dmb *dumbBlock) finish(init bool) {
close(dmb.stop) // Cancel Run

if init {
return
}

select {
case <-dmb.done: // Wait finish of Run
return
}
return
}

// OnServerConnect:
func (dmb *dumbBlock) serverConnected(connection any) {
//Inform test about event
m := make(sputnik.Msg)
m["__name"] = "serverConnected"
dmb.send(m)
return
}

// OnServerDisconnect:
func (dmb *dumbBlock) serverDisConnected() {
//Inform test about event
m := make(sputnik.Msg)
m["__name"] = "serverDisConnected"
dmb.send(m)
return
}

// OnMsg:
func (dmb *dumbBlock) eventReceived(msg sputnik.Msg) {
//Inform test about event
dmb.send(msg)
return
}

func (dmb *dumbBlock) send(msg sputnik.Msg) {
if dmb.q != nil {
//Send message to test
dmb.q.PutMT(msg)
}
return
}

func dumbSputnik(tb *testBlocks) sputnik.Sputnik {
sp, _ := sputnik.NewSputnik(
sputnik.WithConfFactory(dumbConf),
sputnik.WithAppBlocks(blkList),
sputnik.WithBlockFactories(tb.factories()),
)
return *sp
}
241 changes: 0 additions & 241 deletions sputnik_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,250 +4,9 @@ import (
"testing"
"time"

"github.com/g41797/kissngoqueue"
"github.com/g41797/sputnik"
)

// Configuration factory:
func dumbConf() any { return nil }

// Satellite has 3 blocks:
var blkList []sputnik.BlockDescriptor = []sputnik.BlockDescriptor{
{"dumb", "1"},
{"dumb", "2"},
{"dumb", "3"},
}

// Satellite block
type dumbBlock struct {
// Block controller
bc sputnik.BlockController
// Main queue of test
q *kissngoqueue.Queue[sputnik.Msg]
// Used for synchronization
// between finish and run
// This pattern may be used in real application
stop chan struct{}
done chan struct{}
}

// Block factory:
func (tb *testBlocks) dbFact() *sputnik.Block {
dmb := new(dumbBlock)
tb.dbl = append(tb.dbl, dmb)
return sputnik.NewBlock(
sputnik.WithInit(dmb.init),
sputnik.WithRun(dmb.run),
sputnik.WithFinish(dmb.finish),

sputnik.WithOnMsg(dmb.eventReceived),
sputnik.WithOnConnect(dmb.serverConnected),
sputnik.WithOnDisConnect(dmb.serverDisConnected),
)
}

// dumbBlock support all callbacks of Block:
//
// Init
func (dmb *dumbBlock) init(cnf any) error {
dmb.stop = make(chan struct{}, 1)
return nil
}

// Run:
func (dmb *dumbBlock) run(bc sputnik.BlockController) {

// Save controller for further communication
// with blocks
dmb.bc = bc

dmb.done = make(chan struct{})
defer close(dmb.done)

// select isn't required for one channel
// in real application you can add "listening"
// on another channels here e.g. timeouts or
// redirected OnMsg|OnConnect|etc
select {
case <-dmb.stop:
return
}

return
}

// Finish:
func (dmb *dumbBlock) finish(init bool) {
close(dmb.stop) // Cancel Run

if init {
return
}

select {
case <-dmb.done: // Wait finish of Run
return
}
return
}

// OnServerConnect:
func (dmb *dumbBlock) serverConnected(connection any) {
//Inform test about event
m := make(sputnik.Msg)
m["__name"] = "serverConnected"
dmb.send(m)
return
}

// OnServerDisconnect:
func (dmb *dumbBlock) serverDisConnected() {
//Inform test about event
m := make(sputnik.Msg)
m["__name"] = "serverDisConnected"
dmb.send(m)
return
}

// OnMsg:
func (dmb *dumbBlock) eventReceived(msg sputnik.Msg) {
//Inform test about event
dmb.send(msg)
return
}

func (dmb *dumbBlock) send(msg sputnik.Msg) {
if dmb.q != nil {
//Send message to test
dmb.q.PutMT(msg)
}
return
}

func dumbSputnik(tb *testBlocks) sputnik.Sputnik {
sp, _ := sputnik.NewSputnik(
sputnik.WithConfFactory(dumbConf),
sputnik.WithAppBlocks(blkList),
sputnik.WithBlockFactories(tb.factories()),
)
return *sp
}

// Test helper:
type testBlocks struct {
// All blocks
dbl []*dumbBlock
// Test queue
q *kissngoqueue.Queue[sputnik.Msg]
// Launcher
launch sputnik.Launch
// ShootDown
kill sputnik.ShootDown
// Signalling channel
done chan struct{}
}

func NewTestBlocks() *testBlocks {
tb := new(testBlocks)
tb.q = kissngoqueue.NewQueue[sputnik.Msg]()
return tb
}

// Expectation:
// - get n messages from blocks
// - with "__name" == <name>
func (tb *testBlocks) expect(n int, name string) bool {
for i := 0; i < n; i++ {
msg, ok := tb.q.Get()
if !ok {
return false
}

mn, exists := msg["__name"]

if !exists {
return false
}

mname, ok := mn.(string)

if !ok {
return false
}

if mname != name {
return false
}

}

return true
}

// Send msg to block using it's responsibility
// Use this pattern in real application for
// negotiation between blocks
func (tb *testBlocks) sendTo(resp string, msg sputnik.Msg) bool {
cn := tb.dbl[0].bc
bc, exists := cn.Controller(resp)

if !exists {
return false
}
sok := bc.Send(msg)
return sok
}

func (tb *testBlocks) mainCntrl() sputnik.BlockController {
mcn, _ := tb.dbl[0].bc.Controller(sputnik.InitiatorResponsibility)
return mcn
}

// Run Launcher on dedicated goroutine
// Test controls execution via sputnik API
// Results received using queue
func (tb *testBlocks) run() {
tb.done = make(chan struct{})

go func(l sputnik.Launch, done chan struct{}) {
defer close(done)
l()
}(tb.launch, tb.done)

return
}

// Registration of factories for test environment
// For this case init() isn't used
// use this pattern for the case when you don't need
// dynamic registration: all blocks (and factories) are
// known in advance.
func (tb *testBlocks) factories() sputnik.BlockFactories {
res := make(sputnik.BlockFactories)

finfct, _ := sputnik.Factory(sputnik.DefaultFinisherName)

factList := []struct {
name string
fact sputnik.BlockFactory
}{
{"dumb", tb.dbFact},
{"finisher", finfct},
}

for _, fd := range factList {
sputnik.RegisterBlockFactoryInner(fd.name, fd.fact, res)
}
return res
}

func (tb *testBlocks) attachQueue() {
for i, _ := range tb.dbl {
tb.dbl[i].q = tb.q
}
return
}

func TestPrepare(t *testing.T) {

tb := NewTestBlocks()
Expand Down
Loading

0 comments on commit 37fbb56

Please sign in to comment.