Refactor tests to use mock instead of real STM 79/181879/1
authorAleksander Mistewicz <a.mistewicz@samsung.com>
Mon, 18 Jun 2018 11:37:49 +0000 (13:37 +0200)
committerAleksander Mistewicz <a.mistewicz@samsung.com>
Mon, 18 Jun 2018 12:14:30 +0000 (14:14 +0200)
Testing this part of code was troublesome as a working MuxPi was needed.
Due to recent changes in git.tizen.org/tools/muxpi/sw/nanopi/stm,
functions of STM are exposed with Interface. It is easier to test using
a mockup.

Change-Id: I5c2be2ae717b448f3182896074faefd8f2ea1971
Signed-off-by: Aleksander Mistewicz <a.mistewicz@samsung.com>
dryad/muxpi.go
dryad/muxpi_mock_test.go [new file with mode: 0644]
dryad/rusalka.go
dryad/rusalka_test.go

index 63756d4..1935729 100644 (file)
@@ -36,26 +36,29 @@ var (
        pink   = colorLED{128, 16, 32}
 )
 
+type stmHelper struct {
+       stm.Interface
+}
+
 // setLED wraps stm's SetLED so that simple color definitions may be used.
-func setLED(led stm.LED, col colorLED) error {
-       return stm.SetLED(led, col.r, col.g, col.b)
+func (sh *stmHelper) setLED(led stm.LED, col colorLED) (err error) {
+       return sh.SetLED(led, col.r, col.g, col.b)
 }
 
 // blinkMaintenanceLED alternates between LED1 and LED2 lighting each
 // with yellow color for approximately 1 second.
 //
 // It is cancelled by ctx.
-func blinkMaintenanceLED(ctx context.Context) {
-       defer stm.Close()
-       defer stm.ClearDisplay()
+func (sh *stmHelper) blinkMaintenanceLED(ctx context.Context) {
+       defer sh.ClearDisplay()
        for {
-               setLED(stm.LED1, yellow)
+               sh.setLED(stm.LED1, yellow)
                time.Sleep(time.Second)
-               setLED(stm.LED1, off)
+               sh.setLED(stm.LED1, off)
 
-               setLED(stm.LED2, yellow)
+               sh.setLED(stm.LED2, yellow)
                time.Sleep(time.Second)
-               setLED(stm.LED2, off)
+               sh.setLED(stm.LED2, off)
 
                select {
                case <-ctx.Done():
@@ -66,10 +69,16 @@ func blinkMaintenanceLED(ctx context.Context) {
 }
 
 // printMessage clears the OLED display and prints msg to it.
-func printMessage(msg string) error {
-       err := stm.ClearDisplay()
+func (sh *stmHelper) printMessage(msg string) (err error) {
+       err = sh.ClearDisplay()
        if err != nil {
                return err
        }
-       return stm.PrintText(0, 0, stm.Foreground, msg)
+       return sh.PrintText(0, 0, stm.Foreground, msg)
+}
+
+// powerTick switches relay on and off.
+// It may be used for Healthcheck.
+func (sh *stmHelper) powerTick() (err error) {
+       return sh.PowerTick(time.Second)
 }
diff --git a/dryad/muxpi_mock_test.go b/dryad/muxpi_mock_test.go
new file mode 100644 (file)
index 0000000..b89394a
--- /dev/null
@@ -0,0 +1,120 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: git.tizen.org/tools/muxpi/sw/nanopi/stm (interfaces: Interface)
+
+// Package dryad is a generated GoMock package.
+package dryad
+
+import (
+       stm "git.tizen.org/tools/muxpi/sw/nanopi/stm"
+       gomock "github.com/golang/mock/gomock"
+       reflect "reflect"
+       time "time"
+)
+
+// MockInterface is a mock of Interface interface
+type MockInterface struct {
+       ctrl     *gomock.Controller
+       recorder *MockInterfaceMockRecorder
+}
+
+// MockInterfaceMockRecorder is the mock recorder for MockInterface
+type MockInterfaceMockRecorder struct {
+       mock *MockInterface
+}
+
+// NewMockInterface creates a new mock instance
+func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
+       mock := &MockInterface{ctrl: ctrl}
+       mock.recorder = &MockInterfaceMockRecorder{mock}
+       return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
+       return m.recorder
+}
+
+// ClearDisplay mocks base method
+func (m *MockInterface) ClearDisplay() error {
+       ret := m.ctrl.Call(m, "ClearDisplay")
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// ClearDisplay indicates an expected call of ClearDisplay
+func (mr *MockInterfaceMockRecorder) ClearDisplay() *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearDisplay", reflect.TypeOf((*MockInterface)(nil).ClearDisplay))
+}
+
+// DUT mocks base method
+func (m *MockInterface) DUT() error {
+       ret := m.ctrl.Call(m, "DUT")
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// DUT indicates an expected call of DUT
+func (mr *MockInterfaceMockRecorder) DUT() *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DUT", reflect.TypeOf((*MockInterface)(nil).DUT))
+}
+
+// GetCurrent mocks base method
+func (m *MockInterface) GetCurrent() (int, error) {
+       ret := m.ctrl.Call(m, "GetCurrent")
+       ret0, _ := ret[0].(int)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// GetCurrent indicates an expected call of GetCurrent
+func (mr *MockInterfaceMockRecorder) GetCurrent() *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrent", reflect.TypeOf((*MockInterface)(nil).GetCurrent))
+}
+
+// PowerTick mocks base method
+func (m *MockInterface) PowerTick(arg0 time.Duration) error {
+       ret := m.ctrl.Call(m, "PowerTick", arg0)
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// PowerTick indicates an expected call of PowerTick
+func (mr *MockInterfaceMockRecorder) PowerTick(arg0 interface{}) *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PowerTick", reflect.TypeOf((*MockInterface)(nil).PowerTick), arg0)
+}
+
+// PrintText mocks base method
+func (m *MockInterface) PrintText(arg0, arg1 uint, arg2 stm.Color, arg3 string) error {
+       ret := m.ctrl.Call(m, "PrintText", arg0, arg1, arg2, arg3)
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// PrintText indicates an expected call of PrintText
+func (mr *MockInterfaceMockRecorder) PrintText(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintText", reflect.TypeOf((*MockInterface)(nil).PrintText), arg0, arg1, arg2, arg3)
+}
+
+// SetLED mocks base method
+func (m *MockInterface) SetLED(arg0 stm.LED, arg1, arg2, arg3 byte) error {
+       ret := m.ctrl.Call(m, "SetLED", arg0, arg1, arg2, arg3)
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// SetLED indicates an expected call of SetLED
+func (mr *MockInterfaceMockRecorder) SetLED(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLED", reflect.TypeOf((*MockInterface)(nil).SetLED), arg0, arg1, arg2, arg3)
+}
+
+// TS mocks base method
+func (m *MockInterface) TS() error {
+       ret := m.ctrl.Call(m, "TS")
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// TS indicates an expected call of TS
+func (mr *MockInterfaceMockRecorder) TS() *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TS", reflect.TypeOf((*MockInterface)(nil).TS))
+}
index e69d51c..8782b0b 100644 (file)
@@ -33,13 +33,15 @@ import (
 type Rusalka struct {
        Dryad
        dryadUser         *borutaUser
+       stm               *stmHelper
        cancelMaintenance context.CancelFunc
 }
 
 // NewRusalka returns Dryad interface to Rusalka.
-func NewRusalka(username string, groups []string) Dryad {
+func NewRusalka(stmConn stm.Interface, username string, groups []string) Dryad {
        return &Rusalka{
                dryadUser: newBorutaUser(username, groups),
+               stm:       &stmHelper{stmConn},
        }
 }
 
@@ -48,18 +50,13 @@ func NewRusalka(username string, groups []string) Dryad {
 // Otherwise it may make it unusable for other STM users. It is closed
 // when blinkMaintenanceLED exits.
 func (r *Rusalka) PutInMaintenance(msg string) error {
-       // Connection to STM is closed in blinkMaintenanceLED().
-       err := stm.Open()
-       if err != nil {
-               return err
-       }
-       err = printMessage(msg)
+       err := r.stm.printMessage(msg)
        if err != nil {
                return err
        }
        var ctx context.Context
        ctx, r.cancelMaintenance = context.WithCancel(context.Background())
-       go blinkMaintenanceLED(ctx)
+       go r.stm.blinkMaintenanceLED(ctx)
        return nil
 }
 
@@ -90,9 +87,5 @@ func (r *Rusalka) Prepare() (key *rsa.PrivateKey, err error) {
 
 // Healthcheck is part of implementation of Dryad interface.
 func (r *Rusalka) Healthcheck() (err error) {
-       err = stm.Open()
-       if err != nil {
-               return err
-       }
-       return stm.Close()
+       return r.stm.powerTick()
 }
index 2455128..16f8a7f 100644 (file)
  *  limitations under the License
  */
 
-package dryad_test
+package dryad
+
+//go:generate mockgen -destination=muxpi_mock_test.go -package dryad git.tizen.org/tools/muxpi/sw/nanopi/stm Interface
 
 import (
        "crypto/x509"
        "encoding/pem"
-       "fmt"
        "os"
+       "os/user"
        "time"
 
-       . "git.tizen.org/tools/boruta/dryad"
-       "git.tizen.org/tools/muxpi/sw/nanopi/stm"
-
        "git.tizen.org/tools/boruta"
+       "git.tizen.org/tools/muxpi/sw/nanopi/stm"
+       gomock "github.com/golang/mock/gomock"
        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"
 )
 
 var _ = Describe("Rusalka", func() {
        var d boruta.Dryad
+       var ctrl *gomock.Controller
+       var stmMock *MockInterface
        const (
                username           = "test-user"
                homeDir            = "/home/" + username
@@ -41,23 +44,42 @@ var _ = Describe("Rusalka", func() {
        )
 
        BeforeEach(func() {
-               err := stm.Open()
-               if err != nil {
-                       Skip(fmt.Sprintf("STM is probably missing: %s", err))
-               }
-               err = stm.Close()
-               Expect(err).ToNot(HaveOccurred())
-               d = NewRusalka(username, []string{"users"})
+               ctrl = gomock.NewController(GinkgoT())
+               stmMock = NewMockInterface(ctrl)
+
+               d = NewRusalka(stmMock, username, nil)
        })
 
-       It("should put in maintenance", func() {
-               err := d.PutInMaintenance("test message")
-               Expect(err).ToNot(HaveOccurred())
-               // TODO(amistewicz): somehow check that goroutine is running and can be terminated.
-               time.Sleep(10 * time.Second)
+       AfterEach(func() {
+               ctrl.Finish()
        })
 
+       It("should put in maintenance", func(done Done) {
+               const msg = "test message"
+
+               gomock.InOrder(
+                       stmMock.EXPECT().ClearDisplay(),
+                       stmMock.EXPECT().PrintText(uint(0), uint(0), stm.Foreground, msg),
+                       stmMock.EXPECT().SetLED(stm.LED1, yellow.r, yellow.g, yellow.b),
+                       stmMock.EXPECT().SetLED(stm.LED1, off.r, off.g, off.b),
+                       stmMock.EXPECT().SetLED(stm.LED2, yellow.r, yellow.g, yellow.b),
+                       stmMock.EXPECT().SetLED(stm.LED2, off.r, off.g, off.b),
+                       stmMock.EXPECT().ClearDisplay().Do(
+                               func() { close(done) }),
+               )
+
+               err := d.PutInMaintenance(msg)
+               Expect(err).ToNot(HaveOccurred())
+               d.(*Rusalka).cancelMaintenance()
+       }, 4) // blinkMaintenanceLED sleeps twice for 2 seconds each time.
+
        It("should prepare", func() {
+               u, err := user.Current()
+               Expect(err).ToNot(HaveOccurred())
+               if u.Uid != "0" {
+                       Skip("must be run as root")
+               }
+
                key, err := d.Prepare()
                Expect(err).ToNot(HaveOccurred())
                Expect(sshDir).To(BeADirectory())
@@ -72,6 +94,8 @@ var _ = Describe("Rusalka", func() {
        })
 
        It("should be healthy", func() {
+               stmMock.EXPECT().PowerTick(time.Second)
+
                err := d.Healthcheck()
                Expect(err).ToNot(HaveOccurred())
        })