Add frontmatter, markdown, path, xml, rss, and web packages
Sync local packages into the registry repo and update index.json and README.md to include all 9 packages.
This commit is contained in:
3
packages/path/.gitignore
vendored
Normal file
3
packages/path/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
_site/
|
||||
.lux_packages/
|
||||
*.bak
|
||||
93
packages/path/lib.lux
Normal file
93
packages/path/lib.lux
Normal file
@@ -0,0 +1,93 @@
|
||||
// path - File path utilities for Lux
|
||||
|
||||
// Get the last component of a path (filename)
|
||||
// basename("/foo/bar/baz.txt") => "baz.txt"
|
||||
// basename("file.txt") => "file.txt"
|
||||
pub fn basename(path: String): String =
|
||||
match String.lastIndexOf(path, "/") {
|
||||
Some(idx) => String.substring(path, idx + 1, String.length(path)),
|
||||
None => path,
|
||||
}
|
||||
|
||||
// Get the directory portion of a path
|
||||
// dirname("/foo/bar/baz.txt") => "/foo/bar"
|
||||
// dirname("file.txt") => "."
|
||||
pub fn dirname(path: String): String =
|
||||
match String.lastIndexOf(path, "/") {
|
||||
Some(idx) => if idx == 0 then "/" else String.substring(path, 0, idx),
|
||||
None => ".",
|
||||
}
|
||||
|
||||
// Get the file extension (without the dot)
|
||||
// extension("file.txt") => Some("txt")
|
||||
// extension("file") => None
|
||||
// extension("file.tar.gz") => Some("gz")
|
||||
pub fn extension(path: String): Option<String> = {
|
||||
let name = basename(path)
|
||||
match String.lastIndexOf(name, ".") {
|
||||
Some(idx) => if idx == 0 then None
|
||||
else Some(String.substring(name, idx + 1, String.length(name))),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the file extension
|
||||
// stripExtension("file.txt") => "file"
|
||||
// stripExtension("file") => "file"
|
||||
// stripExtension("/foo/bar.txt") => "/foo/bar"
|
||||
pub fn stripExtension(path: String): String =
|
||||
match String.lastIndexOf(path, ".") {
|
||||
Some(dotIdx) => match String.lastIndexOf(path, "/") {
|
||||
Some(slashIdx) => if dotIdx > slashIdx then String.substring(path, 0, dotIdx) else path,
|
||||
None => String.substring(path, 0, dotIdx),
|
||||
},
|
||||
None => path,
|
||||
}
|
||||
|
||||
// Join two path components with a separator
|
||||
// join("/foo", "bar") => "/foo/bar"
|
||||
// join("/foo/", "bar") => "/foo/bar"
|
||||
// join("", "bar") => "bar"
|
||||
pub fn join(a: String, b: String): String =
|
||||
if a == "" then b
|
||||
else if b == "" then a
|
||||
else if String.endsWith(a, "/") then
|
||||
if String.startsWith(b, "/") then a + String.substring(b, 1, String.length(b))
|
||||
else a + b
|
||||
else if String.startsWith(b, "/") then a + b
|
||||
else a + "/" + b
|
||||
|
||||
// Check if a path has a given extension
|
||||
// hasExtension("file.txt", "txt") => true
|
||||
// hasExtension("file.md", "txt") => false
|
||||
pub fn hasExtension(path: String, ext: String): Bool =
|
||||
String.endsWith(path, "." + ext)
|
||||
|
||||
// Replace the file extension
|
||||
// replaceExtension("file.txt", "md") => "file.md"
|
||||
// replaceExtension("file", "md") => "file.md"
|
||||
pub fn replaceExtension(path: String, newExt: String): String =
|
||||
stripExtension(path) + "." + newExt
|
||||
|
||||
// Get the filename without extension (stem)
|
||||
// stem("file.txt") => "file"
|
||||
// stem("/foo/bar.txt") => "bar"
|
||||
// stem("file") => "file"
|
||||
pub fn stem(path: String): String = {
|
||||
let name = basename(path)
|
||||
match String.lastIndexOf(name, ".") {
|
||||
Some(idx) => if idx == 0 then name
|
||||
else String.substring(name, 0, idx),
|
||||
None => name,
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a path is absolute (starts with /)
|
||||
pub fn isAbsolute(path: String): Bool =
|
||||
String.startsWith(path, "/")
|
||||
|
||||
// Check if a path is relative (does not start with /)
|
||||
pub fn isRelative(path: String): Bool =
|
||||
if path == "" then true
|
||||
else if String.startsWith(path, "/") then false
|
||||
else true
|
||||
8
packages/path/lux.toml
Normal file
8
packages/path/lux.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[project]
|
||||
name = "path"
|
||||
version = "0.1.0"
|
||||
description = "File path utilities for Lux"
|
||||
authors = ["Brandon Lucas"]
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
88
packages/path/test_integration.lux
Normal file
88
packages/path/test_integration.lux
Normal file
@@ -0,0 +1,88 @@
|
||||
import lib
|
||||
|
||||
// Integration tests: realistic path manipulation workflows
|
||||
|
||||
// Build a full output path from components
|
||||
fn test_build_output_path(): Unit with {Test} = {
|
||||
let srcFile = "/home/user/project/src/pages/about.md"
|
||||
let outDir = "/home/user/project/_site"
|
||||
let name = lib.stem(srcFile)
|
||||
let outFile = lib.join(outDir, lib.join(name, "index.html"))
|
||||
Test.assertEqualMsg("/home/user/project/_site/about/index.html", outFile, "build output path from source")
|
||||
}
|
||||
|
||||
// Convert all markdown files to HTML paths
|
||||
fn test_extension_swap_workflow(): Unit with {Test} = {
|
||||
let file = "/content/posts/hello-world.md"
|
||||
let result = lib.replaceExtension(file, "html")
|
||||
Test.assertEqualMsg("/content/posts/hello-world.html", result, "swap .md to .html")
|
||||
Test.assertEqualMsg("html", match lib.extension(result) { Some(e) => e, None => "" }, "verify new extension")
|
||||
}
|
||||
|
||||
// Decompose a path and reconstruct it
|
||||
fn test_decompose_reconstruct(): Unit with {Test} = {
|
||||
let original = "/var/log/app/error.log"
|
||||
let dir = lib.dirname(original)
|
||||
let name = lib.basename(original)
|
||||
let reconstructed = lib.join(dir, name)
|
||||
Test.assertEqualMsg(original, reconstructed, "decompose and reconstruct path")
|
||||
}
|
||||
|
||||
// Process a list of file paths: extract stems
|
||||
fn test_batch_stem_extraction(): Unit with {Test} = {
|
||||
let s1 = lib.stem("/posts/hello.md")
|
||||
let s2 = lib.stem("/posts/world.md")
|
||||
let s3 = lib.stem("/posts/readme.txt")
|
||||
Test.assertEqualMsg("hello", s1, "batch stem 1")
|
||||
Test.assertEqualMsg("world", s2, "batch stem 2")
|
||||
Test.assertEqualMsg("readme", s3, "batch stem 3")
|
||||
}
|
||||
|
||||
// Create sibling file path (same dir, different name)
|
||||
fn test_sibling_file(): Unit with {Test} = {
|
||||
let original = "/assets/css/main.css"
|
||||
let dir = lib.dirname(original)
|
||||
let sibling = lib.join(dir, "reset.css")
|
||||
Test.assertEqualMsg("/assets/css/reset.css", sibling, "sibling file in same directory")
|
||||
}
|
||||
|
||||
// Multi-level path construction
|
||||
fn test_multi_level_join(): Unit with {Test} = {
|
||||
let base = "/var/www"
|
||||
let path = lib.join(lib.join(lib.join(base, "html"), "blog"), "index.html")
|
||||
Test.assertEqualMsg("/var/www/html/blog/index.html", path, "multi-level path join")
|
||||
Test.assertEqualMsg("/var/www/html/blog", lib.dirname(path), "dirname of multi-level")
|
||||
Test.assertEqualMsg("index.html", lib.basename(path), "basename of multi-level")
|
||||
Test.assert(lib.isAbsolute(path), "multi-level path is absolute")
|
||||
}
|
||||
|
||||
// Verify path properties are consistent
|
||||
fn test_path_property_consistency(): Unit with {Test} = {
|
||||
let path = "src/components/Button.tsx"
|
||||
Test.assertEqualMsg(true, lib.isRelative(path), "is relative")
|
||||
Test.assertEqualMsg(false, lib.isAbsolute(path), "is not absolute")
|
||||
Test.assertEqualMsg(true, lib.hasExtension(path, "tsx"), "has .tsx extension")
|
||||
Test.assertEqualMsg(false, lib.hasExtension(path, "ts"), "does not have .ts extension")
|
||||
Test.assertEqualMsg("Button", lib.stem(path), "stem is Button")
|
||||
Test.assertEqualMsg("src/components", lib.dirname(path), "dirname is src/components")
|
||||
}
|
||||
|
||||
// Swap extension and verify roundtrip
|
||||
fn test_extension_roundtrip(): Unit with {Test} = {
|
||||
let original = "report.csv"
|
||||
let swapped = lib.replaceExtension(original, "json")
|
||||
Test.assertEqualMsg("report.json", swapped, "csv -> json")
|
||||
let back = lib.replaceExtension(swapped, "csv")
|
||||
Test.assertEqualMsg("report.csv", back, "json -> csv roundtrip")
|
||||
}
|
||||
|
||||
// Build a path for a static site generator output
|
||||
fn test_ssg_path_pipeline(): Unit with {Test} = {
|
||||
let inputFile = "content/blog/my-first-post.md"
|
||||
let slug = lib.stem(inputFile)
|
||||
let outputDir = lib.join("_site", slug)
|
||||
let outputFile = lib.join(outputDir, "index.html")
|
||||
Test.assertEqualMsg("my-first-post", slug, "extract slug")
|
||||
Test.assertEqualMsg("_site/my-first-post", outputDir, "output directory")
|
||||
Test.assertEqualMsg("_site/my-first-post/index.html", outputFile, "full output path")
|
||||
}
|
||||
72
packages/path/test_path.lux
Normal file
72
packages/path/test_path.lux
Normal file
@@ -0,0 +1,72 @@
|
||||
import lib
|
||||
|
||||
fn isSome(opt: Option<String>, expected: String): Bool =
|
||||
match opt {
|
||||
Some(v) => v == expected,
|
||||
None => false,
|
||||
}
|
||||
|
||||
fn isNone(opt: Option<String>): Bool =
|
||||
match opt {
|
||||
Some(_) => false,
|
||||
None => true,
|
||||
}
|
||||
|
||||
fn test_basename(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("baz.txt", lib.basename("/foo/bar/baz.txt"), "basename with dirs")
|
||||
Test.assertEqualMsg("file.txt", lib.basename("file.txt"), "basename no dir")
|
||||
Test.assertEqualMsg("", lib.basename("/foo/bar/"), "basename trailing slash")
|
||||
Test.assertEqualMsg("", lib.basename(""), "basename empty")
|
||||
}
|
||||
|
||||
fn test_dirname(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("/foo/bar", lib.dirname("/foo/bar/baz.txt"), "dirname with dirs")
|
||||
Test.assertEqualMsg(".", lib.dirname("file.txt"), "dirname no dir")
|
||||
Test.assertEqualMsg("/", lib.dirname("/file.txt"), "dirname root")
|
||||
}
|
||||
|
||||
fn test_extension(): Unit with {Test} = {
|
||||
Test.assert(isSome(lib.extension("file.txt"), "txt"), "extension simple")
|
||||
Test.assert(isSome(lib.extension("file.tar.gz"), "gz"), "extension double")
|
||||
Test.assert(isNone(lib.extension("file")), "extension none")
|
||||
Test.assert(isSome(lib.extension("/foo/bar.txt"), "txt"), "extension with dir")
|
||||
Test.assert(isNone(lib.extension(".hidden")), "extension dotfile")
|
||||
}
|
||||
|
||||
fn test_strip_extension(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("file", lib.stripExtension("file.txt"), "stripExtension simple")
|
||||
Test.assertEqualMsg("file", lib.stripExtension("file"), "stripExtension none")
|
||||
Test.assertEqualMsg("/foo/bar", lib.stripExtension("/foo/bar.txt"), "stripExtension with dir")
|
||||
}
|
||||
|
||||
fn test_join(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("/foo/bar", lib.join("/foo", "bar"), "join basic")
|
||||
Test.assertEqualMsg("/foo/bar", lib.join("/foo/", "bar"), "join trailing slash")
|
||||
Test.assertEqualMsg("/foo/bar", lib.join("/foo", "/bar"), "join leading slash")
|
||||
Test.assertEqualMsg("/foo/bar", lib.join("/foo/", "/bar"), "join both slashes")
|
||||
Test.assertEqualMsg("bar", lib.join("", "bar"), "join empty first")
|
||||
Test.assertEqualMsg("foo", lib.join("foo", ""), "join empty second")
|
||||
}
|
||||
|
||||
fn test_has_extension(): Unit with {Test} = {
|
||||
Test.assertEqualMsg(true, lib.hasExtension("file.txt", "txt"), "hasExtension true")
|
||||
Test.assertEqualMsg(false, lib.hasExtension("file.md", "txt"), "hasExtension false")
|
||||
}
|
||||
|
||||
fn test_replace_extension(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("file.md", lib.replaceExtension("file.txt", "md"), "replaceExtension simple")
|
||||
Test.assertEqualMsg("file.md", lib.replaceExtension("file", "md"), "replaceExtension no ext")
|
||||
}
|
||||
|
||||
fn test_stem(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("file", lib.stem("file.txt"), "stem simple")
|
||||
Test.assertEqualMsg("bar", lib.stem("/foo/bar.txt"), "stem with dir")
|
||||
Test.assertEqualMsg("file", lib.stem("file"), "stem no ext")
|
||||
}
|
||||
|
||||
fn test_absolute_relative(): Unit with {Test} = {
|
||||
Test.assertEqualMsg(true, lib.isAbsolute("/foo/bar"), "isAbsolute true")
|
||||
Test.assertEqualMsg(false, lib.isAbsolute("foo/bar"), "isAbsolute false")
|
||||
Test.assertEqualMsg(true, lib.isRelative("foo/bar"), "isRelative true")
|
||||
Test.assertEqualMsg(false, lib.isRelative("/foo/bar"), "isRelative false")
|
||||
}
|
||||
65
packages/path/test_snapshot.lux
Normal file
65
packages/path/test_snapshot.lux
Normal file
@@ -0,0 +1,65 @@
|
||||
import lib
|
||||
|
||||
// Snapshot tests: verify complete path decomposition output against golden values
|
||||
|
||||
// Snapshot: decompose a typical web project file
|
||||
fn test_snapshot_web_project_file(): Unit with {Test} = {
|
||||
let path = "/home/user/project/src/components/Button.tsx"
|
||||
Test.assertEqualMsg("Button.tsx", lib.basename(path), "snap: basename")
|
||||
Test.assertEqualMsg("/home/user/project/src/components", lib.dirname(path), "snap: dirname")
|
||||
Test.assertEqualMsg("tsx", match lib.extension(path) { Some(e) => e, None => "" }, "snap: extension")
|
||||
Test.assertEqualMsg("Button", lib.stem(path), "snap: stem")
|
||||
Test.assertEqualMsg("/home/user/project/src/components/Button", lib.stripExtension(path), "snap: stripExtension")
|
||||
Test.assertEqualMsg(true, lib.isAbsolute(path), "snap: isAbsolute")
|
||||
Test.assertEqualMsg(false, lib.isRelative(path), "snap: isRelative")
|
||||
Test.assertEqualMsg(true, lib.hasExtension(path, "tsx"), "snap: hasExtension tsx")
|
||||
Test.assertEqualMsg(false, lib.hasExtension(path, "ts"), "snap: hasExtension ts")
|
||||
Test.assertEqualMsg("/home/user/project/src/components/Button.jsx", lib.replaceExtension(path, "jsx"), "snap: replaceExtension")
|
||||
}
|
||||
|
||||
// Snapshot: decompose a markdown blog post path
|
||||
fn test_snapshot_blog_post(): Unit with {Test} = {
|
||||
let path = "content/posts/2025/my-first-post.md"
|
||||
Test.assertEqualMsg("my-first-post.md", lib.basename(path), "snap: basename")
|
||||
Test.assertEqualMsg("content/posts/2025", lib.dirname(path), "snap: dirname")
|
||||
Test.assertEqualMsg("md", match lib.extension(path) { Some(e) => e, None => "" }, "snap: extension")
|
||||
Test.assertEqualMsg("my-first-post", lib.stem(path), "snap: stem")
|
||||
Test.assertEqualMsg("content/posts/2025/my-first-post", lib.stripExtension(path), "snap: stripExtension")
|
||||
Test.assertEqualMsg(false, lib.isAbsolute(path), "snap: isAbsolute")
|
||||
Test.assertEqualMsg(true, lib.isRelative(path), "snap: isRelative")
|
||||
Test.assertEqualMsg("content/posts/2025/my-first-post.html", lib.replaceExtension(path, "html"), "snap: replaceExtension")
|
||||
}
|
||||
|
||||
// Snapshot: SSG path transformation pipeline
|
||||
fn test_snapshot_ssg_pipeline(): Unit with {Test} = {
|
||||
let input = "pages/about.md"
|
||||
let slug = lib.stem(input)
|
||||
let outputDir = lib.join("_site", slug)
|
||||
let indexFile = lib.join(outputDir, "index.html")
|
||||
Test.assertEqualMsg("about", slug, "snap: slug")
|
||||
Test.assertEqualMsg("_site/about", outputDir, "snap: output dir")
|
||||
Test.assertEqualMsg("_site/about/index.html", indexFile, "snap: index file")
|
||||
Test.assertEqualMsg("_site/about", lib.dirname(indexFile), "snap: dirname of index")
|
||||
Test.assertEqualMsg("index.html", lib.basename(indexFile), "snap: basename of index")
|
||||
Test.assertEqualMsg("index", lib.stem(indexFile), "snap: stem of index")
|
||||
}
|
||||
|
||||
// Snapshot: dotfile handling
|
||||
fn test_snapshot_dotfiles(): Unit with {Test} = {
|
||||
Test.assertEqualMsg(".gitignore", lib.basename("/project/.gitignore"), "snap: dotfile basename")
|
||||
Test.assertEqualMsg("/project", lib.dirname("/project/.gitignore"), "snap: dotfile dirname")
|
||||
Test.assertEqualMsg(".gitignore", lib.stem(".gitignore"), "snap: dotfile stem")
|
||||
Test.assertEqualMsg("json", match lib.extension(".eslintrc.json") { Some(e) => e, None => "" }, "snap: dotconfig extension")
|
||||
Test.assertEqualMsg(".eslintrc", lib.stem(".eslintrc.json"), "snap: dotconfig stem")
|
||||
}
|
||||
|
||||
// Snapshot: path join normalization
|
||||
fn test_snapshot_join_normalization(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("/a/b", lib.join("/a", "b"), "snap: normal join")
|
||||
Test.assertEqualMsg("/a/b", lib.join("/a/", "b"), "snap: trailing slash join")
|
||||
Test.assertEqualMsg("/a/b", lib.join("/a", "/b"), "snap: leading slash join")
|
||||
Test.assertEqualMsg("/a/b", lib.join("/a/", "/b"), "snap: both slash join")
|
||||
Test.assertEqualMsg("b", lib.join("", "b"), "snap: empty first join")
|
||||
Test.assertEqualMsg("a", lib.join("a", ""), "snap: empty second join")
|
||||
Test.assertEqualMsg("", lib.join("", ""), "snap: both empty join")
|
||||
}
|
||||
144
packages/path/test_unit.lux
Normal file
144
packages/path/test_unit.lux
Normal file
@@ -0,0 +1,144 @@
|
||||
import lib
|
||||
|
||||
// --- basename edge cases ---
|
||||
|
||||
fn test_basename_root(): Unit with {Test} =
|
||||
Test.assertEqualMsg("", lib.basename("/"), "basename of root")
|
||||
|
||||
fn test_basename_multiple_slashes(): Unit with {Test} =
|
||||
Test.assertEqualMsg("", lib.basename("///"), "basename of ///")
|
||||
|
||||
fn test_basename_dotfile(): Unit with {Test} =
|
||||
Test.assertEqualMsg(".hidden", lib.basename("/home/.hidden"), "basename of dotfile")
|
||||
|
||||
fn test_basename_dots_in_name(): Unit with {Test} =
|
||||
Test.assertEqualMsg("file.tar.gz", lib.basename("/path/to/file.tar.gz"), "basename with multiple dots")
|
||||
|
||||
fn test_basename_single_char(): Unit with {Test} =
|
||||
Test.assertEqualMsg("x", lib.basename("x"), "basename of single char")
|
||||
|
||||
fn test_basename_deep_path(): Unit with {Test} =
|
||||
Test.assertEqualMsg("deep.txt", lib.basename("/a/b/c/d/e/deep.txt"), "basename of deep path")
|
||||
|
||||
fn test_basename_space_in_name(): Unit with {Test} =
|
||||
Test.assertEqualMsg("my file.txt", lib.basename("/path/my file.txt"), "basename with space")
|
||||
|
||||
// --- dirname edge cases ---
|
||||
|
||||
fn test_dirname_root(): Unit with {Test} =
|
||||
Test.assertEqualMsg("/", lib.dirname("/"), "dirname of root")
|
||||
|
||||
fn test_dirname_root_file(): Unit with {Test} =
|
||||
Test.assertEqualMsg("/", lib.dirname("/a"), "dirname of root-level file")
|
||||
|
||||
fn test_dirname_deep(): Unit with {Test} =
|
||||
Test.assertEqualMsg("/a/b/c", lib.dirname("/a/b/c/d"), "dirname of deep path")
|
||||
|
||||
fn test_dirname_empty(): Unit with {Test} =
|
||||
Test.assertEqualMsg(".", lib.dirname(""), "dirname of empty")
|
||||
|
||||
fn test_dirname_relative(): Unit with {Test} =
|
||||
Test.assertEqualMsg("a/b", lib.dirname("a/b/c"), "dirname of relative path")
|
||||
|
||||
// --- extension edge cases ---
|
||||
|
||||
fn isSome(opt: Option<String>, expected: String): Bool =
|
||||
match opt {
|
||||
Some(v) => v == expected,
|
||||
None => false,
|
||||
}
|
||||
|
||||
fn isNone(opt: Option<String>): Bool =
|
||||
match opt {
|
||||
Some(_) => false,
|
||||
None => true,
|
||||
}
|
||||
|
||||
fn test_extension_dotfile(): Unit with {Test} =
|
||||
Test.assert(isNone(lib.extension(".bashrc")), "extension of .bashrc is None")
|
||||
|
||||
fn test_extension_trailing_dot(): Unit with {Test} =
|
||||
Test.assert(isSome(lib.extension("file."), ""), "extension of file. is empty string")
|
||||
|
||||
fn test_extension_multiple_dots(): Unit with {Test} =
|
||||
Test.assert(isSome(lib.extension("archive.tar.gz"), "gz"), "extension of .tar.gz is gz")
|
||||
|
||||
fn test_extension_empty(): Unit with {Test} =
|
||||
Test.assert(isNone(lib.extension("")), "extension of empty is None")
|
||||
|
||||
fn test_extension_no_dot(): Unit with {Test} =
|
||||
Test.assert(isNone(lib.extension("Makefile")), "extension of Makefile is None")
|
||||
|
||||
fn test_extension_hidden_with_ext(): Unit with {Test} =
|
||||
Test.assert(isSome(lib.extension(".config.json"), "json"), "extension of .config.json is json")
|
||||
|
||||
fn test_extension_dot_in_dir(): Unit with {Test} =
|
||||
Test.assert(isSome(lib.extension("/path.d/file.txt"), "txt"), "extension through dotted dir")
|
||||
|
||||
// --- stripExtension edge cases ---
|
||||
|
||||
fn test_strip_extension_dotfile(): Unit with {Test} =
|
||||
Test.assertEqualMsg("", lib.stripExtension(".bashrc"), "stripExtension of dotfile")
|
||||
|
||||
fn test_strip_extension_trailing_dot(): Unit with {Test} =
|
||||
Test.assertEqualMsg("file", lib.stripExtension("file."), "stripExtension trailing dot")
|
||||
|
||||
fn test_strip_extension_double(): Unit with {Test} =
|
||||
Test.assertEqualMsg("file.tar", lib.stripExtension("file.tar.gz"), "stripExtension only strips last")
|
||||
|
||||
fn test_strip_extension_dot_in_dir(): Unit with {Test} =
|
||||
Test.assertEqualMsg("/usr/local.d/config", lib.stripExtension("/usr/local.d/config.ini"), "stripExtension with dot in dir")
|
||||
|
||||
// --- join edge cases ---
|
||||
|
||||
fn test_join_both_empty(): Unit with {Test} =
|
||||
Test.assertEqualMsg("", lib.join("", ""), "join empty + empty")
|
||||
|
||||
fn test_join_double_slash(): Unit with {Test} =
|
||||
Test.assertEqualMsg("/foo/bar", lib.join("/foo/", "/bar"), "join normalizes double slash")
|
||||
|
||||
fn test_join_root(): Unit with {Test} =
|
||||
Test.assertEqualMsg("/bar", lib.join("/", "bar"), "join root + relative")
|
||||
|
||||
fn test_join_three_parts(): Unit with {Test} = {
|
||||
let result = lib.join(lib.join("/a", "b"), "c")
|
||||
Test.assertEqualMsg("/a/b/c", result, "three-part join")
|
||||
}
|
||||
|
||||
// --- stem edge cases ---
|
||||
|
||||
fn test_stem_dotfile(): Unit with {Test} =
|
||||
Test.assertEqualMsg(".hidden", lib.stem(".hidden"), "stem of dotfile preserved")
|
||||
|
||||
fn test_stem_multiple_dots(): Unit with {Test} =
|
||||
Test.assertEqualMsg("file.tar", lib.stem("file.tar.gz"), "stem of .tar.gz")
|
||||
|
||||
fn test_stem_no_dir_no_ext(): Unit with {Test} =
|
||||
Test.assertEqualMsg("README", lib.stem("README"), "stem of extensionless file")
|
||||
|
||||
// --- isAbsolute / isRelative edge cases ---
|
||||
|
||||
fn test_is_absolute_empty(): Unit with {Test} =
|
||||
Test.assertEqualMsg(false, lib.isAbsolute(""), "empty is not absolute")
|
||||
|
||||
fn test_is_relative_empty(): Unit with {Test} =
|
||||
Test.assertEqualMsg(true, lib.isRelative(""), "empty is relative")
|
||||
|
||||
fn test_is_absolute_just_slash(): Unit with {Test} =
|
||||
Test.assertEqualMsg(true, lib.isAbsolute("/"), "/ is absolute")
|
||||
|
||||
// --- hasExtension / replaceExtension edge cases ---
|
||||
|
||||
fn test_has_extension_case_sensitive(): Unit with {Test} =
|
||||
Test.assertEqualMsg(false, lib.hasExtension("file.TXT", "txt"), "hasExtension is case sensitive")
|
||||
|
||||
fn test_has_extension_partial(): Unit with {Test} =
|
||||
Test.assertEqualMsg(false, lib.hasExtension("file.txta", "txt"), "hasExtension no partial match")
|
||||
|
||||
fn test_replace_extension_dotfile(): Unit with {Test} =
|
||||
Test.assertEqualMsg(".md", lib.replaceExtension(".bashrc", "md"), "replaceExtension on dotfile")
|
||||
|
||||
fn test_replace_extension_chain(): Unit with {Test} = {
|
||||
let result = lib.replaceExtension(lib.replaceExtension("file.txt", "md"), "html")
|
||||
Test.assertEqualMsg("file.html", result, "chained replaceExtension")
|
||||
}
|
||||
Reference in New Issue
Block a user