96ba9e3a56
added lexer, parser, Shell, Executor. added tests for sonyalib it already works! will be implementing more features soon.
113 lines
3.3 KiB
Swift
113 lines
3.3 KiB
Swift
import Testing
|
|
@testable import sonyalib
|
|
|
|
// MARK: - Lexer
|
|
|
|
@Test func lexer_tokenizes_recipe_with_no_deps() {
|
|
let tokens = Lexer().tokenize("build:")
|
|
#expect(tokens == [.recipe("build")])
|
|
}
|
|
|
|
@Test func lexer_tokenizes_recipe_with_deps() {
|
|
let tokens = Lexer().tokenize("test: build lint")
|
|
#expect(tokens == [.recipe("test"), .dependency("build"), .dependency("lint")])
|
|
}
|
|
|
|
@Test func lexer_tokenizes_command() {
|
|
let tokens = Lexer().tokenize(" echo hello")
|
|
#expect(tokens == [.command("echo hello")])
|
|
}
|
|
|
|
@Test func lexer_tokenizes_variable() {
|
|
let tokens = Lexer().tokenize("CC = clang")
|
|
#expect(tokens == [.variable(name: "CC", value: "clang")])
|
|
}
|
|
|
|
@Test func lexer_tokenizes_comment() {
|
|
let tokens = Lexer().tokenize("# this is a comment")
|
|
#expect(tokens == [.comment(" this is a comment")])
|
|
}
|
|
|
|
@Test func lexer_empty_line_produces_newline() {
|
|
let tokens = Lexer().tokenize("")
|
|
#expect(tokens == [.newline])
|
|
}
|
|
|
|
@Test func lexer_multiline_sonyafile() {
|
|
let input = """
|
|
build:
|
|
swift build
|
|
"""
|
|
let tokens = Lexer().tokenize(input)
|
|
#expect(tokens == [.recipe("build"), .command("swift build")])
|
|
}
|
|
|
|
// MARK: - Parser
|
|
|
|
@Test func parser_single_recipe_with_command() {
|
|
let tokens: [Token] = [.recipe("build"), .command("swift build")]
|
|
let ast = Parcer().parce(tokens)
|
|
#expect(ast != nil)
|
|
#expect(ast?.rules["build"]?.commands == ["swift build"])
|
|
}
|
|
|
|
@Test func parser_recipe_with_dependency() {
|
|
let tokens: [Token] = [
|
|
.recipe("test"), .dependency("build"),
|
|
.command("swift test")
|
|
]
|
|
let ast = Parcer().parce(tokens)
|
|
#expect(ast?.rules["test"]?.dependencies == ["build"])
|
|
#expect(ast?.rules["test"]?.commands == ["swift test"])
|
|
}
|
|
|
|
@Test func parser_variable_is_stored() {
|
|
let tokens: [Token] = [.variable(name: "CC", value: "clang")]
|
|
let ast = Parcer().parce(tokens)
|
|
#expect(ast?.vars["CC"] == "clang")
|
|
}
|
|
|
|
@Test func parser_multiple_recipes() {
|
|
let tokens: [Token] = [
|
|
.recipe("build"), .command("swift build"),
|
|
.newline,
|
|
.recipe("test"), .command("swift test"),
|
|
]
|
|
let ast = Parcer().parce(tokens)
|
|
#expect(ast?.rules["build"]?.commands == ["swift build"])
|
|
#expect(ast?.rules["test"]?.commands == ["swift test"])
|
|
}
|
|
|
|
@Test func parser_variable_before_recipe_flushes_context() {
|
|
// A variable declaration after a recipe must flush the current recipe first.
|
|
let tokens: [Token] = [
|
|
.recipe("build"), .command("swift build"),
|
|
.variable(name: "X", value: "1"),
|
|
]
|
|
let ast = Parcer().parce(tokens)
|
|
#expect(ast?.rules["build"]?.commands == ["swift build"])
|
|
#expect(ast?.vars["X"] == "1")
|
|
}
|
|
|
|
// MARK: - Variable resolution
|
|
|
|
@Test func resolve_substitutes_known_variable() {
|
|
let result = resolve("$(CC) main.c", vars: ["CC": "clang"])
|
|
#expect(result == "clang main.c")
|
|
}
|
|
|
|
@Test func resolve_returns_nil_for_unknown_variable() {
|
|
let result = resolve("$(MISSING) main.c", vars: [:])
|
|
#expect(result == nil)
|
|
}
|
|
|
|
@Test func resolve_no_variables_passes_through() {
|
|
let result = resolve("echo hello", vars: [:])
|
|
#expect(result == "echo hello")
|
|
}
|
|
|
|
@Test func resolve_multiple_substitutions() {
|
|
let result = resolve("$(CC) $(FLAGS) main.c", vars: ["CC": "clang", "FLAGS": "-O2"])
|
|
#expect(result == "clang -O2 main.c")
|
|
}
|