aboutsummaryrefslogtreecommitdiffstats
path: root/core/syncer/watch-cat_test.go
blob: b1f1bf8189b77ec534de60a76b582c1e85798419 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2019 The dexon-consensus Authors
// This file is part of the dexon-consensus library.
//
// The dexon-consensus library is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.  //
// The dexon-consensus library is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the dexon-consensus library. If not, see
// <http://www.gnu.org/licenses/>.

package syncer

import (
    "sync"
    "testing"
    "time"

    "github.com/stretchr/testify/suite"

    "github.com/dexon-foundation/dexon-consensus/common"
    "github.com/dexon-foundation/dexon-consensus/core/types"
)

type WatchCatTestSuite struct {
    suite.Suite
}

type testConfigAccessor struct {
    notarySetSize uint32
}

func (cfg *testConfigAccessor) Configuration(uint64) *types.Config {
    return &types.Config{
        NotarySetSize: cfg.notarySetSize,
    }
}

type recovery struct {
    lock  sync.RWMutex
    votes map[uint64]uint64
}

func (rec *recovery) ProposeSkipBlock(height uint64) error {
    rec.lock.Lock()
    defer rec.lock.Unlock()
    rec.votes[height]++
    return nil
}

func (rec *recovery) Votes(height uint64) (uint64, error) {
    rec.lock.RLock()
    defer rec.lock.RUnlock()
    return rec.votes[height], nil
}

func (s *WatchCatTestSuite) newWatchCat(
    notarySetSize uint32, polling, timeout time.Duration) (*WatchCat, *recovery) {
    cfg := &testConfigAccessor{
        notarySetSize: notarySetSize,
    }
    recovery := &recovery{
        votes: make(map[uint64]uint64),
    }
    wc := NewWatchCat(recovery, cfg, polling, timeout, &common.NullLogger{})
    return wc, recovery
}

func (s *WatchCatTestSuite) TestBasicUsage() {
    polling := 50 * time.Millisecond
    timeout := 50 * time.Millisecond
    notarySet := uint32(24)
    watchCat, rec := s.newWatchCat(notarySet, polling, timeout)
    watchCat.Start()
    defer watchCat.Stop()
    pos := types.Position{
        Height: 10,
    }

    for i := 0; i < 10; i++ {
        pos.Height++
        watchCat.Feed(pos)
        time.Sleep(timeout / 2)
        select {
        case <-watchCat.Meow():
            s.FailNow("unexpected terminated")
        default:
        }
    }

    time.Sleep(timeout)
    rec.lock.RLock()
    s.Require().Equal(1, len(rec.votes))
    s.Require().Equal(uint64(1), rec.votes[pos.Height])
    rec.lock.RUnlock()

    time.Sleep(polling * 2)
    select {
    case <-watchCat.Meow():
        s.FailNow("unexpected terminated")
    default:
    }

    rec.lock.Lock()
    rec.votes[pos.Height] = uint64(notarySet/2 + 1)
    rec.lock.Unlock()

    time.Sleep(polling * 2)
    select {
    case <-watchCat.Meow():
    default:
        s.FailNow("expecting terminated")
    }
    s.Equal(pos, watchCat.LastPosition())
}

func TestWatchCat(t *testing.T) {
    suite.Run(t, new(WatchCatTestSuite))
}