namespace DodoSSH.Client.Ssh.Tests; /// /// Remote paths, permission bits and byte counts — the three things the file browser renders. /// /// /// Not in the SSH collection, so this class needs no container. Every case here is one where the obvious /// implementation is wrong on Windows, at a filesystem root, or on a number that has just crossed a /// threshold — which is to say, one that a listing of somebody's home directory would not reveal. /// public sealed class RemotePathTests { [Theory] [InlineData("/var/log", "syslog", "/var/log/syslog")] [InlineData("/", "etc", "/etc")] [InlineData("/home/dodo/", "notes", "/home/dodo/notes")] public void Combine_JoinsWithExactlyOneSeparator(string directory, string name, string expected) { // Deliberately not Path.Combine, which on Windows would yield "/var/log\syslog" — a path the remote // cannot resolve, failing as "no such file" somewhere the backslash is invisible. SftpPath.Combine(directory, name).ShouldBe(expected); } [Theory] [InlineData("/var/log/syslog", "/var/log")] [InlineData("/var/log", "/var")] [InlineData("/var", "/")] [InlineData("/", "/")] [InlineData("/var/log/", "/var")] public void Parent_StopsAtTheRoot(string path, string expected) { // The root is its own parent rather than null, which is what lets the breadcrumb's "up" be a plain // navigation with nothing above it to special-case. SftpPath.Parent(path).ShouldBe(expected); } [Theory] [InlineData("/var/log/syslog", "syslog")] [InlineData("/var/log/", "log")] [InlineData("/", "/")] public void Name_IsTheLastSegment(string path, string expected) { SftpPath.Name(path).ShouldBe(expected); } [Fact] public void Trail_NamesEverySegmentWithThePathThatReachesIt() { SftpPath.Trail("/var/log/nginx").ShouldBe( [("var", "/var"), ("log", "/var/log"), ("nginx", "/var/log/nginx")]); } [Fact] public void Trail_IsEmptyAtTheRoot() { // The root has no segment to name. The breadcrumb draws it as a leading separator, so a trail with a // phantom empty crumb in it would render as a button with no label. SftpPath.Trail("/").ShouldBeEmpty(); } [Fact] public void PosixMode_RendersTheKindCharacterAndThreeTriples() { PosixMode.Format( SftpEntryKind.Directory, ownerRead: true, ownerWrite: true, ownerExecute: true, groupRead: true, groupWrite: false, groupExecute: true, othersRead: true, othersWrite: false, othersExecute: true) .ShouldBe("drwxr-xr-x"); PosixMode.Format( SftpEntryKind.File, ownerRead: true, ownerWrite: true, ownerExecute: false, groupRead: true, groupWrite: false, groupExecute: false, othersRead: false, othersWrite: false, othersExecute: false) .ShouldBe("-rw-r-----"); PosixMode.Format( SftpEntryKind.SymbolicLink, ownerRead: true, ownerWrite: true, ownerExecute: true, groupRead: true, groupWrite: true, groupExecute: true, othersRead: true, othersWrite: true, othersExecute: true) .ShouldBe("lrwxrwxrwx"); } [Theory] [InlineData(0, "0 B")] [InlineData(1023, "1023 B")] [InlineData(1024, "1.0 KB")] [InlineData(10 * 1024, "10 KB")] [InlineData(1536 * 1024, "1.5 MB")] [InlineData(5L * 1024 * 1024 * 1024, "5.0 GB")] public void ByteSize_KeepsOneDecimalOnlyWhileItMeansSomething(long bytes, string expected) { // The boundary at ten is the whole rule: "9.4 MB" and "512 MB" carry the same information, and // "512.3 MB" claims a precision the figure does not have by the time it is that large. ByteSize.Format(bytes).ShouldBe(expected); } }