Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate unit tests to swift-testing #175

Merged
merged 23 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d6d5e3c
Migrate LuckyNumberHandlerTests to swift-testing
fwcd Oct 19, 2024
d3bff37
Migrate SpamHandlerTests to swift-testing
fwcd Oct 19, 2024
52f5c55
Migrate TriggerReactionHandlerTests to swift-testing
fwcd Oct 19, 2024
d583a4f
Migrate ChessStateTests to swift-testing
fwcd Oct 19, 2024
95ab555
Migrate ChessPieceUtilsTests to swift-testing
fwcd Oct 19, 2024
9273c9d
Migrate ChessNotationParserTests to swift-testing
fwcd Oct 19, 2024
bcde03c
Migrate TicTacToeCommandTests to swift-testing
fwcd Oct 19, 2024
0e137b7
Migrate WordleBoardTests to swift-testing
fwcd Oct 19, 2024
ad63ecc
Migrate ExpressionParserTests to swift-testing
fwcd Oct 19, 2024
badfaca
Migrate QuadraticEquationTests to swift-testing
fwcd Oct 19, 2024
5ef0608
Migrate EchoCommandTests to swift-testing
fwcd Oct 19, 2024
fefc3d2
Migrate MorseCoderTests to swift-testing
fwcd Oct 19, 2024
55333e4
Migrate FindKeyCommandTests to swift-testing
fwcd Oct 19, 2024
cc3fc57
Migrate NoteLetterTests to swift-testing
fwcd Oct 19, 2024
9839f23
Migrate NoteTests to swift-testing
fwcd Oct 19, 2024
b9e832a
Migrate MinecraftIntegerTests to swift-testing
fwcd Oct 19, 2024
3de78eb
Migrate MinecraftVarIntTests to swift-testing
fwcd Oct 19, 2024
99803f2
Migrate UltimateGuitarTabParserTests to swift-testing
fwcd Oct 19, 2024
720542a
Migrate WolframAlphaParserDelegateTests to swift-testing
fwcd Oct 19, 2024
4645409
Migrate D2ScriptASTVisitorTests to swift-testing
fwcd Oct 19, 2024
c96b516
Migrate D2ScriptExecutorTests to swift-testing
fwcd Oct 19, 2024
51ce173
Migrate D2ScriptStorageTests to swift-testing
fwcd Oct 20, 2024
aef5e6c
Migrate D2ScriptParserTests to swift-testing
fwcd Oct 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ let package = Package(
name: "D2ScriptTests",
dependencies: [
.product(name: "Utils", package: "swift-utils"),
.target(name: "D2TestUtils"),
.target(name: "D2Script"),
]
),
Expand Down
2 changes: 1 addition & 1 deletion Sources/D2Commands/Game/Wordle/WordleBoard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public struct WordleBoard: RichValueConvertible, Sendable {
}
}

