X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=remote%2Fsync%2Fsync_test.go;fp=remote%2Fsync%2Fsync_test.go;h=f5d2e1120748b98704c8865ab2c4183d24552b67;hb=9269fa3c94e700afc0be823f58ea473a2db8f3dc;hp=0000000000000000000000000000000000000000;hpb=fd97e8019e2ab166d9475ed59782c86247d8430b;p=safcm%2Fsafcm.git diff --git a/remote/sync/sync_test.go b/remote/sync/sync_test.go new file mode 100644 index 0000000..f5d2e11 --- /dev/null +++ b/remote/sync/sync_test.go @@ -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 . + +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 +}