github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/state/leadership/util_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package leadership_test
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/state/lease"
    17  )
    18  
    19  // Client implements lease.Client for testing purposes.
    20  type Client struct {
    21  	leases map[string]lease.Info
    22  	expect []call
    23  	failed string
    24  	done   chan struct{}
    25  }
    26  
    27  // NewClient initializes and returns a new client configured to report
    28  // the supplied leases and expect the supplied calls.
    29  func NewClient(leases map[string]lease.Info, expect []call) *Client {
    30  	if leases == nil {
    31  		leases = make(map[string]lease.Info)
    32  	}
    33  	done := make(chan struct{})
    34  	if len(expect) == 0 {
    35  		close(done)
    36  	}
    37  	return &Client{
    38  		leases: leases,
    39  		expect: expect,
    40  		done:   done,
    41  	}
    42  }
    43  
    44  // Wait will return when all expected calls have been made, or fail the test
    45  // if they don't happen within a second. (You control the clock; your tests
    46  // should pass in *way* less than a second of wall-clock time.)
    47  func (client *Client) Wait(c *gc.C) {
    48  	select {
    49  	case <-client.done:
    50  		if client.failed != "" {
    51  			c.Fatalf(client.failed)
    52  		}
    53  	case <-time.After(time.Second):
    54  		c.Fatalf("Client test took way too long")
    55  	}
    56  }
    57  
    58  // Leases is part of the lease.Client interface.
    59  func (client *Client) Leases() map[string]lease.Info {
    60  	result := make(map[string]lease.Info)
    61  	for k, v := range client.leases {
    62  		result[k] = v
    63  	}
    64  	return result
    65  }
    66  
    67  // call implements the bulk of the lease.Client interface.
    68  func (client *Client) call(method string, args []interface{}) error {
    69  	select {
    70  	case <-client.done:
    71  		return errors.Errorf("Client method called after test complete: %s %v", method, args)
    72  	default:
    73  		defer func() {
    74  			if len(client.expect) == 0 || client.failed != "" {
    75  				close(client.done)
    76  			}
    77  		}()
    78  	}
    79  
    80  	expect := client.expect[0]
    81  	client.expect = client.expect[1:]
    82  	if expect.callback != nil {
    83  		expect.callback(client.leases)
    84  	}
    85  
    86  	if method == expect.method {
    87  		if ok, _ := jc.DeepEqual(args, expect.args); ok {
    88  			return expect.err
    89  		}
    90  	}
    91  	client.failed = fmt.Sprintf("unexpected Client call:\n  actual: %s %v\n  expect: %s %v",
    92  		method, args, expect.method, expect.args,
    93  	)
    94  	return errors.New(client.failed)
    95  }
    96  
    97  // ClaimLease is part of the lease.Client interface.
    98  func (client *Client) ClaimLease(name string, request lease.Request) error {
    99  	return client.call("ClaimLease", []interface{}{name, request})
   100  }
   101  
   102  // ExtendLease is part of the lease.Client interface.
   103  func (client *Client) ExtendLease(name string, request lease.Request) error {
   104  	return client.call("ExtendLease", []interface{}{name, request})
   105  }
   106  
   107  // ExpireLease is part of the lease.Client interface.
   108  func (client *Client) ExpireLease(name string) error {
   109  	return client.call("ExpireLease", []interface{}{name})
   110  }
   111  
   112  // Refresh is part of the lease.Client interface.
   113  func (client *Client) Refresh() error {
   114  	return client.call("Refresh", nil)
   115  }
   116  
   117  // call defines a expected method call on a Client; it encodes:
   118  type call struct {
   119  
   120  	// method is the name of the method.
   121  	method string
   122  
   123  	// args is the expected arguments.
   124  	args []interface{}
   125  
   126  	// err is the error to return.
   127  	err error
   128  
   129  	// callback, if non-nil, will be passed the internal leases dict; for
   130  	// modification, if desired. Otherwise you can use it to, e.g., assert
   131  	// clock time.
   132  	callback func(leases map[string]lease.Info)
   133  }
   134  
   135  // Clock implements lease.Clock for testing purposes.
   136  type Clock struct {
   137  	mu     sync.Mutex
   138  	now    time.Time
   139  	alarms []alarm
   140  }
   141  
   142  // NewClock returns a new clock set to the supplied time.
   143  func NewClock(now time.Time) *Clock {
   144  	return &Clock{now: now}
   145  }
   146  
   147  // Now is part of the lease.Clock interface.
   148  func (clock *Clock) Now() time.Time {
   149  	clock.mu.Lock()
   150  	defer clock.mu.Unlock()
   151  	return clock.now
   152  }
   153  
   154  // Alarm is part of the lease.Clock interface.
   155  func (clock *Clock) Alarm(t time.Time) <-chan time.Time {
   156  	clock.mu.Lock()
   157  	defer clock.mu.Unlock()
   158  	notify := make(chan time.Time, 1)
   159  	if !clock.now.Before(t) {
   160  		notify <- clock.now
   161  	} else {
   162  		clock.alarms = append(clock.alarms, alarm{t, notify})
   163  		sort.Sort(byTime(clock.alarms))
   164  	}
   165  	return notify
   166  }
   167  
   168  // Advance advances the result of Now by the supplied duration, and sends
   169  // the "current" time on all alarms which are no longer "in the future".
   170  func (clock *Clock) Advance(d time.Duration) {
   171  	clock.mu.Lock()
   172  	defer clock.mu.Unlock()
   173  	clock.now = clock.now.Add(d)
   174  	rung := 0
   175  	for _, alarm := range clock.alarms {
   176  		if clock.now.Before(alarm.time) {
   177  			break
   178  		}
   179  		alarm.notify <- clock.now
   180  		rung++
   181  	}
   182  	clock.alarms = clock.alarms[rung:]
   183  }
   184  
   185  // alarm records the time at which we're expected to send on notify.
   186  type alarm struct {
   187  	time   time.Time
   188  	notify chan time.Time
   189  }
   190  
   191  // byTime is used to sort alarms by time.
   192  type byTime []alarm
   193  
   194  func (a byTime) Len() int           { return len(a) }
   195  func (a byTime) Less(i, j int) bool { return a[i].time.Before(a[j].time) }
   196  func (a byTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }