From 3a9b545b0f33435c277fcede2251e4b5ae800d40 Mon Sep 17 00:00:00 2001 From: Mission Liao Date: Wed, 15 Aug 2018 14:04:35 +0800 Subject: test: refine test utility (#61) * Add functionality to test.App * Add test utility to generate slices of types.ValidatorID --- core/test/app.go | 158 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 142 insertions(+), 16 deletions(-) (limited to 'core/test/app.go') diff --git a/core/test/app.go b/core/test/app.go index 55ba7c5..aedcba9 100644 --- a/core/test/app.go +++ b/core/test/app.go @@ -18,44 +18,79 @@ package test import ( + "fmt" + "sync" "time" "github.com/dexon-foundation/dexon-consensus-core/common" ) +var ( + // ErrEmptyDeliverSequence means there is no delivery event in this App + // instance. + ErrEmptyDeliverSequence = fmt.Errorf("emptry deliver sequence") + // ErrMismatchBlockHashSequence means the delivering sequence between two App + // instances are different. + ErrMismatchBlockHashSequence = fmt.Errorf("mismatch block hash sequence") + // ErrMismatchConsensusTime means the consensus timestamp between two blocks + // with the same hash from two App instances are different. + ErrMismatchConsensusTime = fmt.Errorf("mismatch consensus time") + // ErrApplicationIntegrityFailed means the internal datum in a App instance + // is not integrated. + ErrApplicationIntegrityFailed = fmt.Errorf("application integrity failed") + // ErrConsensusTimestampOutOfOrder means the later delivered block has + // consensus timestamp older than previous block. + ErrConsensusTimestampOutOfOrder = fmt.Errorf( + "consensus timestamp out of order") + // ErrDeliveredBlockNotAcked means some block delivered (confirmed) but + // not strongly acked. + ErrDeliveredBlockNotAcked = fmt.Errorf("delivered block not acked") + // ErrMismatchTotalOrderingAndDelivered mean the sequence of total ordering + // and delivered are different. + ErrMismatchTotalOrderingAndDelivered = fmt.Errorf( + "mismatch total ordering and delivered sequence") +) + +type totalOrderDeliver struct { + BlockHashes common.Hashes + Early bool +} + // App implements Application interface for testing purpose. type App struct { - Acked map[common.Hash]struct{} - TotalOrdered []*struct { - BlockHashes common.Hashes - Early bool - } - Delivered map[common.Hash]time.Time + Acked map[common.Hash]struct{} + ackedLock sync.RWMutex + TotalOrdered []*totalOrderDeliver + totalOrderedLock sync.RWMutex + Delivered map[common.Hash]time.Time + DeliverSequence common.Hashes + deliveredLock sync.RWMutex } // NewApp constructs a TestApp instance. func NewApp() *App { return &App{ - Acked: make(map[common.Hash]struct{}), - TotalOrdered: []*struct { - BlockHashes common.Hashes - Early bool - }{}, - Delivered: make(map[common.Hash]time.Time), + Acked: make(map[common.Hash]struct{}), + TotalOrdered: []*totalOrderDeliver{}, + Delivered: make(map[common.Hash]time.Time), + DeliverSequence: common.Hashes{}, } } // StronglyAcked implements Application interface. func (app *App) StronglyAcked(blockHash common.Hash) { + app.ackedLock.Lock() + defer app.ackedLock.Unlock() + app.Acked[blockHash] = struct{}{} } // TotalOrderingDeliver implements Application interface. func (app *App) TotalOrderingDeliver(blockHashes common.Hashes, early bool) { - app.TotalOrdered = append(app.TotalOrdered, &struct { - BlockHashes common.Hashes - Early bool - }{ + app.totalOrderedLock.Lock() + defer app.totalOrderedLock.Unlock() + + app.TotalOrdered = append(app.TotalOrdered, &totalOrderDeliver{ BlockHashes: blockHashes, Early: early, }) @@ -63,5 +98,96 @@ func (app *App) TotalOrderingDeliver(blockHashes common.Hashes, early bool) { // DeliverBlock implements Application interface. func (app *App) DeliverBlock(blockHash common.Hash, timestamp time.Time) { + app.deliveredLock.Lock() + defer app.deliveredLock.Unlock() + app.Delivered[blockHash] = timestamp + app.DeliverSequence = append(app.DeliverSequence, blockHash) +} + +// Compare performs these checks against another App instance +// and return erros if not passed: +// - deliver sequence by comparing block hashes. +// - consensus timestamp of each block are equal. +func (app *App) Compare(other *App) error { + app.deliveredLock.RLock() + defer app.deliveredLock.RUnlock() + other.deliveredLock.RLock() + defer other.deliveredLock.RUnlock() + + minLength := len(app.DeliverSequence) + if minLength > len(other.DeliverSequence) { + minLength = len(other.DeliverSequence) + } + if minLength == 0 { + return ErrEmptyDeliverSequence + } + for idx, h := range app.DeliverSequence[:minLength] { + hOther := other.DeliverSequence[idx] + if hOther != h { + return ErrMismatchBlockHashSequence + } + if app.Delivered[h] != other.Delivered[h] { + return ErrMismatchConsensusTime + } + } + return nil +} + +// Verify checks the integrity of date received by this App instance. +func (app *App) Verify() error { + app.deliveredLock.RLock() + defer app.deliveredLock.RUnlock() + + if len(app.DeliverSequence) == 0 { + return ErrEmptyDeliverSequence + } + if len(app.DeliverSequence) != len(app.Delivered) { + return ErrApplicationIntegrityFailed + } + + app.ackedLock.RLock() + defer app.ackedLock.RUnlock() + + prevTime := time.Time{} + for _, h := range app.DeliverSequence { + // Make sure delivered block is strongly acked. + if _, acked := app.Acked[h]; !acked { + return ErrDeliveredBlockNotAcked + } + t, exists := app.Delivered[h] + if !exists { + return ErrApplicationIntegrityFailed + } + // Make sure the consensus time is incremental. + ok := prevTime.Before(t) || prevTime.Equal(t) + if !ok { + return ErrConsensusTimestampOutOfOrder + } + prevTime = t + } + // Make sure the order of delivered and total ordering are the same by + // comparing the concated string. + app.totalOrderedLock.RLock() + defer app.totalOrderedLock.RUnlock() + + hashSequenceIdx := 0 +Loop: + for _, totalOrderDeliver := range app.TotalOrdered { + for _, h := range totalOrderDeliver.BlockHashes { + if hashSequenceIdx >= len(app.DeliverSequence) { + break Loop + } + if h != app.DeliverSequence[hashSequenceIdx] { + return ErrMismatchTotalOrderingAndDelivered + } + hashSequenceIdx++ + } + } + if hashSequenceIdx != len(app.DeliverSequence) { + // The count of delivered blocks should be larger than those delivered + // by total ordering. + return ErrMismatchTotalOrderingAndDelivered + } + return nil } -- cgit v1.2.3