// Mgmt // Copyright (C) James Shubin and the project contributors // Written by James Shubin and the project contributors // // 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 . // // Additional permission under GNU GPL version 3 section 7 // // If you modify this program, or any covered work, by linking or combining it // with embedded mcl code and modules (and that the embedded mcl code and // modules which link with this program, contain a copy of their source code in // the authoritative form) containing parts covered by the terms of any other // license, the licensors of this program grant you additional permission to // convey the resulting work. Furthermore, the licensors of this program grant // the original author, James Shubin, additional permission to update this // additional permission if he deems it necessary to achieve the goals of this // additional permission. //go:build !root package util import ( "reflect" "slices" "sort" "strings" "testing" ) func TestNumToAlpha(t *testing.T) { var numToAlphaTests = []struct { number int result string }{ {0, "a"}, {25, "z"}, {26, "aa"}, {27, "ab"}, {702, "aaa"}, {703, "aab"}, {63269, "cool"}, {-1, ""}, } for _, test := range numToAlphaTests { actual := NumToAlpha(test.number) if actual != test.result { t.Errorf("input: %d, expected: %s, actual: %s", test.number, test.result, actual) } } } func TestUtilT1(t *testing.T) { if Dirname("/foo/bar/baz") != "/foo/bar/" { t.Errorf("result is incorrect.") } if Dirname("/foo/bar/baz/") != "/foo/bar/" { t.Errorf("result is incorrect.") } if Dirname("/foo/") != "/" { t.Errorf("result is incorrect.") } if Dirname("/") != "" { // TODO: should this equal "/" or "" ? t.Errorf("result is incorrect.") } if Dirname("foo/bar.conf") != "foo/" { t.Errorf("result is incorrect.") } if Dirname("foo/bar/baz.conf") != "foo/bar/" { t.Errorf("result is incorrect.") } if Dirname("bar.conf") != "" { t.Errorf("result is incorrect.") } if Basename("/foo/bar/baz") != "baz" { t.Errorf("result is incorrect.") } if Basename("/foo/bar/baz/") != "baz/" { t.Errorf("result is incorrect.") } if Basename("/foo/") != "foo/" { t.Errorf("result is incorrect.") } if Basename("/") != "/" { // TODO: should this equal "" or "/" ? t.Errorf("result is incorrect.") } if Basename("") != "" { // TODO: should this equal something different? t.Errorf("result is incorrect.") } } func TestUtilT2(t *testing.T) { // TODO: compare the output with the actual list p0 := "/" r0 := []string{""} // TODO: is this correct? if len(PathSplit(p0)) != len(r0) { t.Errorf("result should be: %q.", r0) t.Errorf("result should have a length of: %v.", len(r0)) } p1 := "/foo/bar/baz" r1 := []string{"", "foo", "bar", "baz"} if len(PathSplit(p1)) != len(r1) { //t.Errorf("result should be: %q.", r1) t.Errorf("result should have a length of: %v.", len(r1)) } p2 := "/foo/bar/baz/" r2 := []string{"", "foo", "bar", "baz"} if len(PathSplit(p2)) != len(r2) { t.Errorf("result should have a length of: %v.", len(r2)) } } func TestUtilT3(t *testing.T) { if HasPathPrefix("/foo/bar/baz", "/foo/ba") != false { t.Errorf("result should be false.") } if HasPathPrefix("/foo/bar/baz", "/foo/bar") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo/bar/baz", "/foo/bar/") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo/bar/baz/", "/foo/bar") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo/bar/baz/", "/foo/bar/") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo/bar/baz/", "/foo/bar/baz/dude") != false { t.Errorf("result should be false.") } if HasPathPrefix("/foo/bar/baz/boo/", "/foo/") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo/bar/baz", "/foo/bar/baz") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo/bar/baz/", "/foo/bar/baz/") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo", "/foo") != true { t.Errorf("result should be true.") } if HasPathPrefix("/foo/", "/foo/") != true { t.Errorf("result should be true.") } if HasPathPrefix("/", "/") != true { t.Errorf("result should be true.") } } func TestUtilT4(t *testing.T) { if PathPrefixDelta("/foo/bar/baz", "/foo/ba") != -1 { t.Errorf("result should be -1.") } if PathPrefixDelta("/foo/bar/baz", "/foo/bar") != 1 { t.Errorf("result should be 1.") } if PathPrefixDelta("/foo/bar/baz", "/foo/bar/") != 1 { t.Errorf("result should be 1.") } if PathPrefixDelta("/foo/bar/baz/", "/foo/bar") != 1 { t.Errorf("result should be 1.") } if PathPrefixDelta("/foo/bar/baz/", "/foo/bar/") != 1 { t.Errorf("result should be 1.") } if PathPrefixDelta("/foo/bar/baz/", "/foo/bar/baz/dude") != -1 { t.Errorf("result should be -1.") } if PathPrefixDelta("/foo/bar/baz/a/b/c/", "/foo/bar/baz") != 3 { t.Errorf("result should be 3.") } if PathPrefixDelta("/foo/bar/baz/", "/foo/bar/baz") != 0 { t.Errorf("result should be 0.") } } func TestUtilT8(t *testing.T) { r0 := []string{"/"} if fullList0 := PathSplitFullReversed("/"); !reflect.DeepEqual(r0, fullList0) { t.Errorf("expected: %v; got: %v.", r0, fullList0) } r1 := []string{"/foo/bar/baz/file", "/foo/bar/baz/", "/foo/bar/", "/foo/", "/"} if fullList1 := PathSplitFullReversed("/foo/bar/baz/file"); !reflect.DeepEqual(r1, fullList1) { t.Errorf("expected: %v; got: %v.", r1, fullList1) } r2 := []string{"/foo/bar/baz/dir/", "/foo/bar/baz/", "/foo/bar/", "/foo/", "/"} if fullList2 := PathSplitFullReversed("/foo/bar/baz/dir/"); !reflect.DeepEqual(r2, fullList2) { t.Errorf("expected: %v; got: %v.", r2, fullList2) } } func TestUtilT9(t *testing.T) { fileListIn := []string{ // list taken from drbd-utils package "/etc/drbd.conf", "/etc/drbd.d/global_common.conf", "/lib/drbd/drbd", "/lib/drbd/drbdadm-83", "/lib/drbd/drbdadm-84", "/lib/drbd/drbdsetup-83", "/lib/drbd/drbdsetup-84", "/usr/lib/drbd/crm-fence-peer.sh", "/usr/lib/drbd/crm-unfence-peer.sh", "/usr/lib/drbd/notify-emergency-reboot.sh", "/usr/lib/drbd/notify-emergency-shutdown.sh", "/usr/lib/drbd/notify-io-error.sh", "/usr/lib/drbd/notify-out-of-sync.sh", "/usr/lib/drbd/notify-pri-lost-after-sb.sh", "/usr/lib/drbd/notify-pri-lost.sh", "/usr/lib/drbd/notify-pri-on-incon-degr.sh", "/usr/lib/drbd/notify-split-brain.sh", "/usr/lib/drbd/notify.sh", "/usr/lib/drbd/outdate-peer.sh", "/usr/lib/drbd/rhcs_fence", "/usr/lib/drbd/snapshot-resync-target-lvm.sh", "/usr/lib/drbd/stonith_admin-fence-peer.sh", "/usr/lib/drbd/unsnapshot-resync-target-lvm.sh", "/usr/lib/systemd/system/drbd.service", "/usr/lib/tmpfiles.d/drbd.conf", "/usr/sbin/drbd-overview", "/usr/sbin/drbdadm", "/usr/sbin/drbdmeta", "/usr/sbin/drbdsetup", "/usr/share/doc/drbd-utils/COPYING", "/usr/share/doc/drbd-utils/ChangeLog", "/usr/share/doc/drbd-utils/README", "/usr/share/doc/drbd-utils/drbd.conf.example", "/usr/share/man/man5/drbd.conf-8.3.5.gz", "/usr/share/man/man5/drbd.conf-8.4.5.gz", "/usr/share/man/man5/drbd.conf-9.0.5.gz", "/usr/share/man/man5/drbd.conf.5.gz", "/usr/share/man/man8/drbd-8.3.8.gz", "/usr/share/man/man8/drbd-8.4.8.gz", "/usr/share/man/man8/drbd-9.0.8.gz", "/usr/share/man/man8/drbd-overview-9.0.8.gz", "/usr/share/man/man8/drbd-overview.8.gz", "/usr/share/man/man8/drbd.8.gz", "/usr/share/man/man8/drbdadm-8.3.8.gz", "/usr/share/man/man8/drbdadm-8.4.8.gz", "/usr/share/man/man8/drbdadm-9.0.8.gz", "/usr/share/man/man8/drbdadm.8.gz", "/usr/share/man/man8/drbddisk-8.3.8.gz", "/usr/share/man/man8/drbddisk-8.4.8.gz", "/usr/share/man/man8/drbdmeta-8.3.8.gz", "/usr/share/man/man8/drbdmeta-8.4.8.gz", "/usr/share/man/man8/drbdmeta-9.0.8.gz", "/usr/share/man/man8/drbdmeta.8.gz", "/usr/share/man/man8/drbdsetup-8.3.8.gz", "/usr/share/man/man8/drbdsetup-8.4.8.gz", "/usr/share/man/man8/drbdsetup-9.0.8.gz", "/usr/share/man/man8/drbdsetup.8.gz", "/etc/drbd.d", "/usr/share/doc/drbd-utils", "/var/lib/drbd", } sort.Strings(fileListIn) fileListOut := []string{ // fixed up manually "/etc/drbd.conf", "/etc/drbd.d/global_common.conf", "/lib/drbd/drbd", "/lib/drbd/drbdadm-83", "/lib/drbd/drbdadm-84", "/lib/drbd/drbdsetup-83", "/lib/drbd/drbdsetup-84", "/usr/lib/drbd/crm-fence-peer.sh", "/usr/lib/drbd/crm-unfence-peer.sh", "/usr/lib/drbd/notify-emergency-reboot.sh", "/usr/lib/drbd/notify-emergency-shutdown.sh", "/usr/lib/drbd/notify-io-error.sh", "/usr/lib/drbd/notify-out-of-sync.sh", "/usr/lib/drbd/notify-pri-lost-after-sb.sh", "/usr/lib/drbd/notify-pri-lost.sh", "/usr/lib/drbd/notify-pri-on-incon-degr.sh", "/usr/lib/drbd/notify-split-brain.sh", "/usr/lib/drbd/notify.sh", "/usr/lib/drbd/outdate-peer.sh", "/usr/lib/drbd/rhcs_fence", "/usr/lib/drbd/snapshot-resync-target-lvm.sh", "/usr/lib/drbd/stonith_admin-fence-peer.sh", "/usr/lib/drbd/unsnapshot-resync-target-lvm.sh", "/usr/lib/systemd/system/drbd.service", "/usr/lib/tmpfiles.d/drbd.conf", "/usr/sbin/drbd-overview", "/usr/sbin/drbdadm", "/usr/sbin/drbdmeta", "/usr/sbin/drbdsetup", "/usr/share/doc/drbd-utils/COPYING", "/usr/share/doc/drbd-utils/ChangeLog", "/usr/share/doc/drbd-utils/README", "/usr/share/doc/drbd-utils/drbd.conf.example", "/usr/share/man/man5/drbd.conf-8.3.5.gz", "/usr/share/man/man5/drbd.conf-8.4.5.gz", "/usr/share/man/man5/drbd.conf-9.0.5.gz", "/usr/share/man/man5/drbd.conf.5.gz", "/usr/share/man/man8/drbd-8.3.8.gz", "/usr/share/man/man8/drbd-8.4.8.gz", "/usr/share/man/man8/drbd-9.0.8.gz", "/usr/share/man/man8/drbd-overview-9.0.8.gz", "/usr/share/man/man8/drbd-overview.8.gz", "/usr/share/man/man8/drbd.8.gz", "/usr/share/man/man8/drbdadm-8.3.8.gz", "/usr/share/man/man8/drbdadm-8.4.8.gz", "/usr/share/man/man8/drbdadm-9.0.8.gz", "/usr/share/man/man8/drbdadm.8.gz", "/usr/share/man/man8/drbddisk-8.3.8.gz", "/usr/share/man/man8/drbddisk-8.4.8.gz", "/usr/share/man/man8/drbdmeta-8.3.8.gz", "/usr/share/man/man8/drbdmeta-8.4.8.gz", "/usr/share/man/man8/drbdmeta-9.0.8.gz", "/usr/share/man/man8/drbdmeta.8.gz", "/usr/share/man/man8/drbdsetup-8.3.8.gz", "/usr/share/man/man8/drbdsetup-8.4.8.gz", "/usr/share/man/man8/drbdsetup-9.0.8.gz", "/usr/share/man/man8/drbdsetup.8.gz", "/etc/drbd.d/", // added trailing slash "/usr/share/doc/drbd-utils/", // added trailing slash "/var/lib/drbd", // can't be fixed :( } sort.Strings(fileListOut) dirify := DirifyFileList(fileListIn, false) // TODO: test with true sort.Strings(dirify) equals := reflect.DeepEqual(fileListOut, dirify) if a, b := len(fileListOut), len(dirify); a != b { t.Errorf("counts didn't match: %d != %d", a, b) } else if !equals { t.Errorf("did not match expected!") for i := 0; i < len(dirify); i++ { if fileListOut[i] != dirify[i] { t.Errorf("# %d: %v <> %v", i, fileListOut[i], dirify[i]) } } } } func TestUtilT10(t *testing.T) { fileListIn := []string{ // fake package list "/etc/drbd.conf", "/usr/share/man/man8/drbdsetup.8.gz", "/etc/drbd.d", "/etc/drbd.d/foo", "/var/lib/drbd", "/var/somedir/", } sort.Strings(fileListIn) fileListOut := []string{ // fixed up manually "/etc/drbd.conf", "/usr/share/man/man8/drbdsetup.8.gz", "/etc/drbd.d/", // added trailing slash "/etc/drbd.d/foo", "/var/lib/drbd", // can't be fixed :( "/var/somedir/", // stays the same } sort.Strings(fileListOut) dirify := DirifyFileList(fileListIn, false) // TODO: test with true sort.Strings(dirify) equals := reflect.DeepEqual(fileListOut, dirify) if a, b := len(fileListOut), len(dirify); a != b { t.Errorf("counts didn't match: %d != %d", a, b) } else if !equals { t.Errorf("did not match expected!") for i := 0; i < len(dirify); i++ { if fileListOut[i] != dirify[i] { t.Errorf("# %d: %v <> %v", i, fileListOut[i], dirify[i]) } } } } func TestUtilT11(t *testing.T) { in1 := []string{"/", "/usr/", "/usr/lib/", "/usr/share/"} // input ex1 := []string{"/usr/lib/", "/usr/share/"} // expected sort.Strings(ex1) out1 := RemoveCommonFilePrefixes(in1) sort.Strings(out1) if !reflect.DeepEqual(ex1, out1) { t.Errorf("expected: %v; got: %v.", ex1, out1) } in2 := []string{"/", "/usr/"} ex2 := []string{"/usr/"} sort.Strings(ex2) out2 := RemoveCommonFilePrefixes(in2) sort.Strings(out2) if !reflect.DeepEqual(ex2, out2) { t.Errorf("expected: %v; got: %v.", ex2, out2) } in3 := []string{"/"} ex3 := []string{"/"} out3 := RemoveCommonFilePrefixes(in3) if !reflect.DeepEqual(ex3, out3) { t.Errorf("expected: %v; got: %v.", ex3, out3) } in4 := []string{"/usr/bin/foo", "/usr/bin/bar", "/usr/lib/", "/usr/share/"} ex4 := []string{"/usr/bin/foo", "/usr/bin/bar", "/usr/lib/", "/usr/share/"} sort.Strings(ex4) out4 := RemoveCommonFilePrefixes(in4) sort.Strings(out4) if !reflect.DeepEqual(ex4, out4) { t.Errorf("expected: %v; got: %v.", ex4, out4) } in5 := []string{"/usr/bin/foo", "/usr/bin/bar", "/usr/lib/", "/usr/share/", "/usr/bin"} ex5 := []string{"/usr/bin/foo", "/usr/bin/bar", "/usr/lib/", "/usr/share/"} sort.Strings(ex5) out5 := RemoveCommonFilePrefixes(in5) sort.Strings(out5) if !reflect.DeepEqual(ex5, out5) { t.Errorf("expected: %v; got: %v.", ex5, out5) } in6 := []string{"/etc/drbd.d/", "/lib/drbd/", "/usr/lib/drbd/", "/usr/lib/systemd/system/", "/usr/lib/tmpfiles.d/", "/usr/sbin/", "/usr/share/doc/drbd-utils/", "/usr/share/man/man5/", "/usr/share/man/man8/", "/usr/share/doc/", "/var/lib/"} ex6 := []string{"/etc/drbd.d/", "/lib/drbd/", "/usr/lib/drbd/", "/usr/lib/systemd/system/", "/usr/lib/tmpfiles.d/", "/usr/sbin/", "/usr/share/doc/drbd-utils/", "/usr/share/man/man5/", "/usr/share/man/man8/", "/var/lib/"} sort.Strings(ex6) out6 := RemoveCommonFilePrefixes(in6) sort.Strings(out6) if !reflect.DeepEqual(ex6, out6) { t.Errorf("expected: %v; got: %v.", ex6, out6) } in7 := []string{"/etc/", "/lib/", "/usr/lib/", "/usr/lib/systemd/", "/usr/", "/usr/share/doc/", "/usr/share/man/", "/var/"} ex7 := []string{"/etc/", "/lib/", "/usr/lib/systemd/", "/usr/share/doc/", "/usr/share/man/", "/var/"} sort.Strings(ex7) out7 := RemoveCommonFilePrefixes(in7) sort.Strings(out7) if !reflect.DeepEqual(ex7, out7) { t.Errorf("expected: %v; got: %v.", ex7, out7) } in8 := []string{ "/etc/drbd.conf", "/etc/drbd.d/global_common.conf", "/lib/drbd/drbd", "/lib/drbd/drbdadm-83", "/lib/drbd/drbdadm-84", "/lib/drbd/drbdsetup-83", "/lib/drbd/drbdsetup-84", "/usr/lib/drbd/crm-fence-peer.sh", "/usr/lib/drbd/crm-unfence-peer.sh", "/usr/lib/drbd/notify-emergency-reboot.sh", "/usr/lib/drbd/notify-emergency-shutdown.sh", "/usr/lib/drbd/notify-io-error.sh", "/usr/lib/drbd/notify-out-of-sync.sh", "/usr/lib/drbd/notify-pri-lost-after-sb.sh", "/usr/lib/drbd/notify-pri-lost.sh", "/usr/lib/drbd/notify-pri-on-incon-degr.sh", "/usr/lib/drbd/notify-split-brain.sh", "/usr/lib/drbd/notify.sh", "/usr/lib/drbd/outdate-peer.sh", "/usr/lib/drbd/rhcs_fence", "/usr/lib/drbd/snapshot-resync-target-lvm.sh", "/usr/lib/drbd/stonith_admin-fence-peer.sh", "/usr/lib/drbd/unsnapshot-resync-target-lvm.sh", "/usr/lib/systemd/system/drbd.service", "/usr/lib/tmpfiles.d/drbd.conf", "/usr/sbin/drbd-overview", "/usr/sbin/drbdadm", "/usr/sbin/drbdmeta", "/usr/sbin/drbdsetup", "/usr/share/doc/drbd-utils/COPYING", "/usr/share/doc/drbd-utils/ChangeLog", "/usr/share/doc/drbd-utils/README", "/usr/share/doc/drbd-utils/drbd.conf.example", "/usr/share/man/man5/drbd.conf-8.3.5.gz", "/usr/share/man/man5/drbd.conf-8.4.5.gz", "/usr/share/man/man5/drbd.conf-9.0.5.gz", "/usr/share/man/man5/drbd.conf.5.gz", "/usr/share/man/man8/drbd-8.3.8.gz", "/usr/share/man/man8/drbd-8.4.8.gz", "/usr/share/man/man8/drbd-9.0.8.gz", "/usr/share/man/man8/drbd-overview-9.0.8.gz", "/usr/share/man/man8/drbd-overview.8.gz", "/usr/share/man/man8/drbd.8.gz", "/usr/share/man/man8/drbdadm-8.3.8.gz", "/usr/share/man/man8/drbdadm-8.4.8.gz", "/usr/share/man/man8/drbdadm-9.0.8.gz", "/usr/share/man/man8/drbdadm.8.gz", "/usr/share/man/man8/drbddisk-8.3.8.gz", "/usr/share/man/man8/drbddisk-8.4.8.gz", "/usr/share/man/man8/drbdmeta-8.3.8.gz", "/usr/share/man/man8/drbdmeta-8.4.8.gz", "/usr/share/man/man8/drbdmeta-9.0.8.gz", "/usr/share/man/man8/drbdmeta.8.gz", "/usr/share/man/man8/drbdsetup-8.3.8.gz", "/usr/share/man/man8/drbdsetup-8.4.8.gz", "/usr/share/man/man8/drbdsetup-9.0.8.gz", "/usr/share/man/man8/drbdsetup.8.gz", "/etc/drbd.d/", "/usr/share/doc/drbd-utils/", "/var/lib/drbd", } ex8 := []string{ "/etc/drbd.conf", "/etc/drbd.d/global_common.conf", "/lib/drbd/drbd", "/lib/drbd/drbdadm-83", "/lib/drbd/drbdadm-84", "/lib/drbd/drbdsetup-83", "/lib/drbd/drbdsetup-84", "/usr/lib/drbd/crm-fence-peer.sh", "/usr/lib/drbd/crm-unfence-peer.sh", "/usr/lib/drbd/notify-emergency-reboot.sh", "/usr/lib/drbd/notify-emergency-shutdown.sh", "/usr/lib/drbd/notify-io-error.sh", "/usr/lib/drbd/notify-out-of-sync.sh", "/usr/lib/drbd/notify-pri-lost-after-sb.sh", "/usr/lib/drbd/notify-pri-lost.sh", "/usr/lib/drbd/notify-pri-on-incon-degr.sh", "/usr/lib/drbd/notify-split-brain.sh", "/usr/lib/drbd/notify.sh", "/usr/lib/drbd/outdate-peer.sh", "/usr/lib/drbd/rhcs_fence", "/usr/lib/drbd/snapshot-resync-target-lvm.sh", "/usr/lib/drbd/stonith_admin-fence-peer.sh", "/usr/lib/drbd/unsnapshot-resync-target-lvm.sh", "/usr/lib/systemd/system/drbd.service", "/usr/lib/tmpfiles.d/drbd.conf", "/usr/sbin/drbd-overview", "/usr/sbin/drbdadm", "/usr/sbin/drbdmeta", "/usr/sbin/drbdsetup", "/usr/share/doc/drbd-utils/COPYING", "/usr/share/doc/drbd-utils/ChangeLog", "/usr/share/doc/drbd-utils/README", "/usr/share/doc/drbd-utils/drbd.conf.example", "/usr/share/man/man5/drbd.conf-8.3.5.gz", "/usr/share/man/man5/drbd.conf-8.4.5.gz", "/usr/share/man/man5/drbd.conf-9.0.5.gz", "/usr/share/man/man5/drbd.conf.5.gz", "/usr/share/man/man8/drbd-8.3.8.gz", "/usr/share/man/man8/drbd-8.4.8.gz", "/usr/share/man/man8/drbd-9.0.8.gz", "/usr/share/man/man8/drbd-overview-9.0.8.gz", "/usr/share/man/man8/drbd-overview.8.gz", "/usr/share/man/man8/drbd.8.gz", "/usr/share/man/man8/drbdadm-8.3.8.gz", "/usr/share/man/man8/drbdadm-8.4.8.gz", "/usr/share/man/man8/drbdadm-9.0.8.gz", "/usr/share/man/man8/drbdadm.8.gz", "/usr/share/man/man8/drbddisk-8.3.8.gz", "/usr/share/man/man8/drbddisk-8.4.8.gz", "/usr/share/man/man8/drbdmeta-8.3.8.gz", "/usr/share/man/man8/drbdmeta-8.4.8.gz", "/usr/share/man/man8/drbdmeta-9.0.8.gz", "/usr/share/man/man8/drbdmeta.8.gz", "/usr/share/man/man8/drbdsetup-8.3.8.gz", "/usr/share/man/man8/drbdsetup-8.4.8.gz", "/usr/share/man/man8/drbdsetup-9.0.8.gz", "/usr/share/man/man8/drbdsetup.8.gz", "/var/lib/drbd", } sort.Strings(ex8) out8 := RemoveCommonFilePrefixes(in8) sort.Strings(out8) if !reflect.DeepEqual(ex8, out8) { t.Errorf("expected: %v; got: %v.", ex8, out8) } in9 := []string{ "/etc/drbd.conf", "/etc/drbd.d/", "/lib/drbd/drbd", "/lib/drbd/", "/lib/drbd/", "/lib/drbd/", "/usr/lib/drbd/", "/usr/lib/drbd/", "/usr/lib/drbd/", "/usr/lib/drbd/", "/usr/lib/drbd/", "/usr/lib/systemd/system/", "/usr/lib/tmpfiles.d/", "/usr/sbin/", "/usr/sbin/", "/usr/share/doc/drbd-utils/", "/usr/share/doc/drbd-utils/", "/usr/share/man/man5/", "/usr/share/man/man5/", "/usr/share/man/man8/", "/usr/share/man/man8/", "/usr/share/man/man8/", "/etc/drbd.d/", "/usr/share/doc/drbd-utils/", "/var/lib/drbd", } ex9 := []string{ "/etc/drbd.conf", "/etc/drbd.d/", "/lib/drbd/drbd", "/usr/lib/drbd/", "/usr/lib/systemd/system/", "/usr/lib/tmpfiles.d/", "/usr/sbin/", "/usr/share/doc/drbd-utils/", "/usr/share/man/man5/", "/usr/share/man/man8/", "/var/lib/drbd", } sort.Strings(ex9) out9 := RemoveCommonFilePrefixes(in9) sort.Strings(out9) if !reflect.DeepEqual(ex9, out9) { t.Errorf("expected: %v; got: %v.", ex9, out9) } in10 := []string{ "/etc/drbd.conf", "/etc/drbd.d/", // watch me, i'm a dir "/etc/drbd.d/global_common.conf", // and watch me i'm a file! "/lib/drbd/drbd", "/lib/drbd/drbdadm-83", "/lib/drbd/drbdadm-84", "/lib/drbd/drbdsetup-83", "/lib/drbd/drbdsetup-84", "/usr/lib/drbd/crm-fence-peer.sh", "/usr/lib/drbd/crm-unfence-peer.sh", "/usr/lib/drbd/notify-emergency-reboot.sh", "/usr/lib/drbd/notify-emergency-shutdown.sh", "/usr/lib/drbd/notify-io-error.sh", "/usr/lib/drbd/notify-out-of-sync.sh", "/usr/lib/drbd/notify-pri-lost-after-sb.sh", "/usr/lib/drbd/notify-pri-lost.sh", "/usr/lib/drbd/notify-pri-on-incon-degr.sh", "/usr/lib/drbd/notify-split-brain.sh", "/usr/lib/drbd/notify.sh", "/usr/lib/drbd/outdate-peer.sh", "/usr/lib/drbd/rhcs_fence", "/usr/lib/drbd/snapshot-resync-target-lvm.sh", "/usr/lib/drbd/stonith_admin-fence-peer.sh", "/usr/lib/drbd/unsnapshot-resync-target-lvm.sh", "/usr/lib/systemd/system/drbd.service", "/usr/lib/tmpfiles.d/drbd.conf", "/usr/sbin/drbd-overview", "/usr/sbin/drbdadm", "/usr/sbin/drbdmeta", "/usr/sbin/drbdsetup", "/usr/share/doc/drbd-utils/", // watch me, i'm a dir too "/usr/share/doc/drbd-utils/COPYING", "/usr/share/doc/drbd-utils/ChangeLog", "/usr/share/doc/drbd-utils/README", "/usr/share/doc/drbd-utils/drbd.conf.example", "/usr/share/man/man5/drbd.conf-8.3.5.gz", "/usr/share/man/man5/drbd.conf-8.4.5.gz", "/usr/share/man/man5/drbd.conf-9.0.5.gz", "/usr/share/man/man5/drbd.conf.5.gz", "/usr/share/man/man8/drbd-8.3.8.gz", "/usr/share/man/man8/drbd-8.4.8.gz", "/usr/share/man/man8/drbd-9.0.8.gz", "/usr/share/man/man8/drbd-overview-9.0.8.gz", "/usr/share/man/man8/drbd-overview.8.gz", "/usr/share/man/man8/drbd.8.gz", "/usr/share/man/man8/drbdadm-8.3.8.gz", "/usr/share/man/man8/drbdadm-8.4.8.gz", "/usr/share/man/man8/drbdadm-9.0.8.gz", "/usr/share/man/man8/drbdadm.8.gz", "/usr/share/man/man8/drbddisk-8.3.8.gz", "/usr/share/man/man8/drbddisk-8.4.8.gz", "/usr/share/man/man8/drbdmeta-8.3.8.gz", "/usr/share/man/man8/drbdmeta-8.4.8.gz", "/usr/share/man/man8/drbdmeta-9.0.8.gz", "/usr/share/man/man8/drbdmeta.8.gz", "/usr/share/man/man8/drbdsetup-8.3.8.gz", "/usr/share/man/man8/drbdsetup-8.4.8.gz", "/usr/share/man/man8/drbdsetup-9.0.8.gz", "/usr/share/man/man8/drbdsetup.8.gz", "/var/lib/drbd", } ex10 := []string{ "/etc/drbd.conf", "/etc/drbd.d/global_common.conf", "/lib/drbd/drbd", "/lib/drbd/drbdadm-83", "/lib/drbd/drbdadm-84", "/lib/drbd/drbdsetup-83", "/lib/drbd/drbdsetup-84", "/usr/lib/drbd/crm-fence-peer.sh", "/usr/lib/drbd/crm-unfence-peer.sh", "/usr/lib/drbd/notify-emergency-reboot.sh", "/usr/lib/drbd/notify-emergency-shutdown.sh", "/usr/lib/drbd/notify-io-error.sh", "/usr/lib/drbd/notify-out-of-sync.sh", "/usr/lib/drbd/notify-pri-lost-after-sb.sh", "/usr/lib/drbd/notify-pri-lost.sh", "/usr/lib/drbd/notify-pri-on-incon-degr.sh", "/usr/lib/drbd/notify-split-brain.sh", "/usr/lib/drbd/notify.sh", "/usr/lib/drbd/outdate-peer.sh", "/usr/lib/drbd/rhcs_fence", "/usr/lib/drbd/snapshot-resync-target-lvm.sh", "/usr/lib/drbd/stonith_admin-fence-peer.sh", "/usr/lib/drbd/unsnapshot-resync-target-lvm.sh", "/usr/lib/systemd/system/drbd.service", "/usr/lib/tmpfiles.d/drbd.conf", "/usr/sbin/drbd-overview", "/usr/sbin/drbdadm", "/usr/sbin/drbdmeta", "/usr/sbin/drbdsetup", "/usr/share/doc/drbd-utils/COPYING", "/usr/share/doc/drbd-utils/ChangeLog", "/usr/share/doc/drbd-utils/README", "/usr/share/doc/drbd-utils/drbd.conf.example", "/usr/share/man/man5/drbd.conf-8.3.5.gz", "/usr/share/man/man5/drbd.conf-8.4.5.gz", "/usr/share/man/man5/drbd.conf-9.0.5.gz", "/usr/share/man/man5/drbd.conf.5.gz", "/usr/share/man/man8/drbd-8.3.8.gz", "/usr/share/man/man8/drbd-8.4.8.gz", "/usr/share/man/man8/drbd-9.0.8.gz", "/usr/share/man/man8/drbd-overview-9.0.8.gz", "/usr/share/man/man8/drbd-overview.8.gz", "/usr/share/man/man8/drbd.8.gz", "/usr/share/man/man8/drbdadm-8.3.8.gz", "/usr/share/man/man8/drbdadm-8.4.8.gz", "/usr/share/man/man8/drbdadm-9.0.8.gz", "/usr/share/man/man8/drbdadm.8.gz", "/usr/share/man/man8/drbddisk-8.3.8.gz", "/usr/share/man/man8/drbddisk-8.4.8.gz", "/usr/share/man/man8/drbdmeta-8.3.8.gz", "/usr/share/man/man8/drbdmeta-8.4.8.gz", "/usr/share/man/man8/drbdmeta-9.0.8.gz", "/usr/share/man/man8/drbdmeta.8.gz", "/usr/share/man/man8/drbdsetup-8.3.8.gz", "/usr/share/man/man8/drbdsetup-8.4.8.gz", "/usr/share/man/man8/drbdsetup-9.0.8.gz", "/usr/share/man/man8/drbdsetup.8.gz", "/var/lib/drbd", } sort.Strings(ex10) out10 := RemoveCommonFilePrefixes(in10) sort.Strings(out10) if !reflect.DeepEqual(ex10, out10) { t.Errorf("expected: %v; got: %v.", ex10, out10) for i := 0; i < len(ex10); i++ { if ex10[i] != out10[i] { t.Errorf("# %d: %v <> %v", i, ex10[i], out10[i]) } } } } func TestSegmentedPathSplit(t *testing.T) { if ex, out := []string{}, SegmentedPathSplit( "", ); !reflect.DeepEqual(out, ex) { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := []string{"/"}, SegmentedPathSplit( "/", ); !reflect.DeepEqual(out, ex) { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := []string{"/", "foo/", "bar/"}, SegmentedPathSplit( "/foo/bar/", ); !reflect.DeepEqual(out, ex) { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := []string{"/", "foo/", "bar"}, SegmentedPathSplit( "/foo/bar", ); !reflect.DeepEqual(out, ex) { t.Errorf("expected: %v got: %v", ex, out) } } func TestCommonPathPrefix1(t *testing.T) { if ex, out := "/foo/whatever2/", CommonPathPrefix( "/foo/whatever2/", "/foo/whatever2/", "/foo/whatever2/", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } } func TestCommonPathPrefix2(t *testing.T) { if ex, out := "/whatever1", CommonPathPrefix( "/whatever1", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/whatever2", CommonPathPrefix( "/whatever2", "/whatever2", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/foo/whatever1", CommonPathPrefix( "/foo/whatever1", "/foo/whatever1", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/foo/whatever2", CommonPathPrefix( "/foo/whatever2", "/foo/whatever2", "/foo/whatever2", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/whatever3/", CommonPathPrefix( "/whatever3/", "/whatever3/", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/foo/whatever3/", CommonPathPrefix( "/foo/whatever3/", "/foo/whatever3/", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/foo/whatever4/", CommonPathPrefix( "/foo/whatever4/", "/foo/whatever4/", "/foo/whatever4/", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/", CommonPathPrefix( "/foo/bar", "/bar/baz/", "/baz/bing/wow", ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } if ex, out := "/foo/", CommonPathPrefix( "/foo/bar/", "/foo/bar/dude", "/foo/bar", // this is not the same as /foo/bar/ ! ); out != ex { t.Errorf("expected: %v got: %v", ex, out) } // If we want to "safe clean" each path, then this test should be added. //if ex, out := "/home/james/tmp/", CommonPathPrefix( // "/home/james/tmp/coverage/test", // "/home/james/tmp/covert/operator", // "/home/james/tmp/coven/members", // "/home//james/tmp/coventry", // "/home/james/././tmp/covertly/foo", // "/home/luser/../james/tmp/coved/bar", //); out != ex { // t.Errorf("expected: %v got: %v", ex, out) //} } func TestUtilFlattenListWithSplit1(t *testing.T) { { in := []string{} // input ex := []string{} // expected out := FlattenListWithSplit(in, []string{",", ";", " "}) sort.Strings(out) sort.Strings(ex) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } { in := []string{"hey"} // input ex := []string{"hey"} // expected out := FlattenListWithSplit(in, []string{",", ";", " "}) sort.Strings(out) sort.Strings(ex) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } { in := []string{"a", "b", "c", "d"} // input ex := []string{"a", "b", "c", "d"} // expected out := FlattenListWithSplit(in, []string{",", ";", " "}) sort.Strings(out) sort.Strings(ex) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } { in := []string{"a,b,c,d"} // input ex := []string{"a", "b", "c", "d"} // expected out := FlattenListWithSplit(in, []string{",", ";", " "}) sort.Strings(out) sort.Strings(ex) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } { in := []string{"a,b;c d"} // input (mixed) ex := []string{"a", "b", "c", "d"} // expected out := FlattenListWithSplit(in, []string{",", ";", " "}) sort.Strings(out) sort.Strings(ex) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } { in := []string{"a,b,c,d;e,f,g,h;i,j,k,l;m,n,o,p q,r,s,t;u,v,w,x y z"} // input (mixed) ex := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} // expected out := FlattenListWithSplit(in, []string{",", ";", " "}) sort.Strings(out) sort.Strings(ex) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } } func TestRemoveBasePath0(t *testing.T) { // expected successes... if s, err := RemoveBasePath("/usr/bin/foo", "/usr/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "bin/foo" { t.Errorf("unexpected string, got: %s", s) } if s, err := RemoveBasePath("/usr/bin/project/", "/usr/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "bin/project/" { t.Errorf("unexpected string, got: %s", s) } if s, err := RemoveBasePath("/", "/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "" { // TODO: is this correct? t.Errorf("unexpected string, got: %s", s) } if s, err := RemoveBasePath("/usr/bin/project/", "/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "usr/bin/project/" { t.Errorf("unexpected string, got: %s", s) } if s, err := RemoveBasePath("/usr/bin/project/", "/usr/bin/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "project/" { t.Errorf("unexpected string, got: %s", s) } if s, err := RemoveBasePath("/usr/bin/foo", "/usr/bin/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "foo" { t.Errorf("unexpected string, got: %s", s) } // allow this one, even though it's relative paths if s, err := RemoveBasePath("usr/bin/project/", "usr/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "bin/project/" { t.Errorf("unexpected string, got: %s", s) } // expected errors... if s, err := RemoveBasePath("", ""); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := RemoveBasePath("", "/usr/"); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := RemoveBasePath("usr/bin/project/", ""); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := RemoveBasePath("usr/bin/project/", "/usr/"); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := RemoveBasePath("/usr/bin/project/", "usr/"); err == nil { t.Errorf("expected error, got: %s", s) } // allow this one, even though it's relative paths //if s, err := RemoveBasePath("usr/bin/project/", "usr/"); err == nil { // t.Errorf("expected error, got: %s", s) //} if s, err := RemoveBasePath("/usr/bin/project/", "/bin/"); err == nil { t.Errorf("expected error, got: %s", s) } } func TestRebasePath0(t *testing.T) { // expected successes... if s, err := Rebase("/usr/bin/foo", "/usr/", "/usr/local/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "/usr/local/bin/foo" { t.Errorf("unexpected string, got: %s", s) } if s, err := Rebase("/usr/bin/project/", "/usr/", "/usr/local/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "/usr/local/bin/project/" { t.Errorf("unexpected string, got: %s", s) } if s, err := Rebase("/", "/", "/opt/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "/opt/" { // TODO: is this correct? t.Errorf("unexpected string, got: %s", s) } if s, err := Rebase("/usr/bin/project/", "/", "/opt/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "/opt/usr/bin/project/" { t.Errorf("unexpected string, got: %s", s) } if s, err := Rebase("/usr/bin/project/", "/usr/bin/", "/opt/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "/opt/project/" { t.Errorf("unexpected string, got: %s", s) } if s, err := Rebase("/usr/bin/foo", "/usr/bin/", "/opt/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "/opt/foo" { t.Errorf("unexpected string, got: %s", s) } // allow this one, even though it's relative paths if s, err := Rebase("usr/bin/project/", "usr/", "/opt/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "/opt/bin/project/" { t.Errorf("unexpected string, got: %s", s) } // empty root to build a relative dir path if s, err := Rebase("/var/lib/dir/file.conf", "/var/lib/", ""); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "dir/file.conf" { t.Errorf("unexpected string, got: %s", s) } // expected errors... if s, err := Rebase("", "", "/opt/"); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := Rebase("", "/usr/", "/opt/"); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := Rebase("usr/bin/project/", "", "/opt/"); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := Rebase("usr/bin/project/", "/usr/", "/opt/"); err == nil { t.Errorf("expected error, got: %s", s) } if s, err := Rebase("/usr/bin/project/", "usr/", "/opt/"); err == nil { t.Errorf("expected error, got: %s", s) } // allow this one, even though it's relative paths //if s, err := Rebase("usr/bin/project/", "usr/", "/opt/"); err == nil { // t.Errorf("expected error, got: %s", s) //} if s, err := Rebase("/usr/bin/project/", "/bin/", "/opt/"); err == nil { t.Errorf("expected error, got: %s", s) } // formerly a failure: //if s, err := Rebase("/usr/bin/project", "/usr/", ""); err == nil { // t.Errorf("expected error, got: %s", s) //} // replaced with a valid result instead: if s, err := Rebase("/usr/bin/project", "/usr/", ""); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "bin/project" { t.Errorf("unexpected string, got: %s", s) } if s, err := Rebase("/usr/bin/project/", "/usr/", ""); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "bin/project/" { t.Errorf("unexpected string, got: %s", s) } if s, err := Rebase("/usr/bin/project/", "/usr/", "foo/bar/"); err != nil { t.Errorf("unexpected error: %v", err) } else if s != "foo/bar/bin/project/" { t.Errorf("unexpected string, got: %s", s) } } func TestRemovePathPrefix0(t *testing.T) { testCases := []struct { in string out string }{ { in: "/simple1", out: "/", }, { in: "/simple1/foo/bar", out: "/foo/bar", }, { in: "/simple1/foo/bar/", out: "/foo/bar/", }, } for _, test := range testCases { out, err := RemovePathPrefix(test.in) if err != nil { t.Errorf("error: %+v", err) continue } if test.out != out { t.Errorf("failed: %s -> %s", test.in, out) continue } } } func TestRemovePathSuffix0(t *testing.T) { testCases := []struct { in string out string }{ { in: "/simple1/", out: "/", }, { in: "/simple1/foo/bar/", out: "/simple1/foo/", }, { in: "/simple1/foo/", out: "/simple1/", }, // TODO: are these what we want? { in: "/simple1/foo", out: "/simple1/", }, { in: "/simple1", out: "/", }, } for _, test := range testCases { out, err := RemovePathSuffix(test.in) if err != nil { t.Errorf("error: %+v", err) continue } if test.out != out { t.Errorf("failed: %s -> %s (exp: %s)", test.in, out, test.out) continue } } } func TestDirParents0(t *testing.T) { tests := []struct { in string out []string }{ { in: "", out: nil, }, { in: "/", out: []string{}, }, { in: "/tmp/x1/mod1/files/", out: []string{ "/", "/tmp/", "/tmp/x1/", "/tmp/x1/mod1/", }, }, { in: "/tmp/x1/mod1/files/foo", out: []string{ "/", "/tmp/", "/tmp/x1/", "/tmp/x1/mod1/", "/tmp/x1/mod1/files/", }, }, } for index, tt := range tests { result := DirParents(tt.in) if a, b := len(result), len(tt.out); a != b { t.Errorf("test #%d: expected length differs (%d != %d)", index, a, b) t.Errorf("test #%d: actual: %+v", index, result) t.Errorf("test #%d: expected: %+v", index, tt.out) break } for i := range result { if result[i] != tt.out[i] { t.Errorf("test #%d: parents diff: wanted: %s got: %s", index, tt.out[i], result[i]) } } } } func TestMissingMkdirs0(t *testing.T) { tests := []struct { in []string out []string fail bool }{ { in: []string{}, out: []string{}, }, { in: []string{ "/", }, out: []string{}, }, { in: []string{ "/tmp/x1/metadata.yaml", "/tmp/x1/main.mcl", "/tmp/x1/files/", "/tmp/x1/second.mcl", "/tmp/x1/mod1/metadata.yaml", "/tmp/x1/mod1/main.mcl", "/tmp/x1/mod1/files/", }, out: []string{ "/", "/tmp/", "/tmp/x1/", "/tmp/x1/mod1/", }, }, { in: []string{ "/tmp/x1/files/", "/tmp/x1/main.mcl", "/tmp/x1/metadata.yaml", "/tmp/x1/mod1/files/", "/tmp/x1/mod1/main.mcl", "/tmp/x1/mod1/metadata.yaml", "/tmp/x1/second.mcl", }, out: []string{ "/", "/tmp/", "/tmp/x1/", "/tmp/x1/mod1/", }, }, { in: []string{ "/tmp/x1/files/", "/tmp/x1/files/a/b/c/", }, out: []string{ "/", "/tmp/", "/tmp/x1/", //"/tmp/x1/files/", // already exists! "/tmp/x1/files/a/", "/tmp/x1/files/a/b/", }, }, { in: []string{ "/tmp/x1/files/", "/tmp/x1/files/a/b/c/", "/tmp/x1/files/a/b/c/", "/tmp/x1/files/a/b/c/", "/tmp/x1/files/a/b/c/", // duplicates }, out: []string{ "/", "/tmp/", "/tmp/x1/", //"/tmp/x1/files/", // already exists! "/tmp/x1/files/a/", "/tmp/x1/files/a/b/", }, }, { in: []string{ "/tmp/x1/files/", "/tmp/x1/files/a/b/c/d1", "/tmp/x1/files/a/b/c/d2", }, out: []string{ "/", "/tmp/", "/tmp/x1/", //"/tmp/x1/files/", // already exists! "/tmp/x1/files/a/", "/tmp/x1/files/a/b/", "/tmp/x1/files/a/b/c/", }, }, { in: []string{ "/tmp/x1/files/", "/tmp/x1/files/a/b/c/d1", "/tmp/x1/files/a/b/c/d1", "/tmp/x1/files/a/b/c/d1", "/tmp/x1/files/a/b/c/d1", // duplicates! "/tmp/x1/files/a/b/c/d2", }, out: []string{ "/", "/tmp/", "/tmp/x1/", //"/tmp/x1/files/", // already exists! "/tmp/x1/files/a/", "/tmp/x1/files/a/b/", "/tmp/x1/files/a/b/c/", }, }, { in: []string{ "/tmp/x1/files/", "/tmp/x1/files/a/b/c/d1", "/tmp/x1/files/a/b/c/d1", "/tmp/x1/files/a/b/c/d1", "/tmp/x1/files/a/b/c/d1", // duplicates! "/tmp/x1/files/a/b/", "/tmp/x1/files/a/b/c/d2", }, out: []string{ "/", "/tmp/", "/tmp/x1/", //"/tmp/x1/files/", // already exists! "/tmp/x1/files/a/", "/tmp/x1/files/a/b/c/", }, }, // invalid path list, so undefined results //{ // in: []string{ // "/tmp/x1/files/", // "/tmp/x1/files/a/b/c/d", // "/tmp/x1/files/a/b/c/d/", // error: same name as file // "/tmp/x1/files/a/b/c/d1", // }, // out: []string{}, // fail: true, // TODO: put a specific error? //}, // TODO: add more tests } for index, tt := range tests { result, err := MissingMkdirs(tt.in) if !tt.fail && err != nil { t.Errorf("test #%d: failed with: %+v", index, err) break } if tt.fail && err == nil { t.Errorf("test #%d: passed, expected fail", index) break } if !tt.fail && result == nil { t.Errorf("test #%d: output was nil", index) break } if a, b := len(result), len(tt.out); a != b { t.Errorf("test #%d: expected length differs (%d != %d)", index, a, b) t.Errorf("test #%d: actual: %+v", index, result) t.Errorf("test #%d: expected: %+v", index, tt.out) break } for i := range result { if result[i] != tt.out[i] { t.Errorf("test #%d: missing mkdirs diff: wanted: %s got: %s", index, tt.out[i], result[i]) } } } } func TestPriorityStrSliceSort0(t *testing.T) { in := []string{"foo", "bar", "baz"} ex := []string{"bar", "baz", "foo"} fn := func(x string) bool { return x == "foo" } out := PriorityStrSliceSort(in, fn) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } func TestPriorityStrSliceSort1(t *testing.T) { in := []string{"foo", "bar", "baz"} ex := []string{"bar", "foo", "baz"} fn := func(x string) bool { return x != "bar" // != brings this key to the front } out := PriorityStrSliceSort(in, fn) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } func TestPriorityStrSliceSort2(t *testing.T) { in := []string{"bar", "foo", "bar", "bar", "baz"} ex := []string{"foo", "baz", "bar", "bar", "bar"} fn := func(x string) bool { return x == "bar" } out := PriorityStrSliceSort(in, fn) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } func TestPriorityStrSliceSort3(t *testing.T) { in := []string{"foo", "bar1", "bar2", "bar3", "baz"} ex := []string{"bar1", "bar2", "bar3", "foo", "baz"} fn := func(x string) bool { return !strings.HasPrefix(x, "bar") } out := PriorityStrSliceSort(in, fn) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } func TestPriorityStrSliceSort4(t *testing.T) { in := []string{"foo", "bar1", "bar2", "bar3", "baz"} ex := []string{"foo", "baz", "bar1", "bar2", "bar3"} fn := func(x string) bool { return strings.HasPrefix(x, "bar") } out := PriorityStrSliceSort(in, fn) if !reflect.DeepEqual(ex, out) { t.Errorf("expected: %v; got: %v.", ex, out) } } func TestSortedStrSliceCompare0(t *testing.T) { slice0 := []string{"foo", "bar", "baz"} slice1 := []string{"bar", "foo", "baz"} if err := SortedStrSliceCompare(slice0, slice1); err != nil { t.Errorf("slices were not evaluated as equivalent: %v, %v", slice0, slice1) } } func TestSortedStrSliceCompare1(t *testing.T) { slice0 := []string{"foo", "bar", "baz"} slice1 := []string{"fi", "fi", "fo"} if err := SortedStrSliceCompare(slice0, slice1); err == nil { t.Errorf("slices were evaluated as equivalent: %v, %v", slice0, slice1) } } func TestSortedStrSliceCompare2(t *testing.T) { slice0 := []string{"foo", "bar", "baz"} slice1 := []string{"foo", "bar"} if err := SortedStrSliceCompare(slice0, slice1); err == nil { t.Errorf("slices were evaluated as equivalent: %v, %v", slice0, slice1) } } func TestSortedStrSliceCompare3(t *testing.T) { slice0 := []string{"foo", "bar", "baz"} slice1 := []string{"zip", "zap", "zop"} _ = SortedStrSliceCompare(slice0, slice1) if slice0[0] != "foo" || slice0[1] != "bar" || slice0[2] != "baz" { t.Errorf("input slice reordered to: %v", slice0) } if slice1[0] != "zip" || slice1[1] != "zap" || slice1[2] != "zop" { t.Errorf("input slice reordered to: %v", slice1) } } func TestPathSliceSort(t *testing.T) { tests := []struct { in []string out []string }{ { in: []string{ "/foo/bar/baz", "/bing/bang/boom", "/1/2/3/", "/foo/bar/raz", "/bing/buzz/", "/foo/", "/", "/1/", "/foo/bar/baz/bam", "/bing/bang/", "/1/2/", "/foo/bar/", "/bing/", }, out: []string{ "/", "/1/", "/1/2/", "/1/2/3/", "/bing/", "/bing/bang/", "/bing/bang/boom", "/bing/buzz/", "/foo/", "/foo/bar/", "/foo/bar/baz", "/foo/bar/baz/bam", "/foo/bar/raz", }, }, } for _, tt := range tests { sort.Sort(PathSlice(tt.in)) for i := range tt.in { if tt.in[i] != tt.out[i] { t.Errorf("path sort failed: wanted: %s got: %s", tt.out[i], tt.in[i]) } } } } func TestSortUInt64Slice(t *testing.T) { slice0 := []uint64{42, 13, 0} sort.Sort(UInt64Slice(slice0)) if slice0[0] != 0 || slice0[1] != 13 || slice0[2] != 42 { t.Errorf("input slice reordered to: %v", slice0) } slice1 := []uint64{99, 12, 13} sort.Sort(UInt64Slice(slice1)) if slice1[0] != 12 || slice1[1] != 13 || slice1[2] != 99 { t.Errorf("input slice reordered to: %v", slice1) } } func TestSortMapStringValuesByUInt64Keys(t *testing.T) { if x := len(SortMapStringValuesByUInt64Keys(nil)); x != 0 { t.Errorf("input map of nil caused a: %d", x) } map0 := map[uint64]string{ 42: "world", 34: "there", 13: "hello", } slice0 := SortMapStringValuesByUInt64Keys(map0) if slice0[0] != "hello" || slice0[1] != "there" || slice0[2] != "world" { t.Errorf("input slice reordered to: %v", slice0) } map1 := map[uint64]string{ 99: "a", 12: "c", 13: "b", } slice1 := SortMapStringValuesByUInt64Keys(map1) if slice1[0] != "c" || slice1[1] != "b" || slice1[2] != "a" { t.Errorf("input slice reordered to: %v", slice1) } map2 := map[uint64]string{ 12: "c", 0: "d", 44442: "b", } slice2 := SortMapStringValuesByUInt64Keys(map2) if slice2[0] != "d" || slice2[1] != "c" || slice2[2] != "b" { t.Errorf("input slice reordered to: %v", slice2) } } func TestFirstToUpper(t *testing.T) { tests := []struct { name string input string want string }{ { name: "empty string", input: "", want: "", }, { name: "lowercase word", input: "small", want: "Small", }, { name: "capitalized word", input: "CAPITAL", want: "CAPITAL", }, { name: "capitalized first letter", input: "First", want: "First", }, { name: "lowercase first letter", input: "fIRST", want: "FIRST", }, { name: "number", input: "0number", want: "0number", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := FirstToUpper(tt.input) if got != tt.want { t.Errorf("got: %s, want: %s", got, tt.want) } }) } } func TestUint64KeyFromStrInMap(t *testing.T) { type input struct { needle string haystack map[uint64]string } type want struct { key uint64 exist bool } tests := []struct { name string input input want want }{ { name: `needle "n" in empty haystack`, input: input{ needle: "n", haystack: make(map[uint64]string), }, want: want{ key: 0, exist: false, }, }, { name: `needle "n" in haystack doesn't contain "n"`, input: input{ needle: "n", haystack: map[uint64]string{0: "a", 1: "b"}, }, want: want{ key: 0, exist: false, }, }, { name: `needle "n" in haystack contain "n"`, input: input{ needle: "n", haystack: map[uint64]string{0: "a", 1: "b", 2: "n"}, }, want: want{ key: 2, exist: true, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotKey, gotExist := Uint64KeyFromStrInMap(tt.input.needle, tt.input.haystack) if gotKey != tt.want.key { t.Errorf("got key: %d, want key: %d", gotKey, tt.want.key) } if gotExist != tt.want.exist { t.Errorf("got exist: %t, want exist: %t", gotExist, tt.want.exist) } }) } } func TestStrFilterElementsInList(t *testing.T) { type input struct { filter []string list []string } tests := []struct { name string input input want []string }{ { name: "empty filter", input: input{ filter: []string{}, list: []string{"first", "second"}, }, want: []string{"first", "second"}, }, { name: "empty list", input: input{ filter: []string{"filter"}, list: []string{}, }, want: []string{}, }, { name: "nil", input: input{ filter: nil, list: nil, }, want: []string{}, }, { name: "filter", input: input{ filter: []string{"filter"}, list: []string{"first", "second", "filter"}, }, want: []string{"first", "second"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := StrFilterElementsInList(tt.input.filter, tt.input.list) if !slices.Equal(got, tt.want) { t.Errorf("got: %s, want: %s", got, tt.want) } }) } } func TestStrListIntersection(t *testing.T) { type input struct { list1 []string list2 []string } tests := []struct { name string input input want []string }{ { name: "nil", input: input{ list1: nil, list2: nil, }, want: []string{}, }, { name: "no intersection elements", input: input{ list1: []string{"one", "two"}, list2: []string{"three", "four"}, }, want: []string{}, }, { name: "contains intersection element", input: input{ list1: []string{"one", "two"}, list2: []string{"two", "three"}, }, want: []string{"two"}, }, { name: "all intersection elements", input: input{ list1: []string{"one", "two"}, list2: []string{"one", "two"}, }, want: []string{"one", "two"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := StrListIntersection(tt.input.list1, tt.input.list2) if !slices.Equal(got, tt.want) { t.Errorf("got: %s, want: %s", got, tt.want) } }) } } func TestStrMapKeys(t *testing.T) { tests := []struct { name string input map[string]string want []string }{ { name: "nil", input: nil, want: []string{}, }, { name: "empty map", input: map[string]string{}, want: []string{}, }, { name: "returns sorted keys", input: map[string]string{"key1": "value1", "key2": "value2"}, want: []string{"key1", "key2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := StrMapKeys(tt.input) if !slices.Equal(got, tt.want) { t.Errorf("got: %s, want: %s", got, tt.want) } }) } } func TestStrMapKeysUint64(t *testing.T) { tests := []struct { name string input map[string]uint64 want []string }{ { name: "nil", input: nil, want: []string{}, }, { name: "empty map", input: map[string]uint64{}, want: []string{}, }, { name: "returns sorted keys", input: map[string]uint64{"key1": 1, "key2": 2}, want: []string{"key1", "key2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := StrMapKeysUint64(tt.input) if !slices.Equal(got, tt.want) { t.Errorf("got: %s, want: %s", got, tt.want) } }) } } func TestBoolMapValues(t *testing.T) { tests := []struct { name string input map[string]bool want []bool }{ { name: "nil", input: nil, want: []bool{}, }, { name: "empty map", input: map[string]bool{}, want: []bool{}, }, { name: "return values unordered", input: map[string]bool{"key1": true}, want: []bool{true}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := BoolMapValues(tt.input) if !slices.Equal(got, tt.want) { t.Errorf("got: %v, want: %v", got, tt.want) } }) } } func TestStrMapValues(t *testing.T) { tests := []struct { name string input map[string]string want []string }{ { name: "nil", input: nil, want: []string{}, }, { name: "empty map", input: map[string]string{}, want: []string{}, }, { name: "return values", input: map[string]string{"key1": "value1", "key2": "value2"}, want: []string{"value1", "value2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := StrMapValues(tt.input) if !slices.Equal(got, tt.want) { t.Errorf("got: %v, want: %v", got, tt.want) } }) } } func TestStrMapValuesUint64(t *testing.T) { tests := []struct { name string input map[uint64]string want []string }{ { name: "nil", input: nil, want: []string{}, }, { name: "empty map", input: map[uint64]string{}, want: []string{}, }, { name: "return values", input: map[uint64]string{1: "value1", 2: "value2"}, want: []string{"value1", "value2"}, }, } for _, tt := range tests { got := StrMapValuesUint64(tt.input) if !slices.Equal(got, tt.want) { t.Errorf("got: %v, want: %v", got, tt.want) } } } func TestBoolMapTrue(t *testing.T) { tests := []struct { name string input []bool want bool }{ { name: "nil", input: nil, want: true, }, { name: "empty slice", input: []bool{}, want: true, }, { name: "all true return true", input: []bool{true, true, true}, want: true, }, { name: "contain false return false", input: []bool{true, false, true}, want: false, }, { name: "all false return false", input: []bool{false, false, false}, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := BoolMapTrue(tt.input) if got != tt.want { t.Errorf("got: %v, want: %v", got, tt.want) } }) } } func TestSafePathClean(t *testing.T) { tests := []struct { name string input string want string }{ { name: "empty", input: "", want: ".", }, { name: "slash", input: "/", want: "/", }, { name: "end with slash", input: "a//b/", want: "a/b/", }, { name: "end without slash", input: "a//b", want: "a/b", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := SafePathClean(tt.input) if got != tt.want { t.Errorf("got: %v, want: %v", got, tt.want) } }) } } func TestCommonPathPrefix(t *testing.T) { tests := []struct { name string input []string want string }{ { name: "common path", input: []string{"/common/uncommon1", "/common/uncommon2"}, want: "/common/", }, { name: "empty", input: []string{}, want: "", }, { name: "single path", input: []string{"/path/to"}, want: "/path/to", }, // XXX: currently undefined behaviour //{ // name: "single path doesn't start with /", // input: []string{"path/to"}, // want: "path/to", //}, { name: "one of the paths doesn't contain /", input: []string{"/path/with/slash", "path/without/slash"}, want: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := CommonPathPrefix(tt.input...) if got != tt.want { t.Errorf("got: %v, want: %v", got, tt.want) } }) } } func TestFlattenListWithSplit(t *testing.T) { type input struct { input []string split []string } tests := []struct { name string input input want []string }{ { name: "split by spaces and dots", input: input{ input: []string{"a b.c"}, split: []string{" ", "."}, }, want: []string{"a", "b", "c"}, }, { name: "empty split", input: input{ input: []string{"a b.c"}, split: []string{}, }, want: []string{"a b.c"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := FlattenListWithSplit(tt.input.input, tt.input.split) if !slices.Equal(got, tt.want) { t.Errorf("got: %v, want: %v", got, tt.want) } }) } } func TestRebase(t *testing.T) { type input struct { path string base string root string } type want struct { rebasedPath string err error } tests := []struct { name string input input want want }{ { name: "rebased to absolute directory", input: input{ path: "/usr/bin/foo", base: "/usr/", root: "/usr/local/", }, want: want{ rebasedPath: "/usr/local/bin/foo", err: nil, }, }, { name: "rebased to relative directory", input: input{ path: "/var/lib/dir/file.conf", base: "/var/lib/", root: "", }, want: want{ rebasedPath: "dir/file.conf", err: nil, }, }, } for _, tt := range tests { gotStr, gotErr := Rebase(tt.input.path, tt.input.base, tt.input.root) if gotStr != tt.want.rebasedPath { t.Errorf("got rebased path: %v, want rebased path: %v", gotStr, tt.want.rebasedPath) } if gotErr != tt.want.err { t.Errorf("got error: %v, want error to be: %v", gotErr, tt.want.err) } } t.Run("root doesn't end with /", func(t *testing.T) { // XXX: in Rebase function return a predefined error // e.g. var ErrRootNotDirectory = errors.New("root is not a directory") // so it would be clearer and easier to test gotStr, gotErr := Rebase("/usr/bin/foo", "/user/", "/usr/local") if gotStr != "" { t.Errorf("rebased path should be empty") } if gotErr.Error() != "root is not a directory" { t.Errorf(`should receive error: "root is not a directory"`) } }) } func TestRemovePathPrefix(t *testing.T) { t.Run("removes path prefix", func(t *testing.T) { gotStr, gotErr := RemovePathPrefix("/removed/path/to") if gotStr != "/path/to" { t.Errorf("got: %v, want: %v", gotStr, gotErr) } if gotErr != nil { t.Errorf("got error: %v, want nil error", gotErr) } }) t.Run("relative path", func(t *testing.T) { gotStr, gotErr := RemovePathPrefix("path/to") if gotStr != "" { t.Errorf("got: %v, want empty string", gotStr) } if gotErr.Error() != "must be absolute" { t.Errorf(`got error: %v, want error "must be absolute"`, gotErr.Error()) } }) // XXX: edge cases currently panic, handle edge cases. "/", "" } func TestRemovePathSuffix(t *testing.T) { t.Run("removes path prefix", func(t *testing.T) { gotStr, gotErr := RemovePathSuffix("/path/to/removed") if gotStr != "/path/to/" { t.Errorf("got: %v, want: %v", gotStr, "/path/to/") } if gotErr != nil { t.Errorf("got error: %v, want nil error", gotErr) } }) t.Run("relative path", func(t *testing.T) { gotStr, gotErr := RemovePathSuffix("path/to") if gotStr != "" { t.Errorf("got: %v, want empty string", gotStr) } if gotErr.Error() != "must be absolute" { t.Errorf(`got error: %v, want error "must be absolute"`, gotErr.Error()) } }) t.Run("/", func(t *testing.T) { gotStr, gotErr := RemovePathSuffix("/") if gotStr != "" { t.Errorf("got: %v, want empty string", gotStr) } if gotErr.Error() != "input is /" { t.Errorf(`got error: %v, want error "input is /"`, gotErr.Error()) } }) // XXX: double check desired behavior for edge cases. "/", "" } func TestSystemBusPrivateUsable(t *testing.T) { t.Run("return conn", func(t *testing.T) { conn, err := SystemBusPrivateUsable() if conn == nil { t.Errorf("got conn %v", conn) } if err != nil { t.Errorf("got error %v", err) } }) // XXX: testing other cases require refactoring(dependency injection, mock provider) } func TestSessionBusPrivateUsable(t *testing.T) { t.Run("return conn", func(t *testing.T) { conn, err := SessionBusPrivateUsable() if conn == nil { t.Errorf("got conn %v", conn) } if err != nil { t.Errorf("got error %v", err) } }) // XXX: testing other cases require refactoring(dependency injection, mock provider) } func TestPathSliceSortMethod(t *testing.T) { s := PathSlice{"/c", "/b", "/a"} s.Sort() if s[0] != "/a" || s[1] != "/b" || s[2] != "/c" { t.Errorf("function PathSlice.Sort did not sort correctly, got: %v", s) } } func TestUInt64SliceSortMethod(t *testing.T) { s := UInt64Slice{3, 2, 1} s.Sort() if s[0] != 1 || s[1] != 2 || s[2] != 3 { t.Errorf("function UInt64Slice.Sort did not sort correctly, got: %v", s) } } func TestPathSliceLessMethod(t *testing.T) { type input struct { s PathSlice i int j int } tests := []struct { name string input input want bool }{ { name: "less", input: input{ s: PathSlice{"/a", "/b"}, i: 0, j: 1, }, want: true, }, { name: "not less", input: input{ s: PathSlice{"/b", "/a"}, i: 0, j: 1, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.input.s.Less(tt.input.i, tt.input.j) if got != tt.want { t.Errorf("got: %v, want: %v", got, tt.want) } }) } // XXX: check other test cases (unable to reach the code) } func TestValueToB64(t *testing.T) { t.Run("value to b64", func(t *testing.T) { v := "value" str, err := ValueToB64(v) valueInB64 := "EhAABnN0cmluZwwHAAV2YWx1ZQ==" if str != valueInB64 { t.Errorf("got: %v, want: %v", str, valueInB64) } if err != nil { t.Errorf("didn't expect error, got %v", err) } }) t.Run("passing function", func(t *testing.T) { str, err := ValueToB64(func() {}) if str != "" { t.Errorf("wanted empty string, got: %v", str) } if !strings.Contains(err.Error(), "gob failed to encode") { t.Errorf(`expected error to contain "gob failed to encode", got: %v`, err.Error()) } }) } func TestB64ToValue(t *testing.T) { t.Run("b64 to value", func(t *testing.T) { b64 := "EhAABnN0cmluZwwHAAV2YWx1ZQ==" str, err := B64ToValue(b64) value := "value" if str != value { t.Errorf("got: %v, want: %v", str, value) } if err != nil { t.Errorf("didn't expect error, got %v", err) } }) t.Run("invalid b64", func(t *testing.T) { i, err := B64ToValue("invalid value") if i != nil { t.Errorf("wanted empty string, got: %v", i) } if !strings.Contains(err.Error(), "base64 failed to decode") { t.Errorf(`expected error to contain "base64 failed to decode", got: %v`, err.Error()) } }) t.Run("invalid gob", func(t *testing.T) { i, err := B64ToValue("dmFsdWU=") if i != nil { t.Errorf("wanted empty string, got: %v", i) } if !strings.Contains(err.Error(), "gob failed to decode") { t.Errorf(`expected error to contain "gob failed to decode", got: %v`, err.Error()) } }) // XXX: check unreachable case: "output `%v` is not a value" }