github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/overlord/standby/standby_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  /*
     3   * Copyright (C) 2018 Canonical Ltd
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License version 3 as
     7   * published by the Free Software Foundation.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   *
    17   */
    18  package standby_test
    19  
    20  import (
    21  	"testing"
    22  	"time"
    23  
    24  	. "gopkg.in/check.v1"
    25  
    26  	"github.com/snapcore/snapd/overlord/standby"
    27  	"github.com/snapcore/snapd/overlord/state"
    28  )
    29  
    30  // Hook up v1 into the "go test" runner
    31  func Test(t *testing.T) { TestingT(t) }
    32  
    33  type standbySuite struct {
    34  	state *state.State
    35  
    36  	canStandby bool
    37  }
    38  
    39  var _ = Suite(&standbySuite{})
    40  
    41  func (s *standbySuite) SetUpTest(c *C) {
    42  	s.state = state.New(nil)
    43  }
    44  
    45  func (s *standbySuite) TestCanStandbyNoChanges(c *C) {
    46  	m := standby.New(s.state)
    47  	c.Check(m.CanStandby(), Equals, false)
    48  
    49  	m.SetStartTime(time.Time{})
    50  	c.Check(m.CanStandby(), Equals, true)
    51  }
    52  
    53  func (s *standbySuite) TestCanStandbyPendingChanges(c *C) {
    54  	st := s.state
    55  	st.Lock()
    56  	chg := st.NewChange("foo", "fake change")
    57  	chg.AddTask(st.NewTask("bar", "fake task"))
    58  	c.Assert(chg.Status(), Equals, state.DoStatus)
    59  	st.Unlock()
    60  
    61  	m := standby.New(s.state)
    62  	m.SetStartTime(time.Time{})
    63  	c.Check(m.CanStandby(), Equals, false)
    64  }
    65  
    66  func (s *standbySuite) TestCanStandbyPendingClean(c *C) {
    67  	st := s.state
    68  	st.Lock()
    69  	t := st.NewTask("bar", "fake task")
    70  	chg := st.NewChange("foo", "fake change")
    71  	chg.AddTask(t)
    72  	t.SetStatus(state.DoneStatus)
    73  	c.Assert(chg.Status(), Equals, state.DoneStatus)
    74  	c.Assert(t.IsClean(), Equals, false)
    75  	st.Unlock()
    76  
    77  	m := standby.New(s.state)
    78  	m.SetStartTime(time.Time{})
    79  	c.Check(m.CanStandby(), Equals, false)
    80  }
    81  
    82  func (s *standbySuite) TestCanStandbyOnlyDonePendingChanges(c *C) {
    83  	st := s.state
    84  	st.Lock()
    85  	t := st.NewTask("bar", "fake task")
    86  	chg := st.NewChange("foo", "fake change")
    87  	chg.AddTask(t)
    88  	t.SetStatus(state.DoneStatus)
    89  	t.SetClean()
    90  	c.Assert(chg.Status(), Equals, state.DoneStatus)
    91  	c.Assert(t.IsClean(), Equals, true)
    92  	st.Unlock()
    93  
    94  	m := standby.New(s.state)
    95  	m.SetStartTime(time.Time{})
    96  	c.Check(m.CanStandby(), Equals, true)
    97  }
    98  
    99  func (s *standbySuite) CanStandby() bool {
   100  	return s.canStandby
   101  }
   102  
   103  func (s *standbySuite) TestCanStandbyWithOpinion(c *C) {
   104  	m := standby.New(s.state)
   105  	m.AddOpinion(s)
   106  	m.SetStartTime(time.Time{})
   107  
   108  	s.canStandby = true
   109  	c.Check(m.CanStandby(), Equals, true)
   110  
   111  	s.canStandby = false
   112  	c.Check(m.CanStandby(), Equals, false)
   113  }
   114  
   115  type opine func() bool
   116  
   117  func (f opine) CanStandby() bool {
   118  	return f()
   119  }
   120  
   121  func (s *standbySuite) TestStartChecks(c *C) {
   122  	n := 0
   123  	// opinions
   124  	ch1 := make(chan bool, 1)
   125  	// sync with request restart
   126  	ch2 := make(chan struct{})
   127  
   128  	defer standby.MockStandbyWait(time.Millisecond)()
   129  	defer standby.MockStateRequestRestart(func(_ *state.State, t state.RestartType) {
   130  		c.Check(t, Equals, state.RestartSocket)
   131  		n++
   132  		ch2 <- struct{}{}
   133  	})()
   134  
   135  	m := standby.New(s.state)
   136  	m.AddOpinion(opine(func() bool {
   137  		opinion := <-ch1
   138  		return opinion
   139  	}))
   140  
   141  	m.Start()
   142  	ch1 <- false
   143  	c.Check(n, Equals, 0)
   144  	ch1 <- false
   145  	c.Check(n, Equals, 0)
   146  
   147  	ch1 <- true
   148  	<-ch2
   149  	c.Check(n, Equals, 1)
   150  	// no more opinions
   151  	close(ch1)
   152  
   153  	m.Stop()
   154  	close(ch2)
   155  }
   156  
   157  func (s *standbySuite) TestStopWaits(c *C) {
   158  	defer standby.MockStandbyWait(time.Millisecond)()
   159  	defer standby.MockStateRequestRestart(func(*state.State, state.RestartType) {
   160  		c.Fatal("request restart should have not been called")
   161  	})()
   162  
   163  	ch := make(chan struct{})
   164  	opineReady := make(chan struct{})
   165  	done := make(chan struct{})
   166  	m := standby.New(s.state)
   167  	synced := false
   168  	m.AddOpinion(opine(func() bool {
   169  		if !synced {
   170  			// synchronize with the main goroutine only at the
   171  			// beginning
   172  			close(opineReady)
   173  			synced = true
   174  		}
   175  		select {
   176  		case <-time.After(200 * time.Millisecond):
   177  		case <-done:
   178  		}
   179  		return false
   180  	}))
   181  
   182  	m.Start()
   183  
   184  	// let the opinionator start its delay
   185  	<-opineReady
   186  	go func() {
   187  		// this will block until standby stops
   188  		m.Stop()
   189  		close(ch)
   190  	}()
   191  
   192  	select {
   193  	case <-time.After(100 * time.Millisecond):
   194  		// wheee
   195  	case <-ch:
   196  		c.Fatal("stop should have blocked and didn't")
   197  	}
   198  
   199  	close(done)
   200  
   201  	// wait for Stop to complete now
   202  	select {
   203  	case <-ch:
   204  		// nothing to do here
   205  	case <-time.After(10 * time.Second):
   206  		c.Fatal("stop did not complete")
   207  	}
   208  }