//===----------------------------------------------------------------------===// // Copyright © 2025 Apple Inc. and the container project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //===----------------------------------------------------------------------===// import ContainerBuildIR import ContainerizationOCI import Testing @testable import ContainerBuildParser /// VisitorTest covers testing that visiting instructions is successful and graphs /// made from those instructions are structured as expected. Tests are added for /// instructions that have complex logic in their `visit` function. @Suite class VisitorTest { @Test func simpleVisitFrom() throws { let imageName = "alpine" let expectedImageRef = ImageReference(parsing: imageName) let stageName = "build" let platformString = "linux/arm64" let expectedPlatform = try Platform(from: platformString) let from = try FromInstruction(image: imageName, platform: platformString, stageName: stageName) let visitor = DockerInstructionVisitor() try visitor.visit(from) let graph = try visitor.graphBuilder.build() #expect(graph.stages.count == 1) let stage = graph.stages[0] #expect(stage.name == stageName) #expect(stage.platform! == expectedPlatform) #expect(stage.base.source == .registry(expectedImageRef!)) } @Test func simpleVisitRun() throws { let from = try FromInstruction(image: "scratch") let command: Command = .exec(["sh", "-c", "top"]) let network = "default" let run = try RunInstruction( command: command, rawMounts: [], network: network ) let visitor = DockerInstructionVisitor() try visitor.visit(from) try visitor.visit(run) let graph = try visitor.graphBuilder.build() #expect(graph.stages.count == 1) let stage = graph.stages[0] #expect(stage.nodes.count == 1) let node = stage.nodes[0] #expect(node.operation is ExecOperation) let exec = node.operation as! ExecOperation #expect(exec.command == command) #expect(exec.mounts.isEmpty) #expect(exec.network == .default) } @Test func simpleVisitCopy() throws { let from = try FromInstruction(image: "scratch") let sources = ["src", "src1", "src2"] let dest = "/dest" let copy = try CopyInstruction( sources: sources, destination: dest, from: nil, ownership: "10:10", permissions: "777" ) let visitor = DockerInstructionVisitor() try visitor.visit(from) try visitor.visit(copy) let graph = try visitor.graphBuilder.build() #expect(graph.stages.count == 1) let stage = graph.stages[0] #expect(stage.nodes.count == 1) let node = stage.nodes[0] #expect(node.operation is FilesystemOperation) let copyNode = node.operation as! FilesystemOperation #expect(copyNode.action == .copy) let expectedSource = ContextSource( name: "default", paths: sources) #expect(copyNode.source == .context(expectedSource)) #expect(copyNode.destination == dest) let expectedOwnership = Ownership(user: .numeric(id: 10), group: .numeric(id: 10)) #expect(copyNode.fileMetadata.ownership == expectedOwnership) let expectedPerms: Permissions = .mode(777) #expect(copyNode.fileMetadata.permissions == expectedPerms) } }