diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c8b0378e..af379201 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,6 +15,22 @@ // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "uname -a", // Configure tool-specific properties. + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": "false", + "username": "vscode", + "upgradePackages": "false" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "os-provided", + "ppa": "false" + } + }, + "runArgs": [ + "--cap-add=SYS_PTRACE", + "--security-opt", + "seccomp=unconfined" + ], "customizations": { "vscode": { "extensions": [ @@ -22,6 +38,7 @@ ], "settings": { "swift.path": "/usr/bin", + "lldb.library": "/usr/lib/liblldb.so", "editor.inlayHints.enabled": "offUnlessPressed", "editor.formatOnSave": true, "[swift]": { @@ -33,7 +50,7 @@ ] } } - } + }, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" + "remoteUser": "vscode" } \ No newline at end of file diff --git a/Package.swift b/Package.swift index 346c1e99..1b13128c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -15,7 +15,7 @@ let executableTarget = PackageDescription.Target.executableTarget( .product(name: "SwiftUtils", package: "SwiftUtils"), .product(name: "VHDLJSModels", package: "VHDLJSModels"), .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures"), - .target(name: "GeneratorCommands") + .target(name: "GeneratorCommands"), ], swiftSettings: [.unsafeFlags(["-parse-as-library"])] ) @@ -31,7 +31,7 @@ let executableTarget = PackageDescription.Target.executableTarget( .product(name: "SwiftUtils", package: "SwiftUtils"), .product(name: "VHDLJSModels", package: "VHDLJSModels"), .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures"), - .target(name: "GeneratorCommands") + .target(name: "GeneratorCommands"), ] ) #endif @@ -46,17 +46,17 @@ let package = Package( name: "llfsm-generate", targets: ["MachineGenerator"] ), - .library(name: "LLFSMGenerate", targets: ["GeneratorCommands"]) + .library(name: "LLFSMGenerate", targets: ["GeneratorCommands"]), ], dependencies: [ .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.4.2"), - .package(url: "https://github.com/mipalgu/VHDLMachines", from: "4.0.5"), + .package(url: "https://github.com/mipalgu/VHDLMachines", from: "4.1.0"), .package(url: "https://github.com/mipalgu/VHDLParsing", from: "2.7.0"), - .package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"), - .package(url: "https://github.com/mipalgu/VHDLKripkeStructureGenerator.git", from: "0.5.1"), - .package(url: "https://github.com/CPSLabGU/SwiftUtils.git", from: "0.1.0"), + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.8.2"), + .package(url: "https://github.com/mipalgu/VHDLKripkeStructureGenerator.git", from: "0.6.0"), + .package(url: "https://github.com/CPSLabGU/SwiftUtils.git", from: "1.0.0"), .package(url: "https://github.com/CPSLabGU/VHDLJSModels", from: "2.0.0"), - .package(url: "https://github.com/CPSLabGU/VHDLKripkeStructures", from: "1.3.0") + .package(url: "https://github.com/CPSLabGU/VHDLKripkeStructures", from: "2.0.0"), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. @@ -71,7 +71,7 @@ let package = Package( .product(name: "VHDLKripkeStructureGenerator", package: "VHDLKripkeStructureGenerator"), .product(name: "SwiftUtils", package: "SwiftUtils"), .product(name: "VHDLJSModels", package: "VHDLJSModels"), - .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures") + .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures"), ], swiftSettings: [ .unsafeFlags( @@ -90,8 +90,8 @@ let package = Package( .product(name: "VHDLKripkeStructureGenerator", package: "VHDLKripkeStructureGenerator"), .product(name: "SwiftUtils", package: "SwiftUtils"), .product(name: "VHDLJSModels", package: "VHDLJSModels"), - "TestHelpers", - .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures") + .target(name: "TestHelpers"), + .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures"), ] ), .testTarget( @@ -100,8 +100,8 @@ let package = Package( .product(name: "VHDLMachines", package: "VHDLMachines"), .product(name: "VHDLParsing", package: "VHDLParsing"), .product(name: "VHDLJSModels", package: "VHDLJSModels"), - .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures") + .product(name: "VHDLKripkeStructures", package: "VHDLKripkeStructures"), ] - ) + ), ] ) diff --git a/README.md b/README.md index cce987f8..aebe36e8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![MacOS CI](https://github.com/CPSLabGU/LLFSMGenerate/actions/workflows/ci-macOS.yml/badge.svg)](https://github.com/CPSLabGU/LLFSMGenerate/actions/workflows/ci-macOS.yml) [![Windows CI](https://github.com/CPSLabGU/LLFSMGenerate/actions/workflows/ci-windows.yml/badge.svg)](https://github.com/CPSLabGU/LLFSMGenerate/actions/workflows/ci-windows.yml) -`llfsmgenerate` is a command-line utility for transforming and compiling LLFSM formats that use the `VHDL` +`llfsm-generate` is a command-line utility for transforming and compiling LLFSM formats that use the `VHDL` Hardware Description Language. This program allows the transformation between [VHDL LLFSMs](https://github.com/mipalgu/VHDLMachines) and other standard Javascript models, such as the [VHDL LLFSM editor](https://github.com/CPSLabGU/editor) that utilises *React*. @@ -14,9 +14,9 @@ formal verification via Kripke structure generation. ## Requirements and Supported Platforms -- Swift 5.10 or later (See [Installing Swift](#installing-swift)). -- macOS 14 (Sonoma) or later. -- Linux (Ubuntu 20.04 or later). +- Swift 6.0 or later (See [Installing Swift](#installing-swift)). +- macOS 26 (Sonoma) or later. +- Linux (Ubuntu 24.04 or later). - Windows 11. - Windows Server Edition 2022. @@ -28,14 +28,14 @@ cd LLFSMGenerate swift build -c release ``` -After the compilation, you will find the binary at `.build/release/llfsmgenerate`. It is preferred that the +After the compilation, you will find the binary at `.build/release/llfsm-generate`. It is preferred that the binary is installed within a location accessible by your `PATH` variable. For example, you may install the program within `/usr/local/`: ```shell -install -m 0755 .build/release/llfsmgenerate /usr/local/bin +install -m 0755 .build/release/llfsm-generate /usr/local/bin ``` -The `llfsmgenerate` binary allows the transformation of LLFSM models that contain VHDL code within their +The `llfsm-generate` binary allows the transformation of LLFSM models that contain VHDL code within their state actions. Most LLFSMs are created using an editor that places an easily parsable format within the LLFSM directory. This format is very easy to parse using Javascript, however, it does not provide type information that is extremely useful in formal verification and code generation. To generate VHDL code for a @@ -43,7 +43,7 @@ given LLFSM, we need to first convert it from the Javascript-like format into th [code generator](https://github.com/mipalgu/VHDLMachines) understands. This is done by using the command: ```shell -llfsmgenerate model +llfsm-generate model ``` > [!IMPORTANT] @@ -55,34 +55,34 @@ This command creates the type-aware model that our Once this model is generated, we can then create the VHDL source files by using: ```shell -llfsmgenerate vhdl +llfsm-generate vhdl ``` This command creates the `.vhd` files located in `/build/vhdl`. We can also create the Kripke structure generator that creates graph structures for formal verification. ```shell -llfsmgenerate vhdl --include-kripke-structure +llfsm-generate vhdl --include-kripke-structure ``` We may now copy our vhdl files into a directory that we can utilise for HDL projects. We have provided a command called `install` to make this simpler. ```shell -llfsmgenerate install +llfsm-generate install ``` You may also specify a vivado project location by passing the `--vivado` flag. ```shell -llfsmgenerate install --vivado +llfsm-generate install --vivado ``` Please see the *help* section of the binary for a complete list of parameters and sub-commands. ```shell OVERVIEW: A utility for performing operations on LLFSM formats. -USAGE: llfsmgenerate +USAGE: llfsm-generate OPTIONS: --version Show the version. @@ -94,8 +94,9 @@ SUBCOMMANDS: clean Clean the generated source files from the machine. install Install the VHDL files into a specified directory. graph Generate a graphviz file (.dot) for the entire kripke structure. + report Report Statistics about the generated machine. - See 'llfsmgenerate help ' for detailed help. + See 'llfsm-generate help ' for detailed help. ``` ## Documentation @@ -106,41 +107,5 @@ The latest documentation may be found on the ## Installing Swift You may verify your swift installation by performing `swift --version`. The minimum required version for -`llfsmgenerate` is `5.7`. To install swift, follow the instructions below for your operating system. - -### Linux - -We prefer that you use [swiftenv](https://github.com/kylef/swiftenv) to install swift on linux. To install -`swiftenv`, clone the repository in your home directory. - -```shell -git clone https://github.com/kylef/swiftenv.git ~/.swiftenv -``` - -Then place the following in your `.bash_profile` (or equivalent if using a different shell). Please note, -some systems will require modifying your `.bashrc` instead of `.bash_profile`. - -```shell -echo 'export SWIFTENV_ROOT="$HOME/.swiftenv"' >> ~/.bash_profile -echo 'export PATH="$SWIFTENV_ROOT/bin:$PATH"' >> ~/.bash_profile -echo 'eval "$(swiftenv init -)"' >> ~/.bash_profile -``` - -You may now install swift via: - -```shell -source ~/.bash_profile -swiftenv install 6.0 -``` - -The full instructions are provided in the -[swiftenv documentation](https://swiftenv.fuller.li/en/latest/installation.html). - -### MacOS - -Make sure you install the latest version of XCode through your App store or -[developer website](https://developer.apple.com/xcode/). - -### Windows - -The full instructions for installing swift may be found on the [swift website](https://www.swift.org/install/windows/). +`llfsm-generate` is `6.0`. To install swift, follow the [instructions](https://www.swift.org/install) +for your operating system. diff --git a/Sources/GeneratorCommands/CleanCommand.swift b/Sources/GeneratorCommands/CleanCommand.swift index 1ceb3fb5..7c594379 100644 --- a/Sources/GeneratorCommands/CleanCommand.swift +++ b/Sources/GeneratorCommands/CleanCommand.swift @@ -61,7 +61,7 @@ import Foundation public struct CleanCommand: ParsableCommand { /// The command configuration. - public static var configuration = CommandConfiguration( + public static let configuration = CommandConfiguration( commandName: "clean", abstract: "Clean the generated source files from the machine." ) diff --git a/Sources/GeneratorCommands/Generate.swift b/Sources/GeneratorCommands/Generate.swift index f1b37844..fccbfdea 100644 --- a/Sources/GeneratorCommands/Generate.swift +++ b/Sources/GeneratorCommands/Generate.swift @@ -64,7 +64,7 @@ import VHDLMachineTransformations public struct Generate: ParsableCommand { /// The command configuration describing a sub-command. - public static var configuration = CommandConfiguration( + public static let configuration = CommandConfiguration( commandName: "model", abstract: "A utility for converting LLFSM formats." ) diff --git a/Sources/GeneratorCommands/GraphCommand.swift b/Sources/GeneratorCommands/GraphCommand.swift index 43ad477e..69b6e7b0 100644 --- a/Sources/GeneratorCommands/GraphCommand.swift +++ b/Sources/GeneratorCommands/GraphCommand.swift @@ -61,7 +61,7 @@ import VHDLKripkeStructures public struct GraphCommand: ParsableCommand { /// The configuration of this command. - public static var configuration = CommandConfiguration( + public static let configuration = CommandConfiguration( commandName: "graph", abstract: "Generate a graphviz file (.dot) for the entire kripke structure." ) diff --git a/Sources/GeneratorCommands/InstallCommand.swift b/Sources/GeneratorCommands/InstallCommand.swift index 65e068e9..d0a841aa 100644 --- a/Sources/GeneratorCommands/InstallCommand.swift +++ b/Sources/GeneratorCommands/InstallCommand.swift @@ -60,7 +60,7 @@ import Foundation public struct InstallCommand: ParsableCommand { /// The configuration of this command. - public static var configuration = CommandConfiguration( + public static let configuration = CommandConfiguration( commandName: "install", abstract: "Install the VHDL files into a specified directory." ) diff --git a/Sources/GeneratorCommands/LLFSMGenerate.swift b/Sources/GeneratorCommands/LLFSMGenerate.swift index ae2e143c..905538b5 100644 --- a/Sources/GeneratorCommands/LLFSMGenerate.swift +++ b/Sources/GeneratorCommands/LLFSMGenerate.swift @@ -60,7 +60,7 @@ import ArgumentParser public struct LLFSMGenerate: ParsableCommand { /// This struct acts as an umbrella struct to multiple `ParsableCommand` subcommands. - public static var configuration = CommandConfiguration( + public static let configuration = CommandConfiguration( commandName: "llfsm-generate", abstract: "A utility for performing operations on LLFSM formats.", version: "3.0.0", diff --git a/Sources/GeneratorCommands/ReportCommand.swift b/Sources/GeneratorCommands/ReportCommand.swift index 57159fd6..10925277 100644 --- a/Sources/GeneratorCommands/ReportCommand.swift +++ b/Sources/GeneratorCommands/ReportCommand.swift @@ -66,7 +66,7 @@ import VHDLKripkeStructures public struct ReportCommand: ParsableCommand { /// The command configuration. - public static var configuration = CommandConfiguration( + public static let configuration = CommandConfiguration( commandName: "report", abstract: "Report Statistics about the generated machine." ) diff --git a/Sources/GeneratorCommands/VHDLGenerator.swift b/Sources/GeneratorCommands/VHDLGenerator.swift index 7610e61c..005740f0 100644 --- a/Sources/GeneratorCommands/VHDLGenerator.swift +++ b/Sources/GeneratorCommands/VHDLGenerator.swift @@ -66,7 +66,7 @@ import VHDLParsing public struct VHDLGenerator: ParsableCommand { /// The configuration for the command. - public static var configuration = CommandConfiguration( + public static let configuration = CommandConfiguration( commandName: "vhdl", abstract: "A utility for generating VHDL source files from LLFSM definitions." ) @@ -211,7 +211,7 @@ public struct VHDLGenerator: ParsableCommand { message: "Failed to generate Kripke Structure for \(name.rawValue)." ) } - try files.write(to: buildFolder, options: .atomic, originalContentsURL: nil) + try files.write(to: buildFolder, options: FileWrapper.WritingOptions.atomic, originalContentsURL: nil) } } diff --git a/Tests/GeneratorTests/CleanCommandTests.swift b/Tests/GeneratorTests/CleanCommandTests.swift index f0cfb821..7b138047 100644 --- a/Tests/GeneratorTests/CleanCommandTests.swift +++ b/Tests/GeneratorTests/CleanCommandTests.swift @@ -53,119 +53,113 @@ // or write to the Free Software Foundation, Inc., 51 Franklin Street, // Fifth Floor, Boston, MA 02110-1301, USA. -import XCTest +import Testing +import Foundation @testable import GeneratorCommands /// Test class for ``CleanCommand``. +@Suite final class CleanCommandTests: MachineTester { /// Build the machine before every test. - override func setUp() { - super.setUp() + override init() { + super.init() let path = self.machine0Path.path Generate.main([path]) VHDLGenerator.main(["--include-kripke-structure", path]) var isDirectory: ObjCBool = true - XCTAssertTrue(manager.fileExists(atPath: self.jsonFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertTrue(manager.fileExists(atPath: self.buildFolder.path, isDirectory: &isDirectory)) - XCTAssertTrue(isDirectory.boolValue) + #expect(manager.fileExists(atPath: self.jsonFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(manager.fileExists(atPath: self.buildFolder.path, isDirectory: &isDirectory)) + #expect(isDirectory.boolValue) } /// Test the clean command removes all generated files. + @Test func testCleanCommand() throws { let commandTypeErased = try CleanCommand.parseAsRoot([self.machine0Path.path]) guard var command = commandTypeErased as? CleanCommand else { - XCTFail("Failed to create command.") + Issue.record("Failed to create command.") return } - XCTAssertFalse(command.cleanBuildFolder) + #expect(!command.cleanBuildFolder) try command.run() - XCTAssertFalse(manager.fileExists(atPath: self.jsonFile.path)) - XCTAssertFalse(manager.fileExists(atPath: self.buildFolder.path)) + #expect(!manager.fileExists(atPath: self.jsonFile.path)) + #expect(!manager.fileExists(atPath: self.buildFolder.path)) var isDirectory: ObjCBool = true - XCTAssertTrue(manager.fileExists(atPath: self.modelFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) + #expect(manager.fileExists(atPath: self.modelFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) } /// Test command doesn't throw errors when files don't exist. + @Test func testCleanCommandStillWorksWithoutBuild() throws { CleanCommand.main([self.machine0Path.path]) CleanCommand.main([self.machine0Path.path]) - XCTAssertFalse(manager.fileExists(atPath: self.jsonFile.path)) - XCTAssertFalse(manager.fileExists(atPath: self.buildFolder.path)) + #expect(!manager.fileExists(atPath: self.jsonFile.path)) + #expect(!manager.fileExists(atPath: self.buildFolder.path)) var isDirectory: ObjCBool = true - XCTAssertTrue(manager.fileExists(atPath: self.modelFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) + #expect(manager.fileExists(atPath: self.modelFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) } /// Test that the correct error is thrown when the build folder is malformed. + @Test func testCommandThrowsErrorForInvalidBuildFolder() throws { try manager.removeItem(atPath: self.buildFolder.path) guard manager.createFile(atPath: self.buildFolder.path, contents: nil) else { - XCTFail("Failed to create build file.") + Issue.record("Failed to create build file.") return } let commandTypeErased = try CleanCommand.parseAsRoot([self.machine0Path.path]) guard var command = commandTypeErased as? CleanCommand else { - XCTFail("Failed to create command.") + Issue.record("Failed to create command.") return } - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Failed to throw correct error.") - return - } - XCTAssertEqual(error, .invalidExportation(message: "Found a file at the build folders location.")) - } + let error = #expect(throws: (GenerationError.self)) { try command.run() } + #expect(error == .invalidExportation(message: "Found a file at the build folders location.")) } /// Test that the correct error is thrown when the machine file is malformed. + @Test func testCommandThrowsErrorForInvalidMachine() throws { try manager.removeItem(at: jsonFile) let newPath = URL(fileURLWithPath: jsonFile.path, isDirectory: true) try manager.createDirectory(at: newPath, withIntermediateDirectories: true) let commandTypeErased = try CleanCommand.parseAsRoot([self.machine0Path.path]) guard var command = commandTypeErased as? CleanCommand else { - XCTFail("Failed to create command.") + Issue.record("Failed to create command.") return } - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Failed to throw correct error.") - return - } - XCTAssertEqual( - error, - .invalidExportation(message: "Found a directory at the machine files location.") - ) - } + let error = #expect(throws: (GenerationError.self)) { try command.run() } + #expect(error == .invalidExportation(message: "Found a directory at the machine files location.")) } /// Test that the `--clean-build-folder` flag ignores the machine file. + @Test func testFlagPreservesMachine() throws { CleanCommand.main(["--clean-build-folder", self.machine0Path.path]) var isDirectory: ObjCBool = true - XCTAssertTrue(manager.fileExists(atPath: self.jsonFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(manager.fileExists(atPath: self.buildFolder.path)) + #expect(manager.fileExists(atPath: self.jsonFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!manager.fileExists(atPath: self.buildFolder.path)) } /// Test that arrangements are cleaned correctly. + @Test func testCleanArrangement() throws { Generate.main([self.arrangement1Folder.path]) VHDLGenerator.main([self.arrangement1Folder.path]) let filesBefore = try self.manager.contentsOfDirectory(atPath: self.arrangement1Folder.path) - XCTAssertGreaterThan(filesBefore.count, 1) + #expect(filesBefore.count > 1) CleanCommand.main([self.arrangement1Folder.path]) let files = try self.manager.contentsOfDirectory(atPath: self.arrangement1Folder.path) .map { self.arrangement1Folder.appendingPathComponent($0) } - XCTAssertEqual( - files, - [self.arrangement1Folder.appendingPathComponent("model.json", isDirectory: false)] + #expect( + files == [self.arrangement1Folder.appendingPathComponent("model.json", isDirectory: false)] ) } diff --git a/Tests/GeneratorTests/GenerationErrorTests.swift b/Tests/GeneratorTests/GenerationErrorTests.swift index 591310ea..e6019369 100644 --- a/Tests/GeneratorTests/GenerationErrorTests.swift +++ b/Tests/GeneratorTests/GenerationErrorTests.swift @@ -53,21 +53,23 @@ // or write to the Free Software Foundation, Inc., 51 Franklin Street, // Fifth Floor, Boston, MA 02110-1301, USA. -import XCTest +import Testing @testable import GeneratorCommands /// Test class for ``GenerationError``. -final class GenerationErrorTests: XCTestCase { +@Suite +struct GenerationErrorTests { /// Test that the description gets the message correctly. + @Test func testDescription() { - XCTAssertEqual(GenerationError.invalidExportation(message: "A").description, "A") - XCTAssertEqual(GenerationError.invalidFormat(message: "B").description, "B") - XCTAssertEqual(GenerationError.invalidGeneration(message: "C").description, "C") - XCTAssertEqual(GenerationError.invalidInput(message: "D").description, "D") - XCTAssertEqual(GenerationError.invalidLayout(message: "E").description, "E") - XCTAssertEqual(GenerationError.invalidMachine(message: "F").description, "F") + #expect(GenerationError.invalidExportation(message: "A").description == "A") + #expect(GenerationError.invalidFormat(message: "B").description == "B") + #expect(GenerationError.invalidGeneration(message: "C").description == "C") + #expect(GenerationError.invalidInput(message: "D").description == "D") + #expect(GenerationError.invalidLayout(message: "E").description == "E") + #expect(GenerationError.invalidMachine(message: "F").description == "F") } } diff --git a/Tests/GeneratorTests/GeneratorTests.swift b/Tests/GeneratorTests/GeneratorTests.swift index d6f5a2ba..9c1f978a 100644 --- a/Tests/GeneratorTests/GeneratorTests.swift +++ b/Tests/GeneratorTests/GeneratorTests.swift @@ -58,11 +58,12 @@ import Foundation import JavascriptModel import VHDLMachines import VHDLMachineTransformations -import XCTest +import Testing @testable import GeneratorCommands /// Test class for ``Generate``. +@Suite final class GeneratorTests: MachineTester { // /// Test the setters set the correct values. @@ -77,46 +78,50 @@ final class GeneratorTests: MachineTester { // } /// Test computed properties function correctly. + @Test func testComputedProperties() throws { let generator = Generate(exportModel: false, path: pathRaw) - XCTAssertEqual(generator.pathURL, machine0Path) + #expect(generator.pathURL == machine0Path) let machineContents = try Data(contentsOf: jsonFile) - XCTAssertFalse(machineContents.isEmpty) - XCTAssertEqual(try generator.machine, machineContents) + #expect(!machineContents.isEmpty) + #expect((try generator.machine) == machineContents) let modelContents = try Data(contentsOf: modelFile) - XCTAssertFalse(modelContents.isEmpty) - XCTAssertEqual(try generator.model, modelContents) - XCTAssertEqual(generator.machinePath, jsonFile) - XCTAssertEqual(generator.modelPath, modelFile) + #expect(!modelContents.isEmpty) + #expect((try generator.model) == modelContents) + #expect(generator.machinePath == jsonFile) + #expect(generator.modelPath == modelFile) } /// Test the `createMachine` function correctly translates the model. + @Test func testCreateMachine() throws { let oldData = try Data(contentsOf: jsonFile) - XCTAssertFalse(oldData.isEmpty) + #expect(!oldData.isEmpty) try "Invalid data".write(to: jsonFile, atomically: true, encoding: .utf8) let generator = Generate(exportModel: false, path: pathRaw) try generator.createMachine() let newData = try generator.machine let newMachine = try decoder.decode(Machine.self, from: newData) let oldMachine = try decoder.decode(Machine.self, from: oldData) - XCTAssertEqual(newMachine, oldMachine) + #expect(newMachine == oldMachine) } /// Test that the createMachine function throws the correct error for an invalid model. + @Test func testCreateMachineThrows() throws { var model = MachineModel.machine0 model.externalVariables = "invalid data" let data = try encoder.encode(model) try data.write(to: modelFile) let generator = Generate(exportModel: false, path: pathRaw) - XCTAssertThrowsError(try generator.createMachine()) + #expect(throws: (any Error).self) { try generator.createMachine() } } /// Test that the model is created correctly. + @Test func testCreateModel() throws { let oldData = try Data(contentsOf: modelFile) - XCTAssertFalse(oldData.isEmpty) + #expect(!oldData.isEmpty) let oldModel = try decoder.decode(MachineModel.self, from: oldData) var invalidModel = oldModel invalidModel.externalVariables = "Invalid data" @@ -126,10 +131,11 @@ final class GeneratorTests: MachineTester { try generator.createModel() let newData = try generator.model let newModel = try decoder.decode(MachineModel.self, from: newData) - XCTAssertEqual(newModel, oldModel) + #expect(newModel == oldModel) } /// Test that the `createModel` method throws the correct error for an invalid state layout. + @Test func testCreateModelDetectsInvalidStateLayouts() throws { let oldData = try Data(contentsOf: modelFile) let oldModel = try decoder.decode(MachineModel.self, from: oldData) @@ -138,25 +144,21 @@ final class GeneratorTests: MachineTester { let invalidData = try encoder.encode(invalidModel) try invalidData.write(to: modelFile) let generator = Generate(exportModel: true, path: pathRaw) - XCTAssertThrowsError(try generator.createModel()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual( - error, - .invalidLayout( + let error = #expect(throws: GenerationError.self) { try generator.createModel() } + #expect( + error + == .invalidLayout( message: """ Found incorrect number of state layouts. Machine: 2 Model: 1 """ ) - ) - } + ) } /// Test that `createModel` detects an invalid number of transition layouts. + @Test func testCreateModelDetectsInvalidTransitionLayouts() throws { let oldData = try Data(contentsOf: modelFile) let oldModel = try decoder.decode(MachineModel.self, from: oldData) @@ -165,25 +167,21 @@ final class GeneratorTests: MachineTester { let invalidData = try encoder.encode(invalidModel) try invalidData.write(to: modelFile) let generator = Generate(exportModel: true, path: pathRaw) - XCTAssertThrowsError(try generator.createModel()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual( - error, - .invalidLayout( + let error = #expect(throws: GenerationError.self) { try generator.createModel() } + #expect( + error + == .invalidLayout( message: """ Found incorrect number of transition layouts. Machine: 1 Model: 0 """ ) - ) - } + ) } /// Test that `run` correctly generates model. + @Test func testRunGeneratesModel() throws { let oldData = try Data(contentsOf: modelFile) let oldModel = try decoder.decode(MachineModel.self, from: oldData) @@ -194,10 +192,11 @@ final class GeneratorTests: MachineTester { Generate.main(["--export-model", pathRaw]) let newData = try Data(contentsOf: modelFile) let newModel = try decoder.decode(MachineModel.self, from: newData) - XCTAssertEqual(newModel, oldModel) + #expect(newModel == oldModel) } /// Test that the `run` method generates the machine correctly. + @Test func testRunGeneratesMachine() throws { let oldData = try Data(contentsOf: jsonFile) let oldMachine = try decoder.decode(Machine.self, from: oldData) @@ -205,10 +204,11 @@ final class GeneratorTests: MachineTester { Generate.main([pathRaw]) let newData = try Data(contentsOf: jsonFile) let newMachine = try decoder.decode(Machine.self, from: newData) - XCTAssertEqual(newMachine, oldMachine) + #expect(newMachine == oldMachine) } /// Test that the arrangement is created correctly. + @Test func testArrangementCreation() throws { Generate.main([self.arrangement1Folder.path]) guard @@ -221,14 +221,15 @@ final class GeneratorTests: MachineTester { .path ) else { - XCTFail("Failed to read file.") + Issue.record("Failed to read file.") return } let arrangement = try self.decoder.decode(Arrangement.self, from: data) - XCTAssertEqual(arrangement, .pingArrangement) + #expect(arrangement == .pingArrangement) } /// Test that arrangement creation throws the correct error when arrangement generation fails. + @Test func testArrangementCreationThrowsCorrectErrorWhenFails() throws { let modelPath = self.arrangement1Folder.appendingPathComponent("model.json", isDirectory: false) try self.manager.removeItem(at: modelPath) @@ -236,7 +237,7 @@ final class GeneratorTests: MachineTester { let data = try self.encoder.encode(model) try data.write(to: modelPath) var command = try Generate.parse([self.arrangement1Folder.path]) - XCTAssertThrowsError(try command.run()) + #expect(throws: (any Error).self) { try command.run() } } } diff --git a/Tests/GeneratorTests/GraphTests.swift b/Tests/GeneratorTests/GraphTests.swift index 387233f5..5df3a0bd 100644 --- a/Tests/GeneratorTests/GraphTests.swift +++ b/Tests/GeneratorTests/GraphTests.swift @@ -53,17 +53,20 @@ // or write to the Free Software Foundation, Inc., 51 Franklin Street, // Fifth Floor, Boston, MA 02110-1301, USA. +import Foundation import VHDLKripkeStructures -import XCTest +import Testing @testable import GeneratorCommands /// Test class for ``GraphCommand``. +@Suite(.serialized) final class GraphTests: MachineTester { /// The path to the dot file. var dotFile: URL { - self.packageRootPath.appendingPathComponent("output.dot", isDirectory: false) + URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true) + .appendingPathComponent("output.dot", isDirectory: false) } /// The dot file in the build folder. @@ -72,111 +75,117 @@ final class GraphTests: MachineTester { } /// Remove the dot file after every test. - override func tearDown() { - super.tearDown() + deinit { try? manager.removeItem(at: self.dotFile) } /// Test the kripke structure is generated correctly for a machine. + @Test func testMachineGeneration() throws { - XCTAssertFalse(manager.fileExists(atPath: dotFile.path)) + #expect(!manager.fileExists(atPath: dotFile.path)) GraphCommand.main(["--machine", self.pingMachineFolder.path]) var isDirectory: ObjCBool = false - XCTAssertTrue(manager.fileExists(atPath: dotFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(try Data(contentsOf: dotFile).isEmpty) + #expect(manager.fileExists(atPath: dotFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!(try Data(contentsOf: dotFile)).isEmpty) } /// Test the kripke structure is generated correctly for a machine with a destination. + @Test func testMachineGenerationWithDestination() throws { - XCTAssertFalse(manager.fileExists(atPath: buildDotFile.path)) + #expect(!manager.fileExists(atPath: buildDotFile.path)) GraphCommand.main(["--machine", self.pingMachineFolder.path, "--destination", self.buildDotFile.path]) var isDirectory: ObjCBool = false - XCTAssertTrue(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(try Data(contentsOf: buildDotFile).isEmpty) + #expect(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!(try Data(contentsOf: buildDotFile)).isEmpty) } /// Test the generation given the file as a path. + @Test func testGeneration() throws { - XCTAssertFalse(manager.fileExists(atPath: dotFile.path)) + #expect(!manager.fileExists(atPath: dotFile.path)) GraphCommand.main([self.pingMachineKripkeStructure.path]) var isDirectory: ObjCBool = false - XCTAssertTrue(manager.fileExists(atPath: dotFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(try Data(contentsOf: dotFile).isEmpty) + #expect(manager.fileExists(atPath: dotFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!(try Data(contentsOf: dotFile)).isEmpty) } /// Test that generation works with a destination. + @Test func testGenerationWithDestination() throws { - XCTAssertFalse(manager.fileExists(atPath: buildDotFile.path)) + #expect(!manager.fileExists(atPath: buildDotFile.path)) GraphCommand.main([self.pingMachineKripkeStructure.path, "--destination", self.buildDotFile.path]) var isDirectory: ObjCBool = false - XCTAssertTrue(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(try Data(contentsOf: buildDotFile).isEmpty) + #expect(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!(try Data(contentsOf: buildDotFile)).isEmpty) } /// Test that the command throws an error for an invalid machine. + @Test func testGenerationThrowsErrorForInvalidMachine() throws { let command = try GraphCommand.parse(["--machine", self.pingMachineKripkeStructure.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError, case .invalidInput(let message) = error else { - XCTFail("Expected GenerationError but got \($0)") - return - } - XCTAssertEqual(message, "The path must be a valid machines location.") + let error = #expect(throws: (GenerationError.self)) { try command.run() } + guard case .invalidInput(let message) = error else { + Issue.record("Expected GenerationError but got \(error)") + return } - XCTAssertFalse(manager.fileExists(atPath: dotFile.path)) + #expect(message == "The path must be a valid machines location.") + #expect(!manager.fileExists(atPath: dotFile.path)) } /// Test that the correct error is thrown when the kripke structure is not present. + @Test func testCommandThrowsErrorForMissingKripkeStructure() throws { try manager.removeItem(at: self.pingMachineKripkeStructure) let command = try GraphCommand.parse([self.pingMachineKripkeStructure.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError, case .invalidMachine(let message) = error else { - XCTFail("Expected GenerationError but got \($0)") - return - } - XCTAssertEqual(message, "The Kripke structure does not exist at this specified location.") + let error = #expect(throws: (GenerationError.self)) { try command.run() } + guard case .invalidMachine(let message) = error else { + Issue.record("Expected GenerationError but got \(error)") + return } - XCTAssertFalse(manager.fileExists(atPath: dotFile.path)) + #expect(message == "The Kripke structure does not exist at this specified location.") + #expect(!manager.fileExists(atPath: dotFile.path)) } /// Test command overwrites existing `.dot` file. + @Test func testCommandOverwritesFile() throws { - XCTAssertTrue(manager.createFile(atPath: dotFile.path, contents: nil)) + #expect(manager.createFile(atPath: dotFile.path, contents: nil)) GraphCommand.main([self.pingMachineKripkeStructure.path, "--destination", self.dotFile.path]) var isDirectory: ObjCBool = false - XCTAssertTrue(manager.fileExists(atPath: dotFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(try Data(contentsOf: dotFile).isEmpty) + #expect(manager.fileExists(atPath: dotFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!(try Data(contentsOf: dotFile)).isEmpty) } /// Test that the file is created in a subdirectory that exists. + @Test func testCommandPlacesFileInFolderthatExists() throws { try manager.createDirectory(at: self.pingMachineBuildFolder, withIntermediateDirectories: true) - XCTAssertFalse(manager.fileExists(atPath: self.buildDotFile.path)) + #expect(!manager.fileExists(atPath: self.buildDotFile.path)) GraphCommand.main([ self.pingMachineKripkeStructure.path, "--destination", self.pingMachineBuildFolder.path, ]) var isDirectory: ObjCBool = false - XCTAssertTrue(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(try Data(contentsOf: buildDotFile).isEmpty) + #expect(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!(try Data(contentsOf: buildDotFile)).isEmpty) } /// Test that the file is created in a subdirectory that doesn't exist. + @Test func testCommandPlacesFileInFolder() throws { - XCTAssertFalse(manager.fileExists(atPath: self.pingMachineBuildFolder.path)) + #expect(!manager.fileExists(atPath: self.pingMachineBuildFolder.path)) GraphCommand.main([ self.pingMachineKripkeStructure.path, "--destination", self.pingMachineBuildFolder.path, ]) var isDirectory: ObjCBool = false - XCTAssertTrue(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) - XCTAssertFalse(isDirectory.boolValue) - XCTAssertFalse(try Data(contentsOf: buildDotFile).isEmpty) + #expect(manager.fileExists(atPath: buildDotFile.path, isDirectory: &isDirectory)) + #expect(!isDirectory.boolValue) + #expect(!(try Data(contentsOf: buildDotFile)).isEmpty) } } diff --git a/Tests/GeneratorTests/InstallCommandTests.swift b/Tests/GeneratorTests/InstallCommandTests.swift index d4b483ec..0c84cc54 100644 --- a/Tests/GeneratorTests/InstallCommandTests.swift +++ b/Tests/GeneratorTests/InstallCommandTests.swift @@ -55,11 +55,12 @@ import ArgumentParser import Foundation -import XCTest +import Testing @testable import GeneratorCommands /// Test class for ``InstallCommand``. +@Suite final class InstallCommandTests: MachineTester { /// The name of the vivado project. @@ -67,7 +68,7 @@ final class InstallCommandTests: MachineTester { /// The path to the vivado project. var vivadoPath: URL { - self.machinesFolder.appendingPathComponent("vivado_project", isDirectory: true) + self.machineScope.appendingPathComponent("vivado_project", isDirectory: true) } /// The path to the destination of the VHDL sources. @@ -91,15 +92,15 @@ final class InstallCommandTests: MachineTester { } /// Setup install locations before every run. - override func setUp() { - super.setUp() + override init() { + super.init() let projectContents = Data("project file!".utf8) guard (try? self.manager.createDirectory(at: vivadoPath, withIntermediateDirectories: true)) != nil, manager.createFile(atPath: self.projectFilePath.path, contents: projectContents), (try? self.manager.createDirectory(at: newPath, withIntermediateDirectories: true)) != nil else { - XCTFail("Failed to create vivado project.") + Issue.record("Failed to create vivado project.") return } Generate.main([self.machine0Path.path]) @@ -107,18 +108,19 @@ final class InstallCommandTests: MachineTester { } /// Remove install locations. - override func tearDown() { - super.tearDown() - XCTAssertNotNil(try? self.manager.removeItem(at: vivadoPath)) + deinit { + #expect((try? self.manager.removeItem(at: vivadoPath)) != nil) } /// Test the computed properties create the correct values. + @Test func testComputedProperties() throws { let command = try InstallCommand.parse([self.machine0Path.path, self.vivadoPath.path]) - XCTAssertEqual(command.installURL, self.vivadoPath) + #expect(command.installURL == self.vivadoPath) } /// Test files are copied correctly. + @Test func testInstallCommandWorksLocally() throws { try manager.removeItem( at: self.vivadoPath.appendingPathComponent("\(vivadoName).srcs", isDirectory: true) @@ -129,165 +131,130 @@ final class InstallCommandTests: MachineTester { let contents = try String(contentsOf: vhdlFilePath, encoding: .utf8) let vivadoVHDLFile = self.vivadoPath.appendingPathComponent("Machine0.vhd", isDirectory: false) let vivadoContents = try String(contentsOf: vivadoVHDLFile, encoding: .utf8) - XCTAssertEqual(contents, vivadoContents) + #expect(contents == vivadoContents) let files = try self.manager.contentsOfDirectory(at: self.vivadoPath, includingPropertiesForKeys: nil) - XCTAssertEqual(files.count, 2) + #expect(files.count == 2) let expected: Set = [self.projectFilePath.path, vivadoVHDLFile.path] - XCTAssertTrue(files.map(\.path).allSatisfy { expected.contains($0) }) - XCTAssertEqual(Set(files).count, 2) - XCTAssertEqual( + #expect(files.map(\.path).allSatisfy { expected.contains($0) }) + #expect(Set(files).count == 2) + #expect( try String( contentsOf: self.buildFolder.appendingPathComponent("vhdl/Machine0.vhd", isDirectory: false), encoding: .utf8 - ), - try String(contentsOf: vivadoVHDLFile, encoding: .utf8) + ) == (try String(contentsOf: vivadoVHDLFile, encoding: .utf8)) ) - XCTAssertEqual(previousProjectContents, try String(contentsOf: self.projectFilePath, encoding: .utf8)) + #expect(previousProjectContents == (try String(contentsOf: self.projectFilePath, encoding: .utf8))) } /// Test files are copied for vivado installation. + @Test func testInstallCommandWorksForVivado() throws { let previousProjectContents = try String(contentsOf: self.projectFilePath, encoding: .utf8) InstallCommand.main([self.machine0Path.path, "--vivado", self.vivadoPath.path]) - XCTAssertEqual(try String(contentsOf: self.projectFilePath, encoding: .utf8), previousProjectContents) + #expect(try String(contentsOf: self.projectFilePath, encoding: .utf8) == previousProjectContents) let projectFiles = try manager.contentsOfDirectory( at: self.vivadoPath, includingPropertiesForKeys: nil ) - XCTAssertEqual(projectFiles.count, 2) - XCTAssertEqual(Set(projectFiles).count, 2) + #expect(projectFiles.count == 2) + #expect(Set(projectFiles).count == 2) let expected: Set = [self.projectFilePath.path, self.vhdlSourcesPath.path] - XCTAssertTrue(projectFiles.map(\.path).allSatisfy(expected.contains)) - XCTAssertEqual( + #expect(projectFiles.map(\.path).allSatisfy(expected.contains)) + #expect( try manager.contentsOfDirectory(at: self.vhdlSourcesPath, includingPropertiesForKeys: nil) - .map(\.path), - [self.sources1Path.path] + .map(\.path) == [self.sources1Path.path] ) - XCTAssertEqual( + #expect( try manager.contentsOfDirectory(at: self.sources1Path, includingPropertiesForKeys: nil) - .map(\.path), - [self.newPath.path] + .map(\.path) == [self.newPath.path] ) let vhdlFiles = try manager.contentsOfDirectory(at: self.newPath, includingPropertiesForKeys: nil) - XCTAssertEqual( - vhdlFiles.map(\.path), - [self.newPath.appendingPathComponent("Machine0.vhd", isDirectory: false).path] + #expect( + vhdlFiles.map(\.path) == [ + self.newPath.appendingPathComponent("Machine0.vhd", isDirectory: false).path + ] ) let vhdlFilePath = self.buildFolder.appendingPathComponent("vhdl/Machine0.vhd", isDirectory: false) let contents = try String(contentsOf: vhdlFilePath, encoding: .utf8) - XCTAssertEqual(contents, try String(contentsOf: vhdlFilePath, encoding: .utf8)) + #expect(contents == (try String(contentsOf: vhdlFilePath, encoding: .utf8))) } /// Test that an error is thrown for folders without the `.machine` extension. + @Test func testThrowsErrorForIncorrectMachineFolder() throws { - let command = try InstallCommand.parse([self.machinesFolder.path, self.vivadoPath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual(error, .invalidMachine(message: "The path provided is not a machine.")) - } + let command = try InstallCommand.parse([self.machineScope.path, self.vivadoPath.path]) + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect(error == .invalidMachine(message: "The path provided is not a machine.")) } /// Test that the correct error is thrown for a file path to the machine. + @Test func testThrowsErrorForFilePath() throws { let newFile = self.vivadoPath.appendingPathComponent("Machine0.machine", isDirectory: false) - XCTAssertTrue(self.manager.createFile(atPath: newFile.path, contents: Data("new file".utf8))) + #expect(self.manager.createFile(atPath: newFile.path, contents: Data("new file".utf8))) let command = try InstallCommand.parse([newFile.path, self.vivadoPath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual(error, .invalidMachine(message: "The path provided is not a valid machine.")) - } + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect(error == .invalidMachine(message: "The path provided is not a valid machine.")) } /// Test that the correct error is thrown for an invalid install location. + @Test func testThrowsErrorForInvalidInstallLocation() throws { let command = try InstallCommand.parse([self.machine0Path.path, self.projectFilePath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual(error, .invalidInput(message: "The install directory is incorrect.")) - } + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect(error == .invalidInput(message: "The install directory is incorrect.")) } /// Test that the correct error is thrown when the VHDL files are not generated. + @Test func testThrowsErrorForMissingBuildFolder() throws { try manager.removeItem(at: self.buildFolder) let command = try InstallCommand.parse([self.machine0Path.path, self.vivadoPath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual( - error, - .invalidGeneration( + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect( + error + == .invalidGeneration( message: "The build folder does not exist. Have you generated the VHDL files?" ) - ) - } + ) } /// Test that the correct error is thrown when untracked files are placed in the build folder. + @Test func testThrowsErrorForCorruptedBuildFolder() throws { let corruptedFile = self.buildFolder.appendingPathComponent("vhdl/Machine0", isDirectory: false) - XCTAssertTrue( + #expect( self.manager.createFile( atPath: corruptedFile.path, contents: Data("corrupted".utf8) ) ) let command = try InstallCommand.parse([self.machine0Path.path, self.vivadoPath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual( - error, - .invalidGeneration( + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect( + error + == .invalidGeneration( message: "The build folder is corrupted! Please regenerate the VHDL files." ) - ) - } + ) } /// Test that the correct error is thrown when the vivado project is invalid. + @Test func testVivadoGenerationThrowsErrorForInvalidVivadoProject() throws { try manager.removeItem(at: self.projectFilePath) let command = try InstallCommand.parse([self.machine0Path.path, "--vivado", self.vivadoPath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual( - error, - .invalidInput(message: "The install directory is not a valid vivado project.") - ) - } + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect(error == .invalidInput(message: "The install directory is not a valid vivado project.")) } /// Test that the correct error is thrown when the vivado project is missing key folders. + @Test func testVivadoGenerationThrowsErrorForCorruptedVivadoProject() throws { try manager.removeItem(at: self.vhdlSourcesPath) let command = try InstallCommand.parse([self.machine0Path.path, "--vivado", self.vivadoPath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Error is not of type GenerationError.") - return - } - XCTAssertEqual( - error, - .invalidInput(message: "The vivado project is not set up correctly.") - ) - } + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect(error == .invalidInput(message: "The vivado project is not set up correctly.")) } } diff --git a/Tests/GeneratorTests/LLFSMGenerateTests.swift b/Tests/GeneratorTests/LLFSMGenerateTests.swift index 8e3429b3..2491505a 100644 --- a/Tests/GeneratorTests/LLFSMGenerateTests.swift +++ b/Tests/GeneratorTests/LLFSMGenerateTests.swift @@ -57,11 +57,12 @@ import ArgumentParser import Foundation import VHDLMachines -import XCTest +import Testing @testable import GeneratorCommands /// Test class for `llfsmgenerate` parent struct. +@Suite final class LLFSMGenerateTests: MachineTester { // /// Test the `run` methods calls the model subcommand. @@ -85,16 +86,18 @@ final class LLFSMGenerateTests: MachineTester { // } /// Test sub-commands exist for parent binary. + @Test func testSubCommands() { let subcommands = LLFSMGenerate.configuration.subcommands let expectedCommands = ["model", "vhdl", "clean", "install", "graph", "report"] - XCTAssertEqual(subcommands.map { $0._commandName }.sorted(), expectedCommands.sorted()) + #expect(subcommands.map { $0._commandName }.sorted() == expectedCommands.sorted()) } /// Test run command handles no arguments. + @Test func testRun() throws { var command = LLFSMGenerate() - XCTAssertThrowsError(try command.run()) + #expect(throws: (any Error).self) { try command.run() } } } diff --git a/Tests/GeneratorTests/Machine0Tests.swift b/Tests/GeneratorTests/Machine0Tests.swift index b1a2f335..9fc37daa 100644 --- a/Tests/GeneratorTests/Machine0Tests.swift +++ b/Tests/GeneratorTests/Machine0Tests.swift @@ -57,27 +57,20 @@ import JavascriptModel import VHDLMachines import VHDLMachineTransformations -import XCTest +import Testing /// Test class for ``Machine0``. -final class Machine0Tests: XCTestCase { - - /// A path to Machine0. - var pathRaw: String { - String(packagePath) + "/Tests/GeneratorTests/machines" - } - - /// A URL to the package root. - let packagePath = URL(fileURLWithPath: #file).pathComponents.prefix { $0 != "Tests" } - .joined(separator: "/") - .dropFirst() +@Suite +final class Machine0Tests: MachineTester { /// Test generation is correct. + @Test func testGeneration() throws { - XCTAssertEqual(try Machine(model: .machine0), Machine.machine0) + #expect(try Machine(model: .machine0) == Machine.machine0) } /// Test the model generation is correct. + @Test func testModelGeneration() throws { let machine = Machine.machine0 let model = MachineModel.machine0 @@ -86,7 +79,7 @@ final class Machine0Tests: XCTestCase { stateLayouts: model.states.map(\.layout), transitionLayouts: model.transitions.map(\.layout) ) - XCTAssertEqual(model, result) + #expect(model == result) } } diff --git a/Tests/GeneratorTests/MachineTester.swift b/Tests/GeneratorTests/MachineTester.swift index 9cd5439f..d9731f73 100644 --- a/Tests/GeneratorTests/MachineTester.swift +++ b/Tests/GeneratorTests/MachineTester.swift @@ -59,10 +59,13 @@ import JavascriptModel import TestHelpers import VHDLKripkeStructures import VHDLMachines -import XCTest +import Testing +import Foundation /// A super class for mutating Machine0 on the file system. -class MachineTester: XCTestCase { +class MachineTester { + + let packageScope: String = UUID().uuidString /// A JSON encoder. let encoder = JSONEncoder() @@ -75,25 +78,29 @@ class MachineTester: XCTestCase { /// A path to Machine0. var pathRaw: String { - String(packagePath) + "/Tests/GeneratorTests/machines/Machine0.machine" + String(packagePath) + "/Tests/GeneratorTests/machines/\(packageScope)/Machine0.machine" } /// A URL to the package root. - let packagePath = FileManager.default.currentDirectoryPath + private let packagePath = FileManager.default.currentDirectoryPath /// The path to the package root. - var packageRootPath: URL { + private var packageRootPath: URL { URL(fileURLWithPath: String(packagePath), isDirectory: true) } /// The path to the machines folder. - var machinesFolder: URL { + private var machinesFolder: URL { packageRootPath.appendingPathComponent("Tests/GeneratorTests/machines", isDirectory: true) } /// The path to Machine0. var machine0Path: URL { - machinesFolder.appendingPathComponent("Machine0.machine", isDirectory: true) + machineScope.appendingPathComponent("Machine0.machine", isDirectory: true) + } + + var machineScope: URL { + machinesFolder.appendingPathComponent(packageScope, isDirectory: true) } /// The path to the machines json file. @@ -113,12 +120,12 @@ class MachineTester: XCTestCase { /// The path to the `Arragnement1` folder. var arrangement1Folder: URL { - machinesFolder.appendingPathComponent("Arrangement1.arrangement", isDirectory: true) + machineScope.appendingPathComponent("Arrangement1.arrangement", isDirectory: true) } /// The path to the `PingMachine` folder. var pingMachineFolder: URL { - machinesFolder.appendingPathComponent("PingMachine.machine", isDirectory: true) + machineScope.appendingPathComponent("PingMachine.machine", isDirectory: true) } /// The build folder in the ping machine. @@ -132,7 +139,7 @@ class MachineTester: XCTestCase { } /// Create test machines before every test. - override func setUp() { + init() { let createDir: ()? = try? manager .createDirectory(at: machine0Path, withIntermediateDirectories: true) @@ -148,7 +155,7 @@ class MachineTester: XCTestCase { let modelData = try? encoder.encode(MachineModel.machine0), let structureData = try? encoder.encode(KripkeStructure.pingPongStructure) else { - XCTFail("Failed to create machine!") + Issue.record("Failed to create machine!") return } try? self.manager.createDirectory(at: self.pingMachineFolder, withIntermediateDirectories: true) @@ -159,17 +166,18 @@ class MachineTester: XCTestCase { to: arrangement1Folder.appendingPathComponent("model.json", isDirectory: false) ) let result: ()? = try? data.write(to: jsonFile) - XCTAssertNotNil(result) + #expect(result != nil) let modelResult: ()? = try? modelData.write(to: modelFile) - XCTAssertNotNil(modelResult) + #expect(modelResult != nil) } - /// Remove test machines before every test. - override func tearDown() { - let result: ()? = try? FileManager.default.removeItem(at: machine0Path) + /// Remove test machines after every test. + deinit { + let result: ()? = try? FileManager.default.removeItem(at: machineScope) + try? self.manager.removeItem(at: machine0Path) try? self.manager.removeItem(at: arrangement1Folder) try? self.manager.removeItem(at: pingMachineFolder) - XCTAssertNotNil(result) + #expect(result != nil) } } diff --git a/Tests/GeneratorTests/PathArgumentTests.swift b/Tests/GeneratorTests/PathArgumentTests.swift index 66576182..daedb72e 100644 --- a/Tests/GeneratorTests/PathArgumentTests.swift +++ b/Tests/GeneratorTests/PathArgumentTests.swift @@ -55,12 +55,13 @@ import ArgumentParser import Foundation -import XCTest +import Testing @testable import GeneratorCommands /// Test class for ``PathArgument``. -final class PathArgumentTests: XCTestCase { +@Suite +struct PathArgumentTests { /// A command with a path. var command: Generate { @@ -77,15 +78,16 @@ final class PathArgumentTests: XCTestCase { } /// Test that the computed properties are correct. + @Test func testComputedProperties() throws { let url = URL(fileURLWithPath: "path", isDirectory: true) - XCTAssertEqual(try argument.pathURL, url) + #expect(try argument.pathURL == url) let machine = url.appendingPathComponent("machine.json", isDirectory: false) - XCTAssertEqual(try argument.machine, machine) + #expect(try argument.machine == machine) let build = url.appendingPathComponent("build", isDirectory: true) - XCTAssertEqual(try argument.buildFolder, build) + #expect(try argument.buildFolder == build) let vhdl = build.appendingPathComponent("vhdl", isDirectory: true) - XCTAssertEqual(try argument.vhdlFolder, vhdl) + #expect(try argument.vhdlFolder == vhdl) } } diff --git a/Tests/GeneratorTests/ReportCommandTests.swift b/Tests/GeneratorTests/ReportCommandTests.swift index bfea54f1..1653db29 100644 --- a/Tests/GeneratorTests/ReportCommandTests.swift +++ b/Tests/GeneratorTests/ReportCommandTests.swift @@ -56,9 +56,11 @@ @testable import GeneratorCommands import JavascriptModel import TestHelpers -import XCTest +import Testing +import Foundation /// Test class for ``ReportCommand``. +@Suite final class ReportCommandTests: MachineTester { // swiftlint:disable trailing_whitespace @@ -124,24 +126,26 @@ final class ReportCommandTests: MachineTester { } /// Remove file after every test case. - override func tearDown() { - super.tearDown() + deinit { try? self.manager.removeItem(at: file) } /// Test the machine is encoded correctly. + @Test func testMachineEncoding() throws { let machine = String(reportForMachine: .pingMachine, name: "PingMachine.machine") - XCTAssertEqual(machine, pingMachineRaw) + #expect(machine == pingMachineRaw) } /// Test the kripke structure is encoded correctly. + @Test func testKripkeStructureEncoding() throws { let structure = String(reportForStructure: .pingPongStructure) - XCTAssertEqual(structure, structureRaw) + #expect(structure == structureRaw) } /// Test the string category init handles data correctly. + @Test func testStringCategoryInit() { let data = "This is\nsome data!" let withData = """ @@ -150,11 +154,12 @@ final class ReportCommandTests: MachineTester { some data! """ let withoutData = "- Data:" - XCTAssertEqual(String(category: "Data", data: data), withData) - XCTAssertEqual(String(category: "Data", data: ""), withoutData) + #expect(String(category: "Data", data: data) == withData) + #expect(String(category: "Data", data: "") == withoutData) } /// Test that writing to the file works correctly. + @Test func testWriteReport() throws { let data0 = "data0" let file = self.pingMachineFolder.appendingPathComponent( @@ -163,21 +168,22 @@ final class ReportCommandTests: MachineTester { ) let printCommand = try ReportCommand.parse([self.pingMachineFolder.path]) try printCommand.writeReport(report: data0) - XCTAssertFalse(self.manager.fileExists(atPath: file.path)) + #expect(!self.manager.fileExists(atPath: file.path)) let command = try ReportCommand.parse([self.pingMachineFolder.path, "--output", file.path]) try command.writeReport(report: data0) let contents0 = try String(contentsOf: file, encoding: .utf8) - XCTAssertEqual(data0, contents0) + #expect(data0 == contents0) let data1 = "data1" try command.writeReport(report: data1) let contents1 = try String(contentsOf: file, encoding: .utf8) - XCTAssertEqual(data1, contents1) + #expect(data1 == contents1) } // swiftlint:disable trailing_whitespace // swiftlint:disable function_body_length /// Test that the run methods works correctly. + @Test func testRunWithKripkeStructure() throws { ReportCommand.main([self.pingMachineFolder.path, "--output", file.path]) let contents = try String(contentsOf: file, encoding: .utf8) @@ -231,10 +237,11 @@ final class ReportCommandTests: MachineTester { - Edges: 13 """ - XCTAssertEqual(contents, expected) + #expect(contents == expected) } /// Test the run method works without a kripke structure. + @Test func testRunWithoutKripkeStructure() throws { try self.manager.removeItem(at: self.pingMachineKripkeStructure) ReportCommand.main([self.pingMachineFolder.path, "--output", file.path]) @@ -285,7 +292,7 @@ final class ReportCommandTests: MachineTester { use IEEE.std_logic_1164.all; """ - XCTAssertEqual(contents, expected) + #expect(contents == expected) } // swiftlint:enable function_body_length diff --git a/Tests/GeneratorTests/VHDLGeneratorTests.swift b/Tests/GeneratorTests/VHDLGeneratorTests.swift index 13e6b177..50b0063a 100644 --- a/Tests/GeneratorTests/VHDLGeneratorTests.swift +++ b/Tests/GeneratorTests/VHDLGeneratorTests.swift @@ -59,115 +59,100 @@ import SwiftUtils import VHDLKripkeStructureGenerator import VHDLMachines import VHDLParsing -import XCTest +import Testing @testable import GeneratorCommands /// Test class for ``VHDLGenerator``. +@Suite final class VHDLGeneratorTests: MachineTester { /// A Kripke structure generator. let generator = VHDLKripkeStructureGenerator() /// Test the main method generates the `VHDL` file correctly. + @Test func testRunGeneratesVHDL() throws { let machine = Machine.machine0 VHDLGenerator.main([pathRaw]) let vhdlPath = machine0Path.appendingPathComponent("build/vhdl/Machine0.vhd", isDirectory: false) guard let representation = MachineRepresentation(machine: machine, name: .machine0) else { - XCTFail("Failed to create VHDL for machine.") + Issue.record("Failed to create VHDL for machine.") return } let vhdlFile = VHDLFile(representation: representation) - XCTAssertEqual(vhdlFile.rawValue + "\n", try String(contentsOf: vhdlPath)) + #expect(vhdlFile.rawValue + "\n" == (try String(contentsOf: vhdlPath, encoding: .utf8))) } /// Test that the main method throws the correct error for an invalid machine. + @Test func testRunThrowsErrorForInvalidMachine() throws { var machine = Machine.machine0 guard let onEntry = VariableName(rawValue: "OnEntry") else { - XCTFail("Failed to create machine.") + Issue.record("Failed to create machine.") return } machine.machineSignals += [LocalSignal(type: .stdLogic, name: onEntry)] let data = try encoder.encode(machine) try data.write(to: jsonFile) var generator = try VHDLGenerator.parseAsRoot([pathRaw]) - XCTAssertThrowsError(try generator.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Thrown incorrect error.") - return - } - XCTAssertEqual(error, .invalidGeneration(message: "Failed to generate VHDL for Machine0.")) - } + let error = #expect(throws: GenerationError.self) { try generator.run() } + #expect(error == .invalidGeneration(message: "Failed to generate VHDL for Machine0.")) } /// Test the correct error is thrown for paths without the correct extension. + @Test func testPathWithoutExtension() throws { - let invalidPath = self.machinesFolder.appendingPathComponent("invalid", isDirectory: true) + let invalidPath = self.machineScope.appendingPathComponent("invalid", isDirectory: true) try self.manager.copyItem(at: self.machine0Path, to: invalidPath) defer { _ = try? self.manager.removeItem(at: invalidPath) } var command = try VHDLGenerator.parse([invalidPath.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Thrown incorrect error.") - return - } - XCTAssertEqual( - error, - .invalidFormat( + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect( + error + == .invalidFormat( message: "The machine specified is invalid. " + "Please make sure you specify a machine with the .machine extension and valid name." ) - ) - } + ) } /// Test correct errors are thrown for paths with only the machine extension. + @Test func testEmptyMachinePath() throws { - let path = self.machinesFolder.appendingPathComponent(".machine", isDirectory: true) + let path = self.machineScope.appendingPathComponent(".machine", isDirectory: true) try self.manager.copyItem(at: self.machine0Path, to: path) defer { _ = try? self.manager.removeItem(at: path) } var command = try VHDLGenerator.parse([path.path]) - XCTAssertThrowsError(try command.run()) { - print($0.localizedDescription) - fflush(stdout) - guard let error = $0 as? GenerationError else { - XCTFail("Thrown incorrect error.") - return - } - XCTAssertEqual( - error, - .invalidFormat( + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect( + error + == .invalidFormat( message: "The machine specified is invalid. " + "Please make sure you specify a machine with the .machine extension and valid name." ) - ) - } + ) } /// Test that VHDL keyword in machine name throw an error. + @Test func testVHDLNameInPath() throws { - let path = self.machinesFolder.appendingPathComponent("signal.machine", isDirectory: true) + let path = self.machineScope.appendingPathComponent("signal.machine", isDirectory: true) try self.manager.copyItem(at: self.machine0Path, to: path) defer { _ = try? self.manager.removeItem(at: path) } var command = try VHDLGenerator.parse([path.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Thrown incorrect error.") - return - } - XCTAssertEqual( - error, - .invalidFormat( + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect( + error + == .invalidFormat( message: "The machine specified is invalid. " + "Please make sure you specify a machine with the .machine extension and valid name." ) - ) - } + ) } /// Test the VHDL generator creates the correct Kripke structure. + @Test func testRunGeneratesKripkeStructure() throws { let machine = Machine.machine0 VHDLGenerator.main(["--include-kripke-structure", pathRaw]) @@ -175,7 +160,7 @@ final class VHDLGeneratorTests: MachineTester { let representation = MachineRepresentation(machine: machine, name: .machine0), let files = generator.generateAll(representation: representation) else { - XCTFail("Failed to create VHDL for machine.") + Issue.record("Failed to create VHDL for machine.") return } files.preferredFilename = "build" @@ -183,12 +168,13 @@ final class VHDLGeneratorTests: MachineTester { } /// Test that the `VHDL` file is created correctly for an arrangement. + @Test func testArrangementCreation() throws { Generate.main([self.arrangement1Folder.path]) VHDLGenerator.main([self.arrangement1Folder.path]) guard let contents = self.manager.contents( - atPath: self.machinesFolder + atPath: self.machineScope .appendingPathComponent( "Arrangement1.arrangement/build/vhdl/Arrangement1.vhd", isDirectory: false @@ -196,7 +182,7 @@ final class VHDLGeneratorTests: MachineTester { .path ) else { - XCTFail("File doesn't exist!") + Issue.record("File doesn't exist!") return } let expected = ArrangementRepresentation( @@ -204,41 +190,38 @@ final class VHDLGeneratorTests: MachineTester { name: .arrangement1 )? .file.rawValue - XCTAssertNotNil(expected) - XCTAssertEqual(String(data: contents, encoding: .utf8), expected) + #expect(expected != nil) + #expect(String(data: contents, encoding: .utf8) == expected) } /// Test that invalid names are detected. + @Test func testArrangementCreationFailsForInvalidName() throws { - let newDir = self.machinesFolder.appendingPathComponent( + let newDir = self.machineScope.appendingPathComponent( "Arrangement1.new.arrangement", isDirectory: true ) try self.manager.moveItem(at: self.arrangement1Folder, to: newDir) defer { try? self.manager.removeItem(at: newDir) } var command = try VHDLGenerator.parse([newDir.path]) - XCTAssertThrowsError(try command.run()) { - guard let error = $0 as? GenerationError else { - XCTFail("Thrown incorrect error.") - return - } - XCTAssertEqual( - error, - .invalidFormat( + let error = #expect(throws: GenerationError.self) { try command.run() } + #expect( + error + == .invalidFormat( message: "The arrangement is not named correctly!" ) - ) - } + ) } /// Test that the arrangement creation works when `vhdl` folder already exists. + @Test func testArrangementCreationWhenVHDLExists() throws { Generate.main([self.arrangement1Folder.path]) VHDLGenerator.main([self.arrangement1Folder.path]) VHDLGenerator.main([self.arrangement1Folder.path]) guard let contents = self.manager.contents( - atPath: self.machinesFolder + atPath: self.machineScope .appendingPathComponent( "Arrangement1.arrangement/build/vhdl/Arrangement1.vhd", isDirectory: false @@ -246,7 +229,7 @@ final class VHDLGeneratorTests: MachineTester { .path ) else { - XCTFail("File doesn't exist!") + Issue.record("File doesn't exist!") return } let expected = ArrangementRepresentation( @@ -254,11 +237,12 @@ final class VHDLGeneratorTests: MachineTester { name: .arrangement1 )? .file.rawValue - XCTAssertNotNil(expected) - XCTAssertEqual(String(data: contents, encoding: .utf8), expected) + #expect(expected != nil) + #expect(String(data: contents, encoding: .utf8) == expected) } /// Test that the arrangement creation works when machines are already compiled. + @Test func testArrangementCreationWhenMachinesHaveVHDL() throws { Generate.main([self.pingMachineFolder.path]) Generate.main([self.arrangement1Folder.path]) @@ -266,7 +250,7 @@ final class VHDLGeneratorTests: MachineTester { VHDLGenerator.main([self.arrangement1Folder.path]) guard let contents = self.manager.contents( - atPath: self.machinesFolder + atPath: self.machineScope .appendingPathComponent( "Arrangement1.arrangement/build/vhdl/Arrangement1.vhd", isDirectory: false @@ -274,7 +258,7 @@ final class VHDLGeneratorTests: MachineTester { .path ) else { - XCTFail("File doesn't exist!") + Issue.record("File doesn't exist!") return } let expected = ArrangementRepresentation( @@ -282,15 +266,15 @@ final class VHDLGeneratorTests: MachineTester { name: .arrangement1 )? .file.rawValue - XCTAssertNotNil(expected) - XCTAssertEqual(String(data: contents, encoding: .utf8), expected) + #expect(expected != nil) + #expect(String(data: contents, encoding: .utf8) == expected) } /// Assert a file wrapper contents recursively against the file system. func assertContents(wrapper: FileWrapper, parentFolder: URL) throws { guard wrapper.isDirectory else { guard let name = wrapper.preferredFilename else { - XCTFail("Failed to read file contents.") + Issue.record("Failed to read file contents.") return } try assertContents(name: name, wrapper: wrapper, parentFolder: parentFolder) @@ -300,7 +284,7 @@ final class VHDLGeneratorTests: MachineTester { let name = wrapper.preferredFilename ?? wrapper.filename ?? "" let wrapperString = "{ \(wrapper.isDirectory), \(wrapper.fileWrappers ?? [:]) }" + "\n\(parentFolder.path)" - XCTFail("Failed to read file contents in \(name).\nWrapper: \(wrapperString)") + Issue.record("Failed to read file contents in \(name).\nWrapper: \(wrapperString)") return } let path = parentFolder.appendingPathComponent(name, isDirectory: true) @@ -314,14 +298,15 @@ final class VHDLGeneratorTests: MachineTester { return } guard let data = wrapper.regularFileContents else { - XCTFail("Failed to read file contents in \(name).") + Issue.record("Failed to read file contents in \(name).") return } let contents = String(data: data, encoding: .utf8) let result = try String( - contentsOf: parentFolder.appendingPathComponent("\(name)", isDirectory: false) + contentsOf: parentFolder.appendingPathComponent("\(name)", isDirectory: false), + encoding: .utf8, ) - XCTAssertEqual(contents, result) + #expect(contents == result) } } diff --git a/Tests/TestHelpers/Arrangement+pingArrangement.swift b/Tests/TestHelpers/Arrangement+pingArrangement.swift index 7584b6d4..7caeb5a9 100644 --- a/Tests/TestHelpers/Arrangement+pingArrangement.swift +++ b/Tests/TestHelpers/Arrangement+pingArrangement.swift @@ -64,7 +64,7 @@ import VHDLParsing extension Arrangement { /// An arrangement containing the `PingMachine`. - public static let pingArrangement = Arrangement( + package static let pingArrangement = Arrangement( mappings: [ MachineInstance(name: .pingMachineInst, type: .pingMachine): MachineMapping( machine: .pingMachine, diff --git a/Tests/TestHelpers/ArrangementModel+pingArrangement.swift b/Tests/TestHelpers/ArrangementModel+pingArrangement.swift index 30cb6ae2..61f50dbc 100644 --- a/Tests/TestHelpers/ArrangementModel+pingArrangement.swift +++ b/Tests/TestHelpers/ArrangementModel+pingArrangement.swift @@ -62,7 +62,7 @@ extension ArrangementModel { /// Create a ping arrangement that contains a ping machine located at `path`. /// - Parameter path: The location of the ping machine. /// - Returns: The arrangement model. - public static func pingArrangement(path: URL) -> ArrangementModel { + package static func pingArrangement(path: URL) -> ArrangementModel { ArrangementModel( clocks: [ClockModel(name: "clk", frequency: "5 MHz")], externalVariables: "externalPing: out std_logic; externalPong: out std_logic;", diff --git a/Tests/TestHelpers/FileTester.swift b/Tests/TestHelpers/FileTester.swift index ddc39273..cd4c7d75 100644 --- a/Tests/TestHelpers/FileTester.swift +++ b/Tests/TestHelpers/FileTester.swift @@ -54,38 +54,50 @@ // Fifth Floor, Boston, MA 02110-1301, USA. import Foundation -import XCTest -/// Add helper methods for testing files. -open class FileTester: XCTestCase { +package protocol FileTester { + var encoder: JSONEncoder { get } + var decoder: JSONDecoder { get } + var manager: FileManager { get } + var currentDirectory: URL { get } + var generatorDirectory: URL { get } + var testsDirectory: URL { get } + var transformationsDirectory: URL { get } +} +extension FileTester { /// A `JSON` encoder. - public let encoder = JSONEncoder() + package var encoder: JSONEncoder { + JSONEncoder() + } /// A `JSON` decoder. - public let decoder = JSONDecoder() + package var decoder: JSONDecoder { + JSONDecoder() + } /// A file manager. - public let manager = FileManager.default + package var manager: FileManager { + FileManager.default + } /// The path to the root directory of the package. - public var currentDirectory: URL { + package var currentDirectory: URL { URL(fileURLWithPath: manager.currentDirectoryPath, isDirectory: true) } /// The path to the `MachineGeneratorTests` target. - public var generatorDirectory: URL { + package var generatorDirectory: URL { testsDirectory.appendingPathComponent("MachineGeneratorTests", isDirectory: true) } /// The path to the `Tests` directory. - public var testsDirectory: URL { + package var testsDirectory: URL { currentDirectory.appendingPathComponent("Tests", isDirectory: true) } /// The path to the `VHDLMachinesTransformationsTests` target. - public var transformationsDirectory: URL { + package var transformationsDirectory: URL { testsDirectory.appendingPathComponent("VHDLMachineTransformationsTests", isDirectory: true) } - } diff --git a/Tests/TestHelpers/KripkeStructure+testStructure.swift b/Tests/TestHelpers/KripkeStructure+testStructure.swift index e9336c1f..fac52411 100644 --- a/Tests/TestHelpers/KripkeStructure+testStructure.swift +++ b/Tests/TestHelpers/KripkeStructure+testStructure.swift @@ -63,14 +63,14 @@ import VHDLParsing extension KripkeStructure { /// The ping pong kripke structure. - public static let pingPongStructure: KripkeStructure = { + package static let pingPongStructure: KripkeStructure = { let decoder = JSONDecoder() // swiftlint:disable:next force_unwrapping return (try? decoder.decode(KripkeStructure.self, from: KripkeStructure.raw.data(using: .utf8)!))! }() /// The raw JSON string for the ping pong kripke structure. - static let raw = """ + package static let raw = """ { "edges" : [ { diff --git a/Tests/TestHelpers/Machine+pingMachine.swift b/Tests/TestHelpers/Machine+pingMachine.swift index 3e2ec613..09f5a7e1 100644 --- a/Tests/TestHelpers/Machine+pingMachine.swift +++ b/Tests/TestHelpers/Machine+pingMachine.swift @@ -62,7 +62,7 @@ import VHDLParsing extension Machine { /// A `PingMachine`. - public static let pingMachine = Machine( + package static let pingMachine = Machine( actions: [.internal, .onEntry, .onExit], includes: [.library(value: .ieee), .include(statement: .stdLogic1164)], externalSignals: [ diff --git a/Tests/TestHelpers/MachineModel+pingMachine.swift b/Tests/TestHelpers/MachineModel+pingMachine.swift index 0f5425ad..804354c0 100644 --- a/Tests/TestHelpers/MachineModel+pingMachine.swift +++ b/Tests/TestHelpers/MachineModel+pingMachine.swift @@ -59,7 +59,7 @@ import JavascriptModel extension MachineModel { /// Add ping machine. - public static let pingMachine = MachineModel( + package static let pingMachine = MachineModel( states: [ StateModel( name: "Initial", diff --git a/Tests/TestHelpers/UseStatement+constants.swift b/Tests/TestHelpers/UseStatement+constants.swift index 800e9875..e81f7ccf 100644 --- a/Tests/TestHelpers/UseStatement+constants.swift +++ b/Tests/TestHelpers/UseStatement+constants.swift @@ -62,12 +62,12 @@ import VHDLParsing extension UseStatement { /// An import to the `IEEE.std_logic_1164.all` module. - public static let stdLogic1164 = UseStatement( + package static let stdLogic1164 = UseStatement( nonEmptyComponents: [.module(name: .ieee), .module(name: .stdLogic1164), .all] )! /// An import to the `IEEE.math_real.all` module. - public static let mathReal = UseStatement( + package static let mathReal = UseStatement( nonEmptyComponents: [.module(name: .ieee), .module(name: .mathReal), .all] )! diff --git a/Tests/TestHelpers/VariableName+testNames.swift b/Tests/TestHelpers/VariableName+testNames.swift index 37f3edad..7831b18a 100644 --- a/Tests/TestHelpers/VariableName+testNames.swift +++ b/Tests/TestHelpers/VariableName+testNames.swift @@ -63,41 +63,41 @@ import VHDLParsing /// Add common test variables. extension VariableName { - public static let arrangement1 = VariableName(rawValue: "Arrangement1")! + package static let arrangement1 = VariableName(rawValue: "Arrangement1")! - public static let beginExecution = VariableName(rawValue: "beginExecution")! + package static let beginExecution = VariableName(rawValue: "beginExecution")! - public static let clk = VariableName(rawValue: "clk")! + package static let clk = VariableName(rawValue: "clk")! - public static let externalPing = VariableName(rawValue: "externalPing")! + package static let externalPing = VariableName(rawValue: "externalPing")! - public static let externalPong = VariableName(rawValue: "externalPong")! + package static let externalPong = VariableName(rawValue: "externalPong")! - public static let ieee = VariableName(rawValue: "IEEE")! + package static let ieee = VariableName(rawValue: "IEEE")! - public static let initial = VariableName(rawValue: "Initial")! + package static let initial = VariableName(rawValue: "Initial")! - public static let `internal` = VariableName(rawValue: "Internal")! + package static let `internal` = VariableName(rawValue: "Internal")! - public static let mathReal = VariableName(rawValue: "math_real")! + package static let mathReal = VariableName(rawValue: "math_real")! - public static let onEntry = VariableName(rawValue: "OnEntry")! + package static let onEntry = VariableName(rawValue: "OnEntry")! - public static let onExit = VariableName(rawValue: "OnExit")! + package static let onExit = VariableName(rawValue: "OnExit")! - public static let ping = VariableName(rawValue: "ping")! + package static let ping = VariableName(rawValue: "ping")! - public static let pingMachine = VariableName(rawValue: "PingMachine")! + package static let pingMachine = VariableName(rawValue: "PingMachine")! - public static let pingMachineInst = VariableName(rawValue: "PingMachine_inst")! + package static let pingMachineInst = VariableName(rawValue: "PingMachine_inst")! - public static let pong = VariableName(rawValue: "pong")! + package static let pong = VariableName(rawValue: "pong")! - public static let sendPing = VariableName(rawValue: "SendPing")! + package static let sendPing = VariableName(rawValue: "SendPing")! - public static let stdLogic1164 = VariableName(rawValue: "std_logic_1164")! + package static let stdLogic1164 = VariableName(rawValue: "std_logic_1164")! - public static let waitForPong = VariableName(rawValue: "WaitForPong")! + package static let waitForPong = VariableName(rawValue: "WaitForPong")! }