164 lines
4.8 KiB
Swift
164 lines
4.8 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")])
|
|
}
|
|
|
|
@Test func lexer_unrecognized_line_produces_error_token() {
|
|
let tokens = Lexer().tokenize("???bad line")
|
|
#expect(tokens == [.error(line: "???bad line")])
|
|
}
|
|
|
|
@Test func lexer_multiple_errors_in_multiline() {
|
|
let input = """
|
|
build:
|
|
???bad
|
|
!!!also bad
|
|
"""
|
|
let tokens = Lexer().tokenize(input)
|
|
#expect(tokens.contains(.error(line: "???bad")))
|
|
#expect(tokens.contains(.error(line: "!!!also bad")))
|
|
}
|
|
|
|
// MARK: - Parser
|
|
|
|
@Test func parser_single_recipe_with_command() throws {
|
|
let tokens: [Token] = [.recipe("build"), .command("swift build")]
|
|
let ast = try Parser().parse(tokens).get()
|
|
#expect(ast.rules["build"]?.commands == ["swift build"])
|
|
}
|
|
|
|
@Test func parser_recipe_with_dependency() throws {
|
|
let tokens: [Token] = [
|
|
.recipe("test"), .dependency("build"),
|
|
.command("swift test")
|
|
]
|
|
let ast = try Parser().parse(tokens).get()
|
|
#expect(ast.rules["test"]?.dependencies == ["build"])
|
|
#expect(ast.rules["test"]?.commands == ["swift test"])
|
|
}
|
|
|
|
@Test func parser_variable_is_stored() throws {
|
|
let tokens: [Token] = [.variable(name: "CC", value: "clang")]
|
|
let ast = try Parser().parse(tokens).get()
|
|
#expect(ast.vars["CC"] == "clang")
|
|
}
|
|
|
|
@Test func parser_multiple_recipes() throws {
|
|
let tokens: [Token] = [
|
|
.recipe("build"), .command("swift build"),
|
|
.newline,
|
|
.recipe("test"), .command("swift test"),
|
|
]
|
|
let ast = try Parser().parse(tokens).get()
|
|
#expect(ast.rules["build"]?.commands == ["swift build"])
|
|
#expect(ast.rules["test"]?.commands == ["swift test"])
|
|
}
|
|
|
|
@Test func parser_variable_before_recipe_flushes_context() throws {
|
|
let tokens: [Token] = [
|
|
.recipe("build"), .command("swift build"),
|
|
.variable(name: "X", value: "1"),
|
|
]
|
|
let ast = try Parser().parse(tokens).get()
|
|
#expect(ast.rules["build"]?.commands == ["swift build"])
|
|
#expect(ast.vars["X"] == "1")
|
|
}
|
|
|
|
// MARK: - Parser error handling
|
|
|
|
@Test func parser_single_error_token_returns_failure() {
|
|
let tokens: [Token] = [.error(line: "???bad")]
|
|
let result = Parser().parse(tokens)
|
|
guard case .failure(let err) = result else {
|
|
Issue.record("expected failure, got success")
|
|
return
|
|
}
|
|
#expect(err.errorLines == ["???bad"])
|
|
}
|
|
|
|
@Test func parser_multiple_error_tokens_collected() {
|
|
let tokens: [Token] = [
|
|
.error(line: "???bad"),
|
|
.recipe("build"), .command("swift build"),
|
|
.error(line: "!!!also bad"),
|
|
]
|
|
let result = Parser().parse(tokens)
|
|
guard case .failure(let err) = result else {
|
|
Issue.record("expected failure, got success")
|
|
return
|
|
}
|
|
#expect(err.errorLines.count == 2)
|
|
#expect(err.errorLines.contains("???bad"))
|
|
#expect(err.errorLines.contains("!!!also bad"))
|
|
}
|
|
|
|
@Test func parser_no_error_tokens_returns_success() {
|
|
let tokens: [Token] = [.recipe("build"), .command("swift build")]
|
|
let result = Parser().parse(tokens)
|
|
guard case .success = result else {
|
|
Issue.record("expected success, got failure")
|
|
return
|
|
}
|
|
}
|
|
|
|
// 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")
|
|
}
|