]> ruderich.org/simon Gitweb - safcm/safcm.git/blobdiff - remote/sync/sync_test.go
Move implementation of cmd/safcm-remote/ to remote/
[safcm/safcm.git] / remote / sync / sync_test.go
diff --git a/remote/sync/sync_test.go b/remote/sync/sync_test.go
new file mode 100644 (file)
index 0000000..f5d2e11
--- /dev/null
@@ -0,0 +1,157 @@
+// Copyright (C) 2021  Simon Ruderich
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package sync
+
+import (
+       "bytes"
+       "fmt"
+       "os/exec"
+       "reflect"
+       "sync"
+       "testing"
+
+       "github.com/google/go-cmp/cmp"
+       "github.com/google/go-cmp/cmp/cmpopts"
+
+       "ruderich.org/simon/safcm"
+       "ruderich.org/simon/safcm/remote/log"
+       "ruderich.org/simon/safcm/remote/run"
+)
+
+// testRunner implements run.Runner to test commands without actually running
+// them.
+type testRunner struct {
+       t         *testing.T
+       expCmds   []*exec.Cmd
+       resStdout [][]byte
+       resStderr [][]byte
+       resError  []error
+}
+
+func (r *testRunner) Run(cmd *exec.Cmd) error {
+       stdout, stderr, resErr := r.check("run", cmd)
+       _, err := cmd.Stdout.Write(stdout)
+       if err != nil {
+               panic(err)
+       }
+       _, err = cmd.Stderr.Write(stderr)
+       if err != nil {
+               panic(err)
+       }
+       return resErr
+}
+func (r *testRunner) CombinedOutput(cmd *exec.Cmd) ([]byte, error) {
+       r.t.Helper()
+
+       stdout, stderr, err := r.check("combinedOutput", cmd)
+       if stderr != nil {
+               // stdout also contains stderr
+               r.t.Fatalf("CombinedOutput: stderr != nil, but %v", stderr)
+       }
+       return stdout, err
+}
+func (r *testRunner) check(method string, cmd *exec.Cmd) (
+       []byte, []byte, error) {
+       r.t.Helper()
+
+       if len(r.expCmds) == 0 {
+               r.t.Fatalf("%s: empty expCmds", method)
+       }
+       if len(r.resStdout) == 0 {
+               r.t.Fatalf("%s: empty resStdout", method)
+       }
+       if len(r.resStderr) == 0 {
+               r.t.Fatalf("%s: empty resStderr", method)
+       }
+       if len(r.resError) == 0 {
+               r.t.Fatalf("%s: empty resError", method)
+       }
+
+       exp := r.expCmds[0]
+       r.expCmds = r.expCmds[1:]
+       if !reflect.DeepEqual(exp, cmd) {
+               opts := cmpopts.IgnoreUnexported(exec.Cmd{}, bytes.Buffer{})
+               r.t.Errorf("%s: %s", method, cmp.Diff(exp, cmd, opts))
+       }
+
+       var stdout, stderr []byte
+       var err error
+
+       stdout, r.resStdout = r.resStdout[0], r.resStdout[1:]
+       stderr, r.resStderr = r.resStderr[0], r.resStderr[1:]
+       err, r.resError = r.resError[0], r.resError[1:]
+
+       return stdout, stderr, err
+}
+
+type syncTestResult struct {
+       ch     chan string
+       wg     sync.WaitGroup
+       dbg    []string
+       runner *testRunner
+}
+
+func prepareSync(req safcm.MsgSyncReq, runner *testRunner) (
+       *Sync, *syncTestResult) {
+
+       res := &syncTestResult{
+               ch:     make(chan string),
+               runner: runner,
+       }
+       res.wg.Add(1)
+       go func() {
+               for {
+                       x, ok := <-res.ch
+                       if !ok {
+                               break
+                       }
+                       res.dbg = append(res.dbg, x)
+               }
+               res.wg.Done()
+       }()
+
+       logger := log.NewLogger(func(level safcm.LogLevel, msg string) {
+               res.ch <- fmt.Sprintf("%d: %s", level, msg)
+       })
+       return &Sync{
+               req: req,
+               cmd: run.NewCmd(runner, logger),
+               log: logger,
+       }, res
+}
+
+func (s *syncTestResult) Wait() []string {
+       s.runner.t.Helper()
+
+       close(s.ch)
+       s.wg.Wait()
+
+       // All expected commands must have been executed
+       if len(s.runner.expCmds) != 0 {
+               s.runner.t.Errorf("expCmds left: %v", s.runner.expCmds)
+       }
+       if len(s.runner.resStdout) != 0 {
+               s.runner.t.Errorf("resStdout left: %v", s.runner.resStdout)
+       }
+       if len(s.runner.resStderr) != 0 {
+               s.runner.t.Errorf("resStderr left: %v", s.runner.resStderr)
+       }
+       if len(s.runner.resError) != 0 {
+               s.runner.t.Errorf("resError left: %v", s.runner.resError)
+       }
+
+       return s.dbg
+}