// p4rse, Copyright 2026, Will Hawkins
//
// This file is part of p4rse.
//
// This file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
import Foundation
import Macros
import P4Compiler
import P4Lang
import P4Runtime
import SwiftTreeSitter
import SystemPackage
import Testing
import TreeSitter
import TreeSitterP4
@testable import Common
@Test func test_preprocessor() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple.p4")
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string).lexicallyNormalized()
let source = try! (#UseOkResult(prep.preprocess(file)))
let program = try! #UseOkResult(Program.Compile(source.getSource()))
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
#expect(source.getLocations().getPath() == expected_file)
}
@Test func test_preprocessor_search_for_file() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "simple.p4")
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/TestData/Sources/simple.p4")
let source = try! (#UseOkResult(prep.preprocess(file)))
let program = try! #UseOkResult(Program.Compile(source.getSource()))
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
#expect(source.getLocations().getPath() == expected_file)
}
@Test func test_preprocessor_nested_includes() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-split.p4")
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string).lexicallyNormalized()
#expect(#RequireOkResult(prep.preprocess(file)))
let source = try! (#UseOkResult(prep.preprocess(file)))
let program = try! #UseOkResult(Program.Compile(source.getSource()))
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
#expect(source.getLocations().getPath() == expected_file)
}
@Test func test_preprocessor_oneline_includes() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-split-oneline.p4")
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string).lexicallyNormalized()
#expect(#RequireOkResult(prep.preprocess(file)))
let source = try! (#UseOkResult(prep.preprocess(file)))
let program = try! #UseOkResult(Program.Compile(source.getSource()))
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
#expect(source.getLocations().getPath() == expected_file)
}
@Test func test_preprocessor_missing_file() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/unfound.p4")
#expect(
#RequireErrorResult(
Error(withMessage: "Could not open ./TestData/Sources/unfound.p4 for preprocessing"),
(prep.preprocess(file))))
}
@Test func test_preprocessor_missing_included_file() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-unfound.p4")
#expect(
#RequireErrorResult(
Error(withMessage: "Could not open unfound.p4 for inclusion"), (prep.preprocess(file))))
}
@Test func test_preprocessor_no_change_locations() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "testing.p4")
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(source.getLocations().getLocation() == SourceLocation(0..<173))
}
@Test func test_preprocessor_change_locations() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "testing-split-a.p4")
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(source.getLocations().getLocation() == SourceLocation(0..<173))
#expect(source.getLocations().getNestedLocations()[0].getLocation() == SourceLocation(48..<173))
}
@Test func test_preprocessor_oneline_includes_locations() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-split-oneline.p4")
#expect(#RequireOkResult(prep.preprocess(file)))
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(source.getLocations().getLocation() == SourceLocation(0..<173))
#expect(source.getLocations().getNestedLocations()[0].getLocation() == SourceLocation(0..<70))
#expect(source.getLocations().getNestedLocations()[1].getLocation() == SourceLocation(70..<173))
}
@Test func test_preprocessor_nested_includes_locations() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/nested-split.p4")
#expect(#RequireOkResult(prep.preprocess(file)))
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(source.getLocations().getLocation() == SourceLocation(0..<173))
#expect(
source.getLocations().getNestedLocations()[0].getNestedLocations()[0].getLocation()
== SourceLocation(27..<47))
#expect(
source.getLocations().getNestedLocations()[0].getNestedLocations()[1].getLocation()
== SourceLocation(48..<166))
}
@Test func test_preprocessor_nested_includes_annotated_source() async throws {
let sm = SourceManager(["./TestData/Sources/"])
let prep = SourceCodePreprocessor(sm)
let file = FilePath.init(stringLiteral: "./TestData/Sources/annotate.p4")
let expected = """
};
}>>
"""
#expect(#RequireOkResult(prep.preprocess(file)))
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(source.getSource(annotated: true) == expected)
}
@Test func test_source_location_contains() async throws {
let outer = SourceLocation(0..<500)
let inner = SourceLocation(0..<499)
#expect(outer.contains(inner))
}
@Test func test_source_location_contains_a() async throws {
let outer = SourceLocation(0, 500)
let inner = SourceLocation(0, 499)
#expect(outer.contains(inner))
}
@Test func test_source_location_contains2() async throws {
let outer = SourceLocation(0..<500)
let not_inner = SourceLocation(0..<501)
#expect(!outer.contains(not_inner))
}
@Test func test_source_location_contains2_a() async throws {
let outer = SourceLocation(0,500)
let not_inner = SourceLocation(0,501)
#expect(!outer.contains(not_inner))
}
@Test func test_source_location_contains3() async throws {
let outer = SourceLocation(200..<500)
let inner = SourceLocation(200..<499)
#expect(outer.contains(inner))
}
@Test func test_source_location_contains3_a() async throws {
let outer = SourceLocation(200,300)
let inner = SourceLocation(200,299)
#expect(outer.contains(inner))
}
@Test func test_source_location_contains4() async throws {
let outer = SourceLocation(200..<500)
let inner = SourceLocation(200, 299)
#expect(outer.contains(inner))
}
@Test func test_source_location_contains5() async throws {
let outer = SourceLocation(200..<300)
let not_inner = SourceLocation(200,101)
#expect(!outer.contains(not_inner))
}