// Copyright (C) 2015 The Syncthing Authors. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // +build integration package integration import ( "io/ioutil" "log" "os" "strings" "testing" "time" "github.com/syncthing/protocol" "github.com/syncthing/syncthing/internal/config" ) func TestOverride(t *testing.T) { // Enable "Master" on s1/default id, _ := protocol.DeviceIDFromString(id1) cfg, _ := config.Load("h1/config.xml", id) fld := cfg.Folders()["default"] fld.ReadOnly = true cfg.SetFolder(fld) os.Rename("h1/config.xml", "h1/config.xml.orig") defer os.Rename("h1/config.xml.orig", "h1/config.xml") cfg.Save() log.Println("Cleaning...") err := removeAll("s1", "s2", "h1/index*", "h2/index*") if err != nil { t.Fatal(err) } log.Println("Generating files...") err = generateFiles("s1", 100, 20, "../LICENSE") if err != nil { t.Fatal(err) } fd, err := os.Create("s1/testfile.txt") if err != nil { t.Fatal(err) } _, err = fd.WriteString("hello\n") if err != nil { t.Fatal(err) } err = fd.Close() if err != nil { t.Fatal(err) } expected, err := directoryContents("s1") if err != nil { t.Fatal(err) } log.Println("Starting master...") master := syncthingProcess{ // id1 instance: "1", argv: []string{"-home", "h1"}, port: 8081, apiKey: apiKey, } err = master.start() if err != nil { t.Fatal(err) } defer master.stop() // Wait for one scan to succeed, or up to 20 seconds... This is to let // startup, UPnP etc complete and make sure the master has the full index // before they connect. for i := 0; i < 20; i++ { err := master.rescan("default") if err != nil { time.Sleep(time.Second) continue } break } log.Println("Starting slave...") slave := syncthingProcess{ // id2 instance: "2", argv: []string{"-home", "h2"}, port: 8082, apiKey: apiKey, } err = slave.start() if err != nil { master.stop() t.Fatal(err) } defer slave.stop() log.Println("Syncing...") if err = ovCompletion(100, master, slave); err != nil { t.Fatal(err) } log.Println("Verifying...") actual, err := directoryContents("s2") if err != nil { t.Fatal(err) } err = compareDirectoryContents(actual, expected) if err != nil { t.Fatal(err) } log.Println("Changing file on slave side...") fd, err = os.OpenFile("s2/testfile.txt", os.O_WRONLY|os.O_APPEND, 0644) if err != nil { t.Fatal(err) } _, err = fd.WriteString("text added to s2\n") if err != nil { t.Fatal(err) } err = fd.Close() if err != nil { t.Fatal(err) } err = slave.rescan("default") if err != nil { t.Fatal(err) } log.Println("Syncing...") time.Sleep(5 * time.Second) // Expect ~99% completion since the change will be rejected by the master side if err = ovCompletion(99, master, slave); err != nil { t.Fatal(err) } log.Println("Hitting Override on master...") resp, err := master.post("/rest/model/override?folder=default", nil) if err != nil { t.Fatal(err) } if resp.StatusCode != 200 { t.Fatal(resp.Status) } log.Println("Syncing...") if err = ovCompletion(100, master, slave); err != nil { t.Fatal(err) } // Verify that the override worked fd, err = os.Open("s1/testfile.txt") if err != nil { t.Fatal(err) } bs, err := ioutil.ReadAll(fd) if err != nil { t.Fatal(err) } fd.Close() if strings.Contains(string(bs), "added to s2") { t.Error("Change should not have been synced to master") } fd, err = os.Open("s2/testfile.txt") if err != nil { t.Fatal(err) } bs, err = ioutil.ReadAll(fd) if err != nil { t.Fatal(err) } fd.Close() if strings.Contains(string(bs), "added to s2") { t.Error("Change should have been overridden on slave") } } func ovCompletion(expected int, p ...syncthingProcess) error { mainLoop: for { time.Sleep(2500 * time.Millisecond) tot := 0 for i := range p { comp, err := p[i].peerCompletion() if err != nil { if isTimeout(err) { continue mainLoop } return err } for _, pct := range comp { tot += pct } } if tot >= expected*len(p) { return nil } log.Printf("%d / %d...", tot, expected*len(p)) } }