diff --git a/backup.go b/backup.go index 731fbec..15d3cd1 100644 --- a/backup.go +++ b/backup.go @@ -13,8 +13,8 @@ import ( ) // Mockings -var mock_exec_Command = exec.Command -var mock_cmd_Run = func(c *exec.Cmd) error { +var mockExecCommand = exec.Command +var mockCmdRun = func(c *exec.Cmd) error { return c.Run() } @@ -81,8 +81,8 @@ func (b *Backup) PrepareRun() error { } writer := io.MultiWriter(logfile) b.logger = log.New(writer, b.Name, log.LstdFlags) - cmd := mock_exec_Command("chown", "-R", b.ExeUser, backupPath) - err = mock_cmd_Run(cmd) + cmd := mockExecCommand("chown", "-R", b.ExeUser, backupPath) + err = mockCmdRun(cmd) if err != nil { b.logger.Printf("chown for backup directory failed: %s", err) return err @@ -107,10 +107,10 @@ func (b *Backup) Run() error { log.Printf("ERROR: Script path is relative, aborting.") return fmt.Errorf("script path is relative, aborting") } - cmd := mock_exec_Command("/usr/bin/sh", b.ScriptPath) + cmd := mockExecCommand("/usr/bin/sh", b.ScriptPath) if b.ExeUser != "" { // setup script environment including user to use - cmd = mock_exec_Command("sudo", "-E", "-u", b.ExeUser, "/usr/bin/sh", b.ScriptPath) + cmd = mockExecCommand("sudo", "-E", "-u", b.ExeUser, "/usr/bin/sh", b.ScriptPath) } b.logger.Printf("Running backup script of '%s'", b.Name) b.logger.Printf("Script is: %s", b.ScriptPath) @@ -129,7 +129,7 @@ func (b *Backup) Run() error { log.Printf("About to run: %s", cmd.String()) // run script - err := mock_cmd_Run(cmd) + err := mockCmdRun(cmd) if err != nil { log.Printf("Backup '%s' run failed", b.Name) return err diff --git a/backup_test.go b/backup_test.go index 7c7389e..fc86d72 100644 --- a/backup_test.go +++ b/backup_test.go @@ -14,12 +14,6 @@ func getCurrentFilePath() string { return file } -type MockCmd struct{} - -func (c *MockCmd) Run() error { - return nil -} - func TestFindBackupsForDevice(t *testing.T) { var testBackups = Backups{} @@ -119,7 +113,7 @@ func setupNewTestEnv(subdir string) { func TestPrepareRun(t *testing.T) { setupNewTestEnv("preparerun") - mock_cmd_Run = func(c *exec.Cmd) error { + mockCmdRun = func(c *exec.Cmd) error { return nil } var testBkp = Backup{ @@ -142,7 +136,7 @@ func TestRun(t *testing.T) { config.Devices["mytarget"].Name = "mytarget" config.Devices["mytarget"].UUID = "123-456-789-abc-def" - mock_cmd_Run = func(c *exec.Cmd) error { + mockCmdRun = func(c *exec.Cmd) error { return nil } var testBkp = Backup{ @@ -167,7 +161,7 @@ func TestRun(t *testing.T) { t.Logf("Error which should not occur: %s", err) t.Fail() } - mock_cmd_Run = func(c *exec.Cmd) error { + mockCmdRun = func(c *exec.Cmd) error { return nil } } diff --git a/database.go b/database.go index dc506e9..31c2099 100644 --- a/database.go +++ b/database.go @@ -7,6 +7,9 @@ import ( "path" ) +var mockOsWriteFile = os.WriteFile +var mockOsReadFile = os.ReadFile + // Database is a simple string to string mapping, where arbitrary strings can be stored and safed to disk or loaded type Database struct { data map[string]string @@ -23,7 +26,7 @@ func (d *Database) Save() { log.Printf("Writing database output to file: %s", jsonstr) saveDir, _ := path.Split(dbPath) CreateDirectoryIfNotExists(saveDir) - err := os.WriteFile(dbPath, []byte(jsonstr), 0644) + err := mockOsWriteFile(dbPath, []byte(jsonstr), 0644) if err != nil { panic(err) } @@ -32,7 +35,7 @@ func (d *Database) Save() { // Load loads the database func (d *Database) Load() { if _, err := os.Stat(dbPath); err == nil { - data, rferr := os.ReadFile(dbPath) + data, rferr := mockOsReadFile(dbPath) if rferr != nil { panic(rferr) } diff --git a/database_test.go b/database_test.go new file mode 100644 index 0000000..ceeb3e6 --- /dev/null +++ b/database_test.go @@ -0,0 +1,23 @@ +package backive + +import ( + "io/fs" + "testing" +) + +func TestDatabase(t *testing.T) { + mockOsStat = func(p string) (fs.FileInfo, error) { + return nil, nil + } + db := new(Database) + mockOsReadFile = func(p string) ([]byte, error) { + return []byte("{}"), nil + } + db.Load() + + mockOsWriteFile = func(p string, data []byte, rights fs.FileMode) error { + return nil + } + + db.Save() +} diff --git a/device.go b/device.go index 1dda358..bcfe318 100644 --- a/device.go +++ b/device.go @@ -35,7 +35,7 @@ func (d *Device) Mount() error { cmd.Stdout = log.Writer() cmd.Stderr = log.Writer() log.Printf("Command to execute: %s", cmd.String()) - err := mock_cmd_Run(cmd) + err := mockCmdRun(cmd) if err != nil { log.Printf("Mounting failed with error %v", err) return err @@ -49,7 +49,7 @@ func (d *Device) Unmount() error { if d.isMounted { log.Printf("Unmounting %s", d.Name) sync := exec.Command("sync") - syncErr := mock_cmd_Run(sync) + syncErr := mockCmdRun(sync) if syncErr != nil { log.Println(syncErr) return syncErr @@ -59,7 +59,7 @@ func (d *Device) Unmount() error { path.Join(config.Settings.SystemMountPoint, d.Name), ) log.Printf("About to run: %s", cmd.String()) - err := mock_cmd_Run(cmd) + err := mockCmdRun(cmd) if err != nil { log.Println(err) return err diff --git a/device_test.go b/device_test.go new file mode 100644 index 0000000..74e4958 --- /dev/null +++ b/device_test.go @@ -0,0 +1,33 @@ +package backive + +import ( + "os/exec" + "testing" +) + +func TestDevice(t *testing.T) { + testDevice := new(Device) + testDevice.Name = "Testdevice" + testDevice.UUID = "123-456-789-abc-def" + mockCmdRun = func(c *exec.Cmd) error { + return nil + } + err := testDevice.Mount() + if err != nil { + t.Log("Should not fail, is mocked.") + t.Fail() + } + if !testDevice.IsMounted() { + t.Log("Should return true.") + t.Fail() + } + err = testDevice.Unmount() + if err != nil { + t.Log("Should not fail, is mocked.") + t.Fail() + } + if testDevice.IsMounted() { + t.Log("Should return false.") + t.Fail() + } +} diff --git a/events.go b/events.go index b9e8430..a9ac5fe 100644 --- a/events.go +++ b/events.go @@ -14,10 +14,12 @@ type EventHandler struct { ls net.Listener //done <-chan struct{} callbacks []func(map[string]string) + stop chan bool } // Init initializes the unix socket. func (eh *EventHandler) Init(socketPath string) { + eh.stop = make(chan bool) log.Println("Initializing EventHandler...") var err error dir, _ := path.Split(socketPath) @@ -29,12 +31,27 @@ func (eh *EventHandler) Init(socketPath string) { eh.callbacks = make([]func(map[string]string), 3) } +func (eh *EventHandler) Stop() { + log.Println("Closing EventHandler") + eh.stop <- true + err := eh.ls.Close() + if err != nil { + log.Println("Error closing the listener") + } + log.Println("Closed EventHandler") +} + // Listen starts the event loop. func (eh *EventHandler) Listen() { log.Println("Running eventloop") func() { for { - eh.process() + select { + case <-eh.stop: + return + default: + eh.process() + } } }() } @@ -49,8 +66,14 @@ func (eh *EventHandler) process() { client, err := eh.ls.Accept() log.Println("Accepted client") if err != nil { - log.Fatal(err) + select { + case <-eh.stop: + return + default: + log.Fatal(err) + } } + defer client.Close() data := make([]byte, 2048) for { buf := make([]byte, 512) @@ -67,15 +90,15 @@ func (eh *EventHandler) process() { //log.Println(sdata) var message map[string]interface{} errjson := json.Unmarshal([]byte(sdata), &message) + if errjson != nil { + log.Fatal(errjson) + } var env = map[string]string{} if message["request"] == "udev" { for k, v := range message["data"].(map[string]interface{}) { env[k] = v.(string) } } - if errjson != nil { - log.Fatal(errjson) - } for _, v := range eh.callbacks { if v != nil { v(env) diff --git a/events_test.go b/events_test.go new file mode 100644 index 0000000..35d8b17 --- /dev/null +++ b/events_test.go @@ -0,0 +1,81 @@ +package backive + +import ( + "encoding/json" + "log" + "net" + "os" + "testing" + "time" +) + +func TestEventhandler(t *testing.T) { + t.Skip("Do not get it to work...") + eh := new(EventHandler) + eh.Init("./backive.socket") + defer func() { + eh.Stop() + err := os.Remove("./backive.socket") + if err != nil { + t.Log(err) + } + }() + t.Log("Initialized test") + go eh.Listen() + t.Log("eh is listening") + var hasBeenCalled = make(chan bool) + eh.RegisterCallback( + func(m map[string]string) { + hasBeenCalled <- true + }, + ) + t.Log("registered callback") + beenCalled := false + var counter = 0 + env := map[string]string{} + env["test"] = "test" + message := map[string]interface{}{} + message["request"] = "udev" + message["data"] = env + for { + select { + case data := <-hasBeenCalled: + t.Log("receiving message") + beenCalled = data + if !beenCalled { + t.Fail() + } + t.Log("received message") + eh.Stop() + return + default: + t.Logf("Waiting for callback %d", counter) + time.Sleep(time.Millisecond) + if counter == 2 { + sendDataToSocket("./backive.socket", message) + t.Log("sent message") + } + if counter < 10 { + counter++ + } else { + t.Log("Stopping with Fail") + eh.Stop() + t.Fail() + break + } + } + } +} + +func sendDataToSocket(socket string, message map[string]interface{}) { + c, err := net.Dial("unix", socket) + if err != nil { + log.Fatalln("Could not instantiate unix socket. Aborting") + } + jsonstr, err := json.Marshal(message) + if err != nil { + log.Fatalln("Could not convert to json. Aborting") + } + c.Write(jsonstr) + defer c.Close() +} diff --git a/utils.go b/utils.go index 0219a98..8bea892 100644 --- a/utils.go +++ b/utils.go @@ -5,12 +5,15 @@ import ( "os" ) +var mockOsStat = os.Stat +var mockOsMkdirAll = os.MkdirAll + // CreateDirectoryIfNotExists Checks for a directory string and creates the directory if it does not exist, must be a absolute path. func CreateDirectoryIfNotExists(dir string) { - if _, err := os.Stat(dir); err == nil { + if _, err := mockOsStat(dir); err == nil { //ignore } else if os.IsNotExist(err) { - os.MkdirAll(dir, 0755) + mockOsMkdirAll(dir, 0755) } else { log.Fatal(err) }