286 lines
11 KiB
Text
286 lines
11 KiB
Text
(namespace test.path
|
|
(test.case "path.sep"
|
|
(assert.eq! path.sep (match platform.os-family (:windows "\\") (_ "/")))
|
|
)
|
|
|
|
(test.case "path.root"
|
|
;; Relative paths and empty string have no root on all platforms.
|
|
(assert.eq! (path.root "relative") "")
|
|
(assert.eq! (path.root "") "")
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
;; Rooted by backslash or forward-slash → canonical "\"
|
|
(assert.eq! (path.root "\\a\\b") "\\")
|
|
(assert.eq! (path.root "/a/b") "\\")
|
|
;; Drive-letter absolute ("X:\" or "X:/")
|
|
(assert.eq! (path.root "C:\\a\\b") "C:\\")
|
|
(assert.eq! (path.root "C:/a/b") "C:\\")
|
|
(assert.eq! (path.root "C:\\") "C:\\")
|
|
;; Drive letter WITHOUT a separator is relative on Windows
|
|
(assert.eq! (path.root "C:") "")
|
|
(assert.eq! (path.root "C:relative") "")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.root "/") "/")
|
|
(assert.eq! (path.root "/a/b") "/")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.absolute?"
|
|
;; "/" is recognised as absolute on all platforms.
|
|
(assert.ok! (path.absolute? "/absolute/unix"))
|
|
(assert.ok! (path.absolute? "/"))
|
|
(assert.not! (path.absolute? "relative/unix"))
|
|
(assert.not! (path.absolute? "./relative/unix"))
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
;; Drive-letter prefix (both separators)
|
|
(assert.ok! (path.absolute? "C:\\absolute\\windows"))
|
|
(assert.ok! (path.absolute? "C:/absolute/windows"))
|
|
;; UNC / rooted backslash
|
|
(assert.ok! (path.absolute? "\\absolute\\unc"))
|
|
;; Relative Windows paths
|
|
(assert.not! (path.absolute? "relative\\windows"))
|
|
(assert.not! (path.absolute? ".\\relative\\windows"))
|
|
;; Drive letter without separator is relative
|
|
(assert.not! (path.absolute? "C:relative"))
|
|
))
|
|
(_ (void))
|
|
)
|
|
)
|
|
|
|
(test.case "path.relative?"
|
|
(assert.ok! (path.relative? "relative/unix"))
|
|
(assert.not! (path.relative? "/absolute/unix"))
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.ok! (path.relative? "relative\\windows"))
|
|
(assert.not! (path.relative? "C:\\absolute\\windows"))
|
|
(assert.not! (path.relative? "\\absolute\\unc"))
|
|
(assert.ok! (path.relative? "C:relative"))
|
|
))
|
|
(_ (void))
|
|
)
|
|
)
|
|
|
|
(test.case "path.split"
|
|
(assert.eq! (path.split "") [])
|
|
(assert.eq! (path.split "a") ["a"])
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.eq! (path.split "a\\b\\c") ["a" "b" "c"])
|
|
;; Windows also accepts "/" as separator
|
|
(assert.eq! (path.split "a/b/c") ["a" "b" "c"])
|
|
;; Drive-letter prefix is kept as the first component (round-trip with join)
|
|
(assert.eq! (path.split "C:\\a\\b") ["C:" "a" "b"])
|
|
(assert.eq! (path.split "C:/a/b") ["C:" "a" "b"])
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.split "a/b/c") ["a" "b" "c"])
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.join"
|
|
(assert.eq! (path.join []) "")
|
|
(assert.eq! (path.join ["a"]) "a")
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.eq! (path.join ["a" "b" "c"]) "a\\b\\c")
|
|
(assert.eq! (path.join ["C:" "a" "b"]) "C:\\a\\b")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.join ["a" "b" "c"]) (str.concat "a" path.sep "b" path.sep "c"))
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.normalize"
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
;; Dot component is stripped
|
|
(assert.eq! (path.normalize "a\\.\\b") (path.join ["a" "b"]))
|
|
;; Double-dot collapses parent
|
|
(assert.eq! (path.normalize "a\\..\\b") "b")
|
|
;; Rooted-backslash absolute path
|
|
(assert.eq! (path.normalize "\\a\\..\\b") (str.concat path.sep "b"))
|
|
;; Bug fix: absolute path collapsing to root returns the root, not ""
|
|
(assert.eq! (path.normalize "\\") "\\")
|
|
(assert.eq! (path.normalize "\\a\\..") "\\")
|
|
;; Drive-letter paths: no spurious leading "\" added
|
|
(assert.eq! (path.normalize "C:\\a\\..\\b") "C:\\b")
|
|
(assert.eq! (path.normalize "C:\\") "C:\\")
|
|
(assert.eq! (path.normalize "C:\\a\\b\\..\\..") "C:\\")
|
|
;; Windows normalises "/" separators as well
|
|
(assert.eq! (path.normalize "a/./b") (path.join ["a" "b"]))
|
|
(assert.eq! (path.normalize "C:/a/../b") "C:\\b")
|
|
))
|
|
(_ (seq
|
|
;; Dot component is stripped
|
|
(assert.eq! (path.normalize "a/./b") (path.join ["a" "b"]))
|
|
;; Double-dot collapses parent
|
|
(assert.eq! (path.normalize "a/../b") "b")
|
|
;; Rooted absolute path
|
|
(assert.eq! (path.normalize "/a/../b") (str.concat path.sep "b"))
|
|
;; Bug fix: absolute path collapsing to root returns "/", not ""
|
|
(assert.eq! (path.normalize "/") "/")
|
|
(assert.eq! (path.normalize "/a/..") "/")
|
|
(assert.eq! (path.normalize "/a/b/../..") "/")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.parent"
|
|
;; Top-level relative path always returns "" on every platform.
|
|
(assert.eq! (path.parent "a") "")
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.eq! (path.parent "a\\b\\c") "a\\b")
|
|
(assert.eq! (path.parent "a\\b") "a")
|
|
;; Bug fix: rooted paths return "\" not hardcoded "/"
|
|
(assert.eq! (path.parent "\\") "\\")
|
|
(assert.eq! (path.parent "\\..\\a\\.\\..\\") "\\")
|
|
;; Drive-letter paths
|
|
(assert.eq! (path.parent "C:\\") "C:\\")
|
|
(assert.eq! (path.parent "C:\\a") "C:\\")
|
|
(assert.eq! (path.parent "C:\\a\\b") "C:\\a")
|
|
(assert.eq! (path.parent "C:\\a\\b\\c") "C:\\a\\b")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.parent "a/b/c") "a/b")
|
|
(assert.eq! (path.parent "a/b") "a")
|
|
(assert.eq! (path.parent "/") "/")
|
|
(assert.eq! (path.parent "/../a/./../") "/")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.basename"
|
|
(assert.eq! (path.basename "file.txt") "file.txt")
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.eq! (path.basename "a\\b\\c") "c")
|
|
;; Trailing separator → empty basename
|
|
(assert.eq! (path.basename "a\\b\\") "")
|
|
(assert.eq! (path.basename "C:\\Users\\file.txt") "file.txt")
|
|
;; Windows also handles "/" separator
|
|
(assert.eq! (path.basename "a/b/c") "c")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.basename "a/b/c") "c")
|
|
(assert.eq! (path.basename "a/b/") "")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.ext"
|
|
;; Basic cases work on all platforms (basename is a simple name)
|
|
(assert.eq! (path.ext "file.txt") ".txt")
|
|
(assert.eq! (path.ext "file") "")
|
|
(assert.eq! (path.ext ".hidden") "")
|
|
(assert.eq! (path.ext "archive.tar.gz") ".gz")
|
|
(assert.eq! (path.ext "file.") "")
|
|
(assert.eq! (path.ext "") "")
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.eq! (path.ext "C:\\Users\\file.txt") ".txt")
|
|
(assert.eq! (path.ext "a\\b\\no-ext") "")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.ext "a/b/file.txt") ".txt")
|
|
(assert.eq! (path.ext "a/b/no-ext") "")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.stem"
|
|
(assert.eq! (path.stem "file.txt") "file")
|
|
(assert.eq! (path.stem "file") "file")
|
|
(assert.eq! (path.stem ".hidden") ".hidden")
|
|
(assert.eq! (path.stem "archive.tar.gz") "archive.tar")
|
|
(assert.eq! (path.stem "") "")
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.eq! (path.stem "C:\\Users\\file.txt") "file")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.stem "a/b/file.txt") "file")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.with-ext"
|
|
(assert.eq! (path.with-ext "file.txt" ".md") "file.md")
|
|
(assert.eq! (path.with-ext "file.txt" "") "file")
|
|
(assert.eq! (path.with-ext "file" ".txt") "file.txt")
|
|
(assert.eq! (path.with-ext "archive.tar.gz" ".bz2") "archive.tar.bz2")
|
|
;; Hidden file: no extension → new-ext is appended
|
|
(assert.eq! (path.with-ext ".hidden" ".txt") ".hidden.txt")
|
|
;; Directory prefix is preserved
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
(assert.eq! (path.with-ext "C:\\a\\file.txt" ".md") "C:\\a\\file.md")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.with-ext "a/b/file.txt" ".md") "a/b/file.md")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.resolve"
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
;; Relative path is joined to base and normalized
|
|
(assert.eq! (path.resolve "C:\\base" "file.txt") "C:\\base\\file.txt")
|
|
;; ".." is resolved against base
|
|
(assert.eq! (path.resolve "C:\\base\\dir" "..\\file.txt") "C:\\base\\file.txt")
|
|
;; Absolute path ignores base
|
|
(assert.eq! (path.resolve "C:\\other" "C:\\absolute") "C:\\absolute")
|
|
;; Deep traversal
|
|
(assert.eq! (path.resolve "C:\\a\\b\\c" "..\\..\\d") "C:\\a\\d")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.resolve "/base" "file.txt") "/base/file.txt")
|
|
(assert.eq! (path.resolve "/base/dir" "../file.txt") "/base/file.txt")
|
|
;; Absolute path ignores base
|
|
(assert.eq! (path.resolve "/other" "/absolute") "/absolute")
|
|
;; Deep traversal
|
|
(assert.eq! (path.resolve "/a/b/c" "../../d") "/a/d")
|
|
;; Relative base + relative path
|
|
(assert.eq! (path.resolve "base/dir" "file.txt") "base/dir/file.txt")
|
|
))
|
|
)
|
|
)
|
|
|
|
(test.case "path.relative-to"
|
|
(match platform.os-family
|
|
(:windows (seq
|
|
;; Child path
|
|
(assert.eq! (path.relative-to "C:\\a\\b" "C:\\a\\b\\c\\d") "c\\d")
|
|
;; Sibling path
|
|
(assert.eq! (path.relative-to "C:\\a\\b" "C:\\a\\c") "..\\c")
|
|
;; Ancestor path
|
|
(assert.eq! (path.relative-to "C:\\a\\b\\c" "C:\\a") "..\\..")
|
|
;; Same path → "."
|
|
(assert.eq! (path.relative-to "C:\\a\\b" "C:\\a\\b") ".")
|
|
;; From root
|
|
(assert.eq! (path.relative-to "C:\\" "C:\\a\\b") "a\\b")
|
|
;; Different drives → return normalized target unchanged
|
|
(assert.eq! (path.relative-to "C:\\a" "D:\\b") "D:\\b")
|
|
))
|
|
(_ (seq
|
|
(assert.eq! (path.relative-to "/a/b" "/a/b/c/d") "c/d")
|
|
(assert.eq! (path.relative-to "/a/b" "/a/c") "../c")
|
|
(assert.eq! (path.relative-to "/a/b/c" "/a") "../..")
|
|
;; Same path → "."
|
|
(assert.eq! (path.relative-to "/a/b" "/a/b") ".")
|
|
;; From root
|
|
(assert.eq! (path.relative-to "/" "/a/b") "a/b")
|
|
;; Relative paths
|
|
(assert.eq! (path.relative-to "a/b" "a/b/c") "c")
|
|
(assert.eq! (path.relative-to "a/b" "a/c") "../c")
|
|
))
|
|
)
|
|
)
|
|
)
|