// 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/cmd/safcm-remote/log" "ruderich.org/simon/safcm/cmd/safcm-remote/run" ) 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) { 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) { 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) { r.t.Errorf("%s: %s", method, cmp.Diff(exp, cmd, cmpopts.IgnoreUnexported( exec.Cmd{}, bytes.Buffer{}))) } 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(logPrefix, func(level safcm.LogLevel, format string, a ...interface{}) { res.ch <- fmt.Sprintf("%d: %s", level, fmt.Sprintf(format, a...)) }) return &Sync{ req: req, cmd: run.NewCmd(runner, logger), log: logger, }, res } func (s *syncTestResult) Wait() []string { 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 }