// Copyright (C) 2014 The Syncthing Authors. // // 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 config import ( "fmt" "os" "reflect" "runtime" "testing" "github.com/syncthing/protocol" ) var device1, device2, device3, device4 protocol.DeviceID func init() { device1, _ = protocol.DeviceIDFromString("AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ") device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY") device3, _ = protocol.DeviceIDFromString("LGFPDIT-7SKNNJL-VJZA4FC-7QNCRKA-CE753K7-2BW5QDK-2FOZ7FR-FEP57QJ") device4, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2") } func TestDefaultValues(t *testing.T) { expected := OptionsConfiguration{ ListenAddress: []string{"0.0.0.0:22000"}, GlobalAnnServers: []string{"udp4://announce.syncthing.net:22026"}, GlobalAnnEnabled: true, LocalAnnEnabled: true, LocalAnnPort: 21025, LocalAnnMCAddr: "[ff32::5222]:21026", MaxSendKbps: 0, MaxRecvKbps: 0, ReconnectIntervalS: 60, StartBrowser: true, UPnPEnabled: true, UPnPLease: 0, UPnPRenewal: 30, RestartOnWakeup: true, AutoUpgradeIntervalH: 12, KeepTemporariesH: 24, CacheIgnoredFiles: true, ProgressUpdateIntervalS: 5, SymlinksEnabled: true, } cfg := New(device1) if !reflect.DeepEqual(cfg.Options, expected) { t.Errorf("Default config differs;\n E: %#v\n A: %#v", expected, cfg.Options) } } func TestDeviceConfig(t *testing.T) { for i := 1; i <= CurrentVersion; i++ { os.Remove("testdata/.stfolder") wr, err := Load(fmt.Sprintf("testdata/v%d.xml", i), device1) if err != nil { t.Fatal(err) } _, err = os.Stat("testdata/.stfolder") if i < 6 && err != nil { t.Fatal(err) } else if i >= 6 && err == nil { t.Fatal("Unexpected file") } cfg := wr.cfg expectedFolders := []FolderConfiguration{ { ID: "test", Path: "testdata", Devices: []FolderDeviceConfiguration{{DeviceID: device1}, {DeviceID: device4}}, ReadOnly: true, RescanIntervalS: 600, Copiers: 1, Pullers: 16, Hashers: 0, }, } expectedDevices := []DeviceConfiguration{ { DeviceID: device1, Name: "node one", Addresses: []string{"a"}, Compression: true, }, { DeviceID: device4, Name: "node two", Addresses: []string{"b"}, Compression: true, }, } expectedDeviceIDs := []protocol.DeviceID{device1, device4} if cfg.Version != CurrentVersion { t.Errorf("%d: Incorrect version %d != %d", i, cfg.Version, CurrentVersion) } if !reflect.DeepEqual(cfg.Folders, expectedFolders) { t.Errorf("%d: Incorrect Folders\n A: %#v\n E: %#v", i, cfg.Folders, expectedFolders) } if !reflect.DeepEqual(cfg.Devices, expectedDevices) { t.Errorf("%d: Incorrect Devices\n A: %#v\n E: %#v", i, cfg.Devices, expectedDevices) } if !reflect.DeepEqual(cfg.Folders[0].DeviceIDs(), expectedDeviceIDs) { t.Errorf("%d: Incorrect DeviceIDs\n A: %#v\n E: %#v", i, cfg.Folders[0].DeviceIDs(), expectedDeviceIDs) } } } func TestNoListenAddress(t *testing.T) { cfg, err := Load("testdata/nolistenaddress.xml", device1) if err != nil { t.Error(err) } expected := []string{""} actual := cfg.Options().ListenAddress if !reflect.DeepEqual(actual, expected) { t.Errorf("Unexpected ListenAddress %#v", actual) } } func TestOverriddenValues(t *testing.T) { expected := OptionsConfiguration{ ListenAddress: []string{":23000"}, GlobalAnnServers: []string{"udp4://syncthing.nym.se:22026"}, GlobalAnnEnabled: false, LocalAnnEnabled: false, LocalAnnPort: 42123, LocalAnnMCAddr: "quux:3232", MaxSendKbps: 1234, MaxRecvKbps: 2341, ReconnectIntervalS: 6000, StartBrowser: false, UPnPEnabled: false, UPnPLease: 60, UPnPRenewal: 15, RestartOnWakeup: false, AutoUpgradeIntervalH: 24, KeepTemporariesH: 48, CacheIgnoredFiles: false, ProgressUpdateIntervalS: 10, SymlinksEnabled: false, } cfg, err := Load("testdata/overridenvalues.xml", device1) if err != nil { t.Error(err) } if !reflect.DeepEqual(cfg.Options(), expected) { t.Errorf("Overridden config differs;\n E: %#v\n A: %#v", expected, cfg.Options()) } } func TestDeviceAddressesDynamic(t *testing.T) { name, _ := os.Hostname() expected := map[protocol.DeviceID]DeviceConfiguration{ device1: { DeviceID: device1, Addresses: []string{"dynamic"}, Compression: true, }, device2: { DeviceID: device2, Addresses: []string{"dynamic"}, Compression: true, }, device3: { DeviceID: device3, Addresses: []string{"dynamic"}, Compression: true, }, device4: { DeviceID: device4, Name: name, // Set when auto created Addresses: []string{"dynamic"}, }, } cfg, err := Load("testdata/deviceaddressesdynamic.xml", device4) if err != nil { t.Error(err) } actual := cfg.Devices() if !reflect.DeepEqual(actual, expected) { t.Errorf("Devices differ;\n E: %#v\n A: %#v", expected, actual) } } func TestDeviceAddressesStatic(t *testing.T) { name, _ := os.Hostname() expected := map[protocol.DeviceID]DeviceConfiguration{ device1: { DeviceID: device1, Addresses: []string{"192.0.2.1", "192.0.2.2"}, }, device2: { DeviceID: device2, Addresses: []string{"192.0.2.3:6070", "[2001:db8::42]:4242"}, }, device3: { DeviceID: device3, Addresses: []string{"[2001:db8::44]:4444", "192.0.2.4:6090"}, }, device4: { DeviceID: device4, Name: name, // Set when auto created Addresses: []string{"dynamic"}, }, } cfg, err := Load("testdata/deviceaddressesstatic.xml", device4) if err != nil { t.Error(err) } actual := cfg.Devices() if !reflect.DeepEqual(actual, expected) { t.Errorf("Devices differ;\n E: %#v\n A: %#v", expected, actual) } } func TestVersioningConfig(t *testing.T) { cfg, err := Load("testdata/versioningconfig.xml", device4) if err != nil { t.Error(err) } vc := cfg.Folders()["test"].Versioning if vc.Type != "simple" { t.Errorf(`vc.Type %q != "simple"`, vc.Type) } if l := len(vc.Params); l != 2 { t.Errorf("len(vc.Params) %d != 2", l) } expected := map[string]string{ "foo": "bar", "baz": "quux", } if !reflect.DeepEqual(vc.Params, expected) { t.Errorf("vc.Params differ;\n E: %#v\n A: %#v", expected, vc.Params) } } func TestIssue1262(t *testing.T) { cfg, err := Load("testdata/issue-1262.xml", device4) if err != nil { t.Error(err) } actual := cfg.Folders()["test"].Path expected := "e:" if runtime.GOOS == "windows" { expected = `e:\` } if actual != expected { t.Errorf("%q != %q", actual, expected) } } func TestNewSaveLoad(t *testing.T) { path := "testdata/temp.xml" os.Remove(path) exists := func(path string) bool { _, err := os.Stat(path) return err == nil } intCfg := New(device1) cfg := Wrap(path, intCfg) // To make the equality pass later cfg.cfg.XMLName.Local = "configuration" if exists(path) { t.Error(path, "exists") } err := cfg.Save() if err != nil { t.Error(err) } if !exists(path) { t.Error(path, "does not exist") } cfg2, err := Load(path, device1) if err != nil { t.Error(err) } if !reflect.DeepEqual(cfg.Raw(), cfg2.Raw()) { t.Errorf("Configs are not equal;\n E: %#v\n A: %#v", cfg.Raw(), cfg2.Raw()) } os.Remove(path) } func TestPrepare(t *testing.T) { var cfg Configuration if cfg.Folders != nil || cfg.Devices != nil || cfg.Options.ListenAddress != nil { t.Error("Expected nil") } cfg.prepare(device1) if cfg.Folders == nil || cfg.Devices == nil || cfg.Options.ListenAddress == nil { t.Error("Unexpected nil") } } func TestRequiresRestart(t *testing.T) { wr, err := Load("testdata/v6.xml", device1) if err != nil { t.Fatal(err) } cfg := wr.cfg if ChangeRequiresRestart(cfg, cfg) { t.Error("No change does not require restart") } newCfg := cfg newCfg.Devices = append(newCfg.Devices, DeviceConfiguration{ DeviceID: device3, }) if ChangeRequiresRestart(cfg, newCfg) { t.Error("Adding a device does not require restart") } newCfg = cfg newCfg.Devices = newCfg.Devices[:len(newCfg.Devices)-1] if !ChangeRequiresRestart(cfg, newCfg) { t.Error("Removing a device requires restart") } newCfg = cfg newCfg.Folders = append(newCfg.Folders, FolderConfiguration{ ID: "t1", Path: "t1", }) if !ChangeRequiresRestart(cfg, newCfg) { t.Error("Adding a folder requires restart") } newCfg = cfg newCfg.Folders = newCfg.Folders[:len(newCfg.Folders)-1] if !ChangeRequiresRestart(cfg, newCfg) { t.Error("Removing a folder requires restart") } newCfg = cfg newFolders := make([]FolderConfiguration, len(cfg.Folders)) copy(newFolders, cfg.Folders) newCfg.Folders = newFolders if ChangeRequiresRestart(cfg, newCfg) { t.Error("No changes done yet") } newCfg.Folders[0].Path = "different" if !ChangeRequiresRestart(cfg, newCfg) { t.Error("Changing a folder requires restart") } newCfg = cfg newDevices := make([]DeviceConfiguration, len(cfg.Devices)) copy(newDevices, cfg.Devices) newCfg.Devices = newDevices if ChangeRequiresRestart(cfg, newCfg) { t.Error("No changes done yet") } newCfg.Devices[0].Name = "different" if ChangeRequiresRestart(cfg, newCfg) { t.Error("Changing a device does not require restart") } newCfg = cfg newCfg.Options.GlobalAnnEnabled = !cfg.Options.GlobalAnnEnabled if !ChangeRequiresRestart(cfg, newCfg) { t.Error("Changing general options requires restart") } newCfg = cfg newCfg.GUI.UseTLS = !cfg.GUI.UseTLS if !ChangeRequiresRestart(cfg, newCfg) { t.Error("Changing GUI options requires restart") } }