public enum Clue: UInt32, CaseIterable {
public enum Clue: UInt32, CaseIterable, Sendable, Hashable {
case unknown = 0
case nowhere
case somewhere
Expand Down
5 changes: 5 additions & 0 deletions Sources/D2TestUtils/Double+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public extension Double {
func isApproximatelyEqual(to other: Self, accuracy: Double = 0.0001) -> Bool {
abs(self - other) < accuracy
}
}
26 changes: 13 additions & 13 deletions Tests/D2CommandTests/Game/Chess/ChessStateTests.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import XCTest
import Testing
@testable import D2Commands

final class ChessStateTests: XCTestCase {
func testPossibleMoves() throws {
struct ChessStateTests {
@Test func possibleMoves() throws {
let state = ChessState(players: [GamePlayer(username: "Mr. White"), GamePlayer(username: "Mr. Black")])
let initialMoves = state.possibleMoves

XCTAssert(initialMoves.allSatisfy { $0.color == .white }, "Initial moves should all be white")
XCTAssert(initialMoves.allSatisfy { $0.isCapture == false }, "Initial moves should not contain captures (or unspecified 'isCapture' fields)")
XCTAssert(initialMoves.contains(ChessMove(
#expect(initialMoves.allSatisfy { $0.color == .white }, "Initial moves should all be white")
#expect(initialMoves.allSatisfy { $0.isCapture == false }, "Initial moves should not contain captures (or unspecified 'isCapture' fields)")
#expect(initialMoves.contains(ChessMove(
pieceType: .pawn,
color: .white,
originX: xOf(file: "e"),
Expand All @@ -18,19 +18,19 @@ final class ChessStateTests: XCTestCase {
destinationY: yOf(rank: 4),
isEnPassant: false
)), "Possible moves should contain pawn move e4e6")
assert(initialMoves, containsMove: "white Nb1c3")
expect(initialMoves, containsMove: "white Nb1c3")

let secondState = try state.childState(after: move("white e2e4", in: state.possibleMoves)!)
let secondMoves = secondState.possibleMoves

XCTAssert(secondMoves.allSatisfy { $0.color == .black }, "Second moves should all be black")
assert(secondMoves, containsMove: "black e7e6")
assert(secondMoves, containsMove: "black b7b5")
assert(secondMoves, containsMove: "black Ng8f6")
#expect(secondMoves.allSatisfy { $0.color == .black }, "Second moves should all be black")
expect(secondMoves, containsMove: "black e7e6")
expect(secondMoves, containsMove: "black b7b5")
expect(secondMoves, containsMove: "black Ng8f6")
}

private func assert(_ moves: Set<ChessMove>, containsMove moveDescription: String) {
XCTAssert(moves.contains { $0.description == moveDescription }, "Moves should contain '\(moveDescription)', but did not: \(moves)")
private func expect(_ moves: Set<ChessMove>, containsMove moveDescription: String, sourceLocation: SourceLocation = #_sourceLocation) {
#expect(moves.contains { $0.description == moveDescription }, "Moves should contain '\(moveDescription)', but did not: \(moves)", sourceLocation: sourceLocation)
}

private func move(_ description: String, in moves: Set<ChessMove>) -> ChessMove? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,60 @@
import XCTest
import Testing
@testable import D2Commands

final class ChessNotationParserTests: XCTestCase {
func testShortAlgebraicNotation() throws {
struct ChessNotationParserTests {
@Test func shortAlgebraicNotation() {
let parser = ShortAlgebraicNotationParser()

let bishopMove = parser.parse("Lc4")
XCTAssertEqual(bishopMove?.pieceType, ChessPieceType.bishop)
XCTAssertEqual(bishopMove?.destinationX, 2)
XCTAssertEqual(bishopMove?.destinationY, 4)
XCTAssertEqual(bishopMove?.isCapture, false)
#expect(bishopMove?.pieceType == ChessPieceType.bishop)
#expect(bishopMove?.destinationX == 2)
#expect(bishopMove?.destinationY == 4)
#expect(bishopMove?.isCapture == false)

let captureMove = parser.parse("Bxb5")
XCTAssertEqual(captureMove?.pieceType, ChessPieceType.bishop)
XCTAssertEqual(captureMove?.isCapture, true)
XCTAssertEqual(captureMove?.destinationX, 1)
XCTAssertEqual(captureMove?.destinationY, 3)
#expect(captureMove?.pieceType == ChessPieceType.bishop)
#expect(captureMove?.isCapture == true)
#expect(captureMove?.destinationX == 1)
#expect(captureMove?.destinationY == 3)

let pawnMove = parser.parse("g3")
XCTAssertEqual(pawnMove?.pieceType, ChessPieceType.pawn)
XCTAssertEqual(pawnMove?.isCapture, false)
XCTAssertEqual(pawnMove?.isEnPassant, false)
XCTAssertEqual(pawnMove?.destinationX, 6)
XCTAssertEqual(pawnMove?.destinationY, 5)
#expect(pawnMove?.pieceType == ChessPieceType.pawn)
#expect(pawnMove?.isCapture == false)
#expect(pawnMove?.isEnPassant == false)
#expect(pawnMove?.destinationX == 6)
#expect(pawnMove?.destinationY == 5)

let enPassantMove = parser.parse("fxg6 e. p.")
XCTAssertEqual(enPassantMove?.pieceType, ChessPieceType.pawn)
XCTAssertEqual(enPassantMove?.isCapture, true)
XCTAssertEqual(enPassantMove?.isEnPassant, true)
XCTAssertEqual(enPassantMove?.originX, 5)
XCTAssertEqual(enPassantMove?.destinationX, 6)
XCTAssertEqual(enPassantMove?.destinationY, 2)
#expect(enPassantMove?.pieceType == ChessPieceType.pawn)
#expect(enPassantMove?.isCapture == true)
#expect(enPassantMove?.isEnPassant == true)
#expect(enPassantMove?.originX == 5)
#expect(enPassantMove?.destinationX == 6)
#expect(enPassantMove?.destinationY == 2)

let knightMove1 = parser.parse("Sac7")
XCTAssertEqual(knightMove1?.pieceType, ChessPieceType.knight)
XCTAssertEqual(knightMove1?.originX, 0)
XCTAssertEqual(knightMove1?.destinationX, 2)
XCTAssertEqual(knightMove1?.destinationY, 1)
#expect(knightMove1?.pieceType == ChessPieceType.knight)
#expect(knightMove1?.originX == 0)
#expect(knightMove1?.destinationX == 2)
#expect(knightMove1?.destinationY == 1)

let knightMove2 = parser.parse("Ne1xc4")
XCTAssertEqual(knightMove2?.pieceType, ChessPieceType.knight)
XCTAssertEqual(knightMove2?.originX, 4)
XCTAssertEqual(knightMove2?.originY, 7)
XCTAssertEqual(knightMove2?.isCapture, true)
XCTAssertEqual(knightMove2?.destinationX, 2)
XCTAssertEqual(knightMove2?.destinationY, 4)
#expect(knightMove2?.pieceType == ChessPieceType.knight)
#expect(knightMove2?.originX == 4)
#expect(knightMove2?.originY == 7)
#expect(knightMove2?.isCapture == true)
#expect(knightMove2?.destinationX == 2)
#expect(knightMove2?.destinationY == 4)

let rookMove1 = parser.parse("R1c7")
XCTAssertEqual(rookMove1?.pieceType, ChessPieceType.rook)
XCTAssertEqual(rookMove1?.originY, 7)
XCTAssertEqual(rookMove1?.destinationX, 2)
XCTAssertEqual(rookMove1?.destinationY, 1)
#expect(rookMove1?.pieceType == ChessPieceType.rook)
#expect(rookMove1?.originY == 7)
#expect(rookMove1?.destinationX == 2)
#expect(rookMove1?.destinationY == 1)

let rookMove2 = parser.parse("Th2")
XCTAssertEqual(rookMove2?.pieceType, ChessPieceType.rook)
XCTAssertEqual(rookMove2?.destinationX, 7)
XCTAssertEqual(rookMove2?.destinationY, 6)
#expect(rookMove2?.pieceType == ChessPieceType.rook)
#expect(rookMove2?.destinationX == 7)
#expect(rookMove2?.destinationY == 6)
}
}
30 changes: 15 additions & 15 deletions Tests/D2CommandTests/Game/Chess/Pieces/ChessPieceUtilsTests.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import XCTest
import Testing
import Utils
@testable import D2Commands

final class ChessPieceUtilsTests: XCTestCase {
func testNeighborFields() throws {
XCTAssertEqual(Set(neighborFields()), Set([
struct ChessPieceUtilsTests {
@Test func neighborFieldsAsExpected() {
#expect(Set(neighborFields()) == Set([
Vec2(x: -1, y: -1),
Vec2(x: 0, y: -1),
Vec2(x: 1, y: -1),
Expand All @@ -16,18 +16,18 @@ final class ChessPieceUtilsTests: XCTestCase {
]))
}

func testPieceLetters() throws {
assert("Q", matchesPiece: .queen)
assert("D", matchesPiece: .queen)
assert("R", matchesPiece: .rook)
assert("T", matchesPiece: .rook)
assert("L", matchesPiece: .bishop)
assert("B", matchesPiece: .bishop)
assert("N", matchesPiece: .knight)
assert("S", matchesPiece: .knight)
@Test func pieceLetters() {
expect("Q", matchesPiece: .queen)
expect("D", matchesPiece: .queen)
expect("R", matchesPiece: .rook)
expect("T", matchesPiece: .rook)
expect("L", matchesPiece: .bishop)
expect("B", matchesPiece: .bishop)
expect("N", matchesPiece: .knight)
expect("S", matchesPiece: .knight)
}

private func assert(_ letter: Character, matchesPiece pieceType: ChessPieceType) {
XCTAssertEqual(pieceOf(letter: letter)?.pieceType, pieceType)
private func expect(_ letter: Character, matchesPiece pieceType: ChessPieceType, sourceLocation: SourceLocation = #_sourceLocation) {
#expect(pieceOf(letter: letter)?.pieceType == pieceType, sourceLocation: sourceLocation)
}
}
40 changes: 15 additions & 25 deletions Tests/D2CommandTests/Game/TicTacToe/TicTacToeCommandTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import XCTest
import Testing
import D2MessageIO
import D2TestUtils
@testable import D2Commands
Expand All @@ -9,53 +9,43 @@ fileprivate let e = ":white_large_square:"
fileprivate let nameX = "Mr. X"
fileprivate let nameO = "Mr. O"

final class TicTacToeCommandTests: XCTestCase {
struct TicTacToeCommandTests {
private let playerX = GamePlayer(username: nameX)
private let playerO = GamePlayer(username: nameO)

func testXWin() async throws {
@Test func xWin() async {
let command = await GameCommand<TicTacToeGame>()
let output = await TestOutput()
var lastContent: String?
var lastEmbedDescription: String?

let channel = dummyId
await command.startMatch(between: [playerX, playerO], on: channel, output: output)

await command.perform("move", withArgs: "top left", on: channel, output: output, author: playerO)
lastEmbedDescription = await output.lastEmbedDescription
XCTAssertEqual(lastEmbedDescription, ":warning: It is not your turn, `\(nameO)`")
#expect(await output.lastEmbedDescription == ":warning: It is not your turn, `\(nameO)`")

await command.perform("move", withArgs: "top left", on: channel, output: output, author: playerX)
lastContent = await output.lastContent
XCTAssertEqual(lastContent, "\(x)\(e)\(e)\n\(e)\(e)\(e)\n\(e)\(e)\(e)")
#expect(await output.lastContent == "\(x)\(e)\(e)\n\(e)\(e)\(e)\n\(e)\(e)\(e)")

await command.perform("move", withArgs: "top right", on: channel, output: output, author: playerX)
lastEmbedDescription = await output.lastEmbedDescription
XCTAssertEqual(lastEmbedDescription, ":warning: It is not your turn, `\(nameX)`")
#expect(await output.lastEmbedDescription == ":warning: It is not your turn, `\(nameX)`")

await command.perform("move", withArgs: "center center", on: channel, output: output, author: playerO)
lastContent = await output.lastContent
XCTAssertEqual(lastContent, "\(x)\(e)\(e)\n\(e)\(o)\(e)\n\(e)\(e)\(e)")
#expect(await output.lastContent == "\(x)\(e)\(e)\n\(e)\(o)\(e)\n\(e)\(e)\(e)")

await command.perform("move", withArgs: "left bottom", on: channel, output: output, author: playerX)
lastContent = await output.lastContent
XCTAssertEqual(lastContent, "\(x)\(e)\(e)\n\(e)\(o)\(e)\n\(x)\(e)\(e)")
#expect(await output.lastContent == "\(x)\(e)\(e)\n\(e)\(o)\(e)\n\(x)\(e)\(e)")

await command.perform("move", withArgs: "0 2", on: channel, output: output, author: playerO)
lastContent = await output.lastContent
XCTAssertEqual(lastContent, "\(x)\(e)\(o)\n\(e)\(o)\(e)\n\(x)\(e)\(e)")
#expect(await output.lastContent == "\(x)\(e)\(o)\n\(e)\(o)\(e)\n\(x)\(e)\(e)")

await command.perform("move", withArgs: "1 0", on: channel, output: output, author: playerX)
lastContent = await output.lastContent
XCTAssertEqual(lastContent, "\(x)\(e)\(o)\n\(x)\(o)\(e)\n\(x)\(e)\(e)")
#expect(await output.lastContent == "\(x)\(e)\(o)\n\(x)\(o)\(e)\n\(x)\(e)\(e)")

let result = await output.last?.embeds.first
XCTAssertEqual(result?.title, ":crown: Winner")
XCTAssertEqual(result?.description, "\(x) aka. `\(nameX)` won the game!")
#expect(result?.title == ":crown: Winner")
#expect(result?.description == "\(x) aka. `\(nameX)` won the game!")
}

func testDraw() async throws {
@Test func draw() async {
let command = await GameCommand<TicTacToeGame>()
let output = await TestOutput()
let channel = dummyId
Expand All @@ -72,7 +62,7 @@ final class TicTacToeCommandTests: XCTestCase {
await command.perform("move", withArgs: "0 2", on: channel, output: output, author: playerX)

let result = await output.last?.embeds.first
XCTAssertEqual(result?.title, ":crown: Game Over")
XCTAssertEqual(result?.description, "The game resulted in a draw!")
#expect(result?.title == ":crown: Game Over")
#expect(result?.description == "The game resulted in a draw!")
}
}
22 changes: 11 additions & 11 deletions Tests/D2CommandTests/Game/Wordle/WordleBoardTests.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import XCTest
import Testing
@testable import D2Commands

final class WordleBoardTests: XCTestCase {
func testClues() throws {
XCTAssertEqual(WordleBoard.Clues(fromArray: [.here]).count, 1)
XCTAssertEqual(WordleBoard.Clues(fromArray: [.nowhere, .somewhere]).count, 2)

testCodingRoundtrip(for: [])
testCodingRoundtrip(for: [.somewhere, .unknown])
testCodingRoundtrip(for: [.nowhere, .somewhere, .somewhere, .here, .nowhere])
struct WordleBoardTests {
@Test func clues() {
#expect(WordleBoard.Clues(fromArray: [.here]).count == 1)
#expect(WordleBoard.Clues(fromArray: [.nowhere, .somewhere]).count == 2)
}

private func testCodingRoundtrip(for clues: [WordleBoard.Clue]) {
XCTAssertEqual(Array(WordleBoard.Clues(fromArray: clues)), clues)
@Test(arguments: [
[WordleBoard.Clue](),
[.somewhere, .unknown],
[.nowhere, .somewhere, .somewhere, .here, .nowhere],
]) func codingRoundtrip(clues: [WordleBoard.Clue]) {
#expect(Array(WordleBoard.Clues(fromArray: clues)) == clues)
}
}
47 changes: 23 additions & 24 deletions Tests/D2CommandTests/Math/Parse/ExpressionParserTests.swift
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
import XCTest
import Testing
import D2TestUtils
@testable import D2Commands

fileprivate let eps = 0.00001

final class ExpressionParserTests: XCTestCase {
func testRPNExpressionParser() throws {
struct ExpressionParserTests {
@Test func rpnExpressionParser() throws {
let parser = RPNExpressionParser()

let rawProduct = "3 4 *"
guard let product = try parser.parse(rawProduct) as? ProductNode else { XCTFail("\(rawProduct) should be a product node"); return }
XCTAssertEqual(try product.lhs.evaluate(), 3.0, accuracy: eps)
XCTAssertEqual(try product.rhs.evaluate(), 4.0, accuracy: eps)
guard let product = try parser.parse(rawProduct) as? ProductNode else { Issue.record("\(rawProduct) should be a product node"); return }
#expect(try product.lhs.evaluate().isApproximatelyEqual(to: 3.0))
#expect(try product.rhs.evaluate().isApproximatelyEqual(to: 4.0))

let rawQuotient = "2.1 -51.09 pi + 1 - /"
guard let quotient = try parser.parse(rawQuotient) as? QuotientNode else { XCTFail("\(rawQuotient) should be a quotient node"); return }
guard let quotientLeft = quotient.lhs as? ConstantNode else { XCTFail("Left-hand side of quotient should be a constant"); return }
guard let quotientRight = quotient.rhs as? DifferenceNode else { XCTFail("Right-hand side of quotient should be a difference"); return }
guard let differenceLeft = quotientRight.lhs as? SumNode else { XCTFail("Left-hand side of difference should be a sum"); return }
guard let differenceRight = quotientRight.rhs as? ConstantNode else { XCTFail("Right-hand side of quotient should be a constant"); return }
guard let sumLeft = differenceLeft.lhs as? ConstantNode else { XCTFail("Left-hand side of sum should be a constant"); return }
guard let sumRight = differenceLeft.rhs as? ConstantNode else { XCTFail("Right-hand side of sum should be a constant"); return }
XCTAssertEqual(try quotientLeft.evaluate(), 2.1, accuracy: eps)
XCTAssertEqual(try sumLeft.evaluate(), -51.09, accuracy: eps)
XCTAssertEqual(try sumRight.evaluate(), Double.pi, accuracy: eps)
XCTAssertEqual(try differenceRight.evaluate(), 1.0, accuracy: eps)
guard let quotient = try parser.parse(rawQuotient) as? QuotientNode else { Issue.record("\(rawQuotient) should be a quotient node"); return }
guard let quotientLeft = quotient.lhs as? ConstantNode else { Issue.record("Left-hand side of quotient should be a constant"); return }
guard let quotientRight = quotient.rhs as? DifferenceNode else { Issue.record("Right-hand side of quotient should be a difference"); return }
guard let differenceLeft = quotientRight.lhs as? SumNode else { Issue.record("Left-hand side of difference should be a sum"); return }
guard let differenceRight = quotientRight.rhs as? ConstantNode else { Issue.record("Right-hand side of quotient should be a constant"); return }
guard let sumLeft = differenceLeft.lhs as? ConstantNode else { Issue.record("Left-hand side of sum should be a constant"); return }
guard let sumRight = differenceLeft.rhs as? ConstantNode else { Issue.record("Right-hand side of sum should be a constant"); return }
#expect(try quotientLeft.evaluate().isApproximatelyEqual(to: 2.1))
#expect(try sumLeft.evaluate().isApproximatelyEqual(to: -51.09))
#expect(try sumRight.evaluate().isApproximatelyEqual(to: Double.pi))
#expect(try differenceRight.evaluate().isApproximatelyEqual(to: 1.0))
}

func testInfixExpressionParser() throws {
@Test func infixExpressionParser() throws {
let parser = InfixExpressionParser()

XCTAssertTrue(try parser.parse("3 * 4").isEqual(to: ProductNode(lhs: ConstantNode(value: 3), rhs: ConstantNode(value: 4))))
XCTAssertTrue(try parser.parse("3 + 4").isEqual(to: SumNode(lhs: ConstantNode(value: 3), rhs: ConstantNode(value: 4))))
XCTAssertTrue(try parser.parse("4 - 9 * 8").isEqual(to: DifferenceNode(lhs: ConstantNode(value: 4), rhs: ProductNode(lhs: ConstantNode(value: 9), rhs: ConstantNode(value: 8)))))
XCTAssertTrue(try parser.parse("(4 - 9) * 8").isEqual(to: ProductNode(lhs: DifferenceNode(lhs: ConstantNode(value: 4), rhs: ConstantNode(value: 9)), rhs: ConstantNode(value: 8))))
#expect(try parser.parse("3 * 4").isEqual(to: ProductNode(lhs: ConstantNode(value: 3), rhs: ConstantNode(value: 4))))
#expect(try parser.parse("3 + 4").isEqual(to: SumNode(lhs: ConstantNode(value: 3), rhs: ConstantNode(value: 4))))
#expect(try parser.parse("4 - 9 * 8").isEqual(to: DifferenceNode(lhs: ConstantNode(value: 4), rhs: ProductNode(lhs: ConstantNode(value: 9), rhs: ConstantNode(value: 8)))))
#expect(try parser.parse("(4 - 9) * 8").isEqual(to: ProductNode(lhs: DifferenceNode(lhs: ConstantNode(value: 4), rhs: ConstantNode(value: 9)), rhs: ConstantNode(value: 8))))
}
}
Loading
Loading