mirror of
https://github.com/sbrow/thor.git
synced 2026-10-25 10:11:55 -04:00
feat: Added tools feature for CSS pre/post processors.
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
#+test
|
||||
package mustache
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:testing"
|
||||
|
||||
// tool_test_dirs makes a fresh in/out dir pair under the system temp dir and
|
||||
// returns their paths (unique per name).
|
||||
tool_test_dirs :: proc(name: string) -> (in_dir, out_dir: string) {
|
||||
base, _ := os.temp_directory(context.temp_allocator)
|
||||
in_dir = fmt.tprintf("%s/thor_tool_test/%s/in", base, name)
|
||||
out_dir = fmt.tprintf("%s/thor_tool_test/%s/out", base, name)
|
||||
os.make_directory_all(in_dir)
|
||||
os.make_directory_all(out_dir)
|
||||
return
|
||||
}
|
||||
|
||||
// Tool_Test_Ctx carries the asset path as a context value (mustache resolves
|
||||
// tag keys from the context, not from quoted literals).
|
||||
Tool_Test_Ctx :: struct {
|
||||
p: string,
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_unknown_tool :: proc(t: ^testing.T) {
|
||||
reg: Tool_Registry // no commands configured
|
||||
tpl, _ := parse(`{{ p | tool nope }}`)
|
||||
defer delete_template(&tpl)
|
||||
_, err := render(tpl, Tool_Test_Ctx{p = "/a.css"}, tools = ®)
|
||||
testing.expect(t, err != nil, "unknown tool should be a hard error")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_unavailable_without_registry :: proc(t: ^testing.T) {
|
||||
// No registry threaded through (tools == nil): the pipe must fail rather
|
||||
// than silently do nothing.
|
||||
tpl, _ := parse(`{{ p | tool anything }}`)
|
||||
defer delete_template(&tpl)
|
||||
_, err := render(tpl, Tool_Test_Ctx{p = "/a.css"})
|
||||
testing.expect(t, err != nil, "tool pipe without a registry should error")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_arg_count :: proc(t: ^testing.T) {
|
||||
reg: Tool_Registry
|
||||
|
||||
// Zero args.
|
||||
tpl0, _ := parse(`{{ p | tool }}`)
|
||||
defer delete_template(&tpl0)
|
||||
_, err0 := render(tpl0, Tool_Test_Ctx{p = "/a.css"}, tools = ®)
|
||||
testing.expect(t, err0 != nil, "tool with no name should error")
|
||||
|
||||
// Two args.
|
||||
tpl2, _ := parse(`{{ p | tool one two }}`)
|
||||
defer delete_template(&tpl2)
|
||||
_, err2 := render(tpl2, Tool_Test_Ctx{p = "/a.css"}, tools = ®)
|
||||
testing.expect(t, err2 != nil, "tool with two args should error")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_absolute_url_passthrough :: proc(t: ^testing.T) {
|
||||
// A CDN URL has no local source; it should pass through untouched.
|
||||
reg: Tool_Registry
|
||||
url, err := run_tool(®, "anything", "https://cdn.example.com/x.css", 0)
|
||||
testing.expect(t, err == nil, "absolute URL should not error")
|
||||
testing.expect_value(t, url, "https://cdn.example.com/x.css")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_source_not_found :: proc(t: ^testing.T) {
|
||||
in_dir, out_dir := tool_test_dirs("missing_src")
|
||||
commands := make(map[string]string, context.temp_allocator)
|
||||
commands["cp"] = "cp {{in}} {{out}}"
|
||||
reg := Tool_Registry {
|
||||
commands = commands,
|
||||
input_dir = in_dir,
|
||||
output_dir = out_dir,
|
||||
}
|
||||
|
||||
_, err := run_tool(®, "cp", "/nope.css", 0)
|
||||
testing.expect(t, err != nil, "missing source file should be a hard error")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_missing_binary :: proc(t: ^testing.T) {
|
||||
in_dir, out_dir := tool_test_dirs("missing_bin")
|
||||
_ = os.write_entire_file_from_string(fmt.tprintf("%s/a.css", in_dir), "body{}")
|
||||
|
||||
commands := make(map[string]string, context.temp_allocator)
|
||||
commands["ghost"] = "thor-tool-does-not-exist-xyz {{in}} {{out}}"
|
||||
reg := Tool_Registry {
|
||||
commands = commands,
|
||||
input_dir = in_dir,
|
||||
output_dir = out_dir,
|
||||
}
|
||||
|
||||
_, err := run_tool(®, "ghost", "/a.css", 0)
|
||||
testing.expect(t, err != nil, "missing binary should be caught by PATH check")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_runs :: proc(t: ^testing.T) {
|
||||
when ODIN_OS != .Windows {
|
||||
in_dir, out_dir := tool_test_dirs("runs")
|
||||
os.make_directory_all(fmt.tprintf("%s/css", in_dir))
|
||||
_ = os.write_entire_file_from_string(
|
||||
fmt.tprintf("%s/css/main.css", in_dir),
|
||||
"body { color: red }",
|
||||
)
|
||||
|
||||
commands := make(map[string]string, context.temp_allocator)
|
||||
commands["copyit"] = "cp {{in}} {{out}}"
|
||||
reg := Tool_Registry {
|
||||
commands = commands,
|
||||
input_dir = in_dir,
|
||||
output_dir = out_dir,
|
||||
}
|
||||
|
||||
tpl, _ := parse(`{{ p | tool copyit }}`)
|
||||
defer delete_template(&tpl)
|
||||
result, err := render(tpl, Tool_Test_Ctx{p = "/css/main.css"}, tools = ®)
|
||||
defer delete(result)
|
||||
testing.expect(t, err == nil, "cp tool should succeed")
|
||||
testing.expect_value(t, result, "/css/main.css")
|
||||
|
||||
// The tool wrote the output file.
|
||||
written := fmt.tprintf("%s/css/main.css", out_dir)
|
||||
data, rerr := os.read_entire_file_from_path(written, context.temp_allocator)
|
||||
testing.expect(t, rerr == nil, "output file should exist")
|
||||
testing.expect_value(t, string(data), "body { color: red }")
|
||||
|
||||
// The result was memoized; a second call for the same asset returns the
|
||||
// same URL from the registry (mutated through the pointer).
|
||||
url2, err2 := run_tool(®, "copyit", "/css/main.css", 0)
|
||||
testing.expect(t, err2 == nil, "memoized call should not error")
|
||||
testing.expect_value(t, url2, "/css/main.css")
|
||||
testing.expect(t, len(reg.manifest) == 1, "result should be memoized")
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_tool_minify_section :: proc(t: ^testing.T) {
|
||||
// The command template is itself mustache: {{#minify}} should expand only
|
||||
// when the registry has minify on. Verify via the rendered command string.
|
||||
cmd, err := render_command("prog {{#minify}}--min{{/minify}} {{in}}", "/a", "/b", true, 0)
|
||||
testing.expect(t, err == nil, "render_command should succeed (minify on)")
|
||||
testing.expect(t, strings.contains(cmd, "--min"), "minify section should render when on")
|
||||
|
||||
cmd2, err2 := render_command("prog {{#minify}}--min{{/minify}} {{in}}", "/a", "/b", false, 0)
|
||||
testing.expect(t, err2 == nil, "render_command should succeed (minify off)")
|
||||
testing.expect(t, !strings.contains(cmd2, "--min"), "minify section should be empty when off")
|
||||
}
|
||||
Reference in New Issue
Block a user