Skip to content

Commit 144b3cd

Browse files
committed
Migrate to swift testing
Except in engine tests.
1 parent b677ef5 commit 144b3cd

10 files changed

Lines changed: 212 additions & 249 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
### Improvements
44
* Commands and responses are now logged using their own synchronous queue to prevent unreadable output when commands and responses were sent simultaneously.
5-
* `setLoggingEnabled(loggingEnabled:)` has been renamed `set(loggingEnabled:)`
5+
* `setLoggingEnabled(loggingEnabled:)` has been renamed `set(loggingEnabled:) async`
66
* The original function remains but is deprecated.
7+
* Change default `coreCount` in `Engine.start()` to **one less** than the core count (minimum of 1).
8+
* This allows a free core for other processing tasks in a multicore environment.
9+
* Can still be changed with the `coreCount` parameter if desired.
710

811
# ChessKitEngine 0.6.0
912
Released Friday, May 30, 2025.

Sources/ChessKitEngine/Engine.swift

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,12 @@ public final class Engine: Sendable {
8989
/// before you can ask the engine to perform any work.
9090
///
9191
/// - parameter coreCount: The number of processor cores to use for engine
92-
/// calculation. The default value is `nil` which uses the number of
93-
/// cores available on the device.
92+
/// calculation. The default value is `nil` which uses one less than
93+
/// the number of cores available on the device.
9494
/// - parameter multipv: The number of lines the engine should return,
9595
/// sent via the `"MultiPV"` UCI option.
9696
///
97-
public func start(
98-
coreCount: Int? = nil,
99-
multipv: Int = 1
100-
) async {
97+
public func start(coreCount: Int? = nil, multipv: Int = 1) async {
10198
// Setup async stream response if not already set.
10299
await engineConfigurationActor.setAsyncStream()
103100

@@ -155,18 +152,18 @@ public final class Engine: Sendable {
155152
///
156153
@available(*, deprecated, renamed: "set(loggingEnabled:)")
157154
public func setLoggingEnabled(_ loggingEnabled: Bool) {
158-
set(loggingEnabled: loggingEnabled)
155+
Task {
156+
await set(loggingEnabled: loggingEnabled)
157+
}
159158
}
160159

161160
/// Enable printing logs to console.
162161
///
163162
/// - parameter loggingEnabled: If set to `true`, engine commands and responses
164163
/// will be logged to the console. The default value is `false`.
165164
///
166-
public func set(loggingEnabled: Bool) {
167-
Task {
168-
await engineConfigurationActor.set(loggingEnabled: loggingEnabled)
169-
}
165+
public func set(loggingEnabled: Bool) async {
166+
await engineConfigurationActor.set(loggingEnabled: loggingEnabled)
170167
}
171168

172169
// MARK: Private functions
@@ -201,12 +198,10 @@ public final class Engine: Sendable {
201198
if await !self.isRunning {
202199
if parsed == .readyok {
203200
await self.performInitialSetup(
204-
coreCount: coreCount ?? ProcessInfo.processInfo.processorCount,
201+
coreCount: coreCount ?? min(1, ProcessInfo.processInfo.processorCount - 1),
205202
multipv: multipv
206203
)
207-
} else if let next = EngineCommand.nextSetupLoopCommand(
208-
given: parsed
209-
) {
204+
} else if let next = EngineCommand.nextSetupLoopCommand(given: parsed) {
210205
await self.send(command: next)
211206
}
212207
}
@@ -263,7 +258,9 @@ fileprivate actor EngineConfiguration: Sendable {
263258
init(loggingEnabled: Bool = false) {
264259
self.loggingEnabled = loggingEnabled
265260

266-
Task { await setAsyncStream() }
261+
Task {
262+
await setAsyncStream()
263+
}
267264
}
268265

269266
func set(loggingEnabled: Bool) async {

Sources/ChessKitEngine/EngineResponse/EngineResponseParser.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
// ChessKitEngine
44
//
55

6-
class EngineResponseParser {
7-
8-
private init() {}
6+
enum EngineResponseParser {
97

108
static func parse(response: String) -> EngineResponse? {
119
let tokens = response.split { $0.isWhitespace || $0.isNewline }.map(String.init)

Tests/ChessKitEngineTests/EngineCommandPositionTests.swift

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,47 @@
33
// ChessKitEngineTests
44
//
55

6-
import XCTest
76
@testable import ChessKitEngine
7+
import Testing
88

9-
class EngineCommandPositionTests: XCTestCase {
9+
struct EngineCommandPositionTests {
1010

11-
func testPositionStringRawValue() {
11+
@Test func positionStringRawValue() {
1212
let p = EngineCommand.PositionString.startpos
13-
XCTAssertEqual(p.rawValue, "startpos")
13+
#expect(p.rawValue == "startpos")
1414

1515
let fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
1616
let f = EngineCommand.PositionString.fen(fen)
17-
XCTAssertEqual(f.rawValue, "fen \(fen)")
17+
#expect(f.rawValue == "fen \(fen)")
1818
}
1919

20-
func testPositionStringRawValueInit() {
21-
XCTAssertEqual(
22-
EngineCommand.PositionString(rawValue: "startpos"),
23-
.startpos
24-
)
20+
@Test func positionStringRawValueInit() {
21+
#expect(EngineCommand.PositionString(rawValue: "startpos") == .startpos)
2522

2623
let fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
27-
XCTAssertEqual(
28-
EngineCommand.PositionString(rawValue: "fen \(fen)"),
29-
.fen(fen)
30-
)
24+
#expect(EngineCommand.PositionString(rawValue: "fen \(fen)") == .fen(fen))
3125
}
3226

33-
func testInvalidFENPositionStrings() {
27+
@Test func invalidFENPositionStrings() {
3428
let fen1 = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
35-
XCTAssertNil(EngineCommand.PositionString(rawValue: "fen \(fen1)"))
29+
#expect(EngineCommand.PositionString(rawValue: "fen \(fen1)") == nil)
3630

3731
let fen2 = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w"
38-
XCTAssertNil(EngineCommand.PositionString(rawValue: "fen \(fen2)"))
32+
#expect(EngineCommand.PositionString(rawValue: "fen \(fen2)") == nil)
3933

4034
let fen3 = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq"
41-
XCTAssertNil(EngineCommand.PositionString(rawValue: "fen \(fen3)"))
35+
#expect(EngineCommand.PositionString(rawValue: "fen \(fen3)") == nil)
4236

4337
let fen4 = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -"
44-
XCTAssertNil(EngineCommand.PositionString(rawValue: "fen \(fen4)"))
38+
#expect(EngineCommand.PositionString(rawValue: "fen \(fen4)") == nil)
4539

4640
let fen5 = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0"
47-
XCTAssertNil(EngineCommand.PositionString(rawValue: "fen \(fen5)"))
41+
#expect(EngineCommand.PositionString(rawValue: "fen \(fen5)") == nil)
4842
}
4943

50-
func testInvalidPositionString() {
51-
XCTAssertNil(EngineCommand.PositionString(rawValue: "invalid"))
52-
XCTAssertNil(EngineCommand.PositionString(rawValue: ""))
44+
@Test func invalidPositionString() {
45+
#expect(EngineCommand.PositionString(rawValue: "invalid") == nil)
46+
#expect(EngineCommand.PositionString(rawValue: "") == nil)
5347
}
5448

5549
}

Tests/ChessKitEngineTests/EngineCommandTests.swift

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,62 @@
33
// ChessKitEngineTests
44
//
55

6-
import XCTest
76
@testable import ChessKitEngine
7+
import Testing
88

9-
class EngineCommandTests: XCTestCase {
9+
struct EngineCommandTests {
1010

11-
func testDebugCommand() {
11+
@Test func debugCommand() {
1212
let debugOn = EngineCommand.debug(on: true)
13-
XCTAssertEqual(debugOn.rawValue, "debug on")
13+
#expect(debugOn.rawValue == "debug on")
1414

1515
let debugOff = EngineCommand.debug(on: false)
16-
XCTAssertEqual(debugOff.rawValue, "debug off")
16+
#expect(debugOff.rawValue == "debug off")
1717
}
1818

19-
func testUciCommand() {
20-
XCTAssertEqual(EngineCommand.uci.rawValue, "uci")
19+
@Test func uciCommand() {
20+
#expect(EngineCommand.uci.rawValue == "uci")
2121
}
2222

23-
func testIsReadyCommand() {
24-
XCTAssertEqual(EngineCommand.isready.rawValue, "isready")
23+
@Test func isReadyCommand() {
24+
#expect(EngineCommand.isready.rawValue == "isready")
2525
}
2626

27-
func testSetOptionCommand() {
27+
@Test func setOptionCommand() {
2828
let setOption1 = EngineCommand.setoption(id: "name", value: "val")
29-
XCTAssertEqual(
30-
setOption1.rawValue,
31-
"setoption name name value val"
32-
)
29+
#expect(setOption1.rawValue == "setoption name name value val")
3330

3431
let setOption2 = EngineCommand.setoption(id: "name")
35-
XCTAssertEqual(
36-
setOption2.rawValue,
37-
"setoption name name"
38-
)
32+
#expect(setOption2.rawValue == "setoption name name")
3933
}
4034

41-
func testUciNewGameCommand() {
42-
XCTAssertEqual(EngineCommand.ucinewgame.rawValue, "ucinewgame")
35+
@Test func uciNewGameCommand() {
36+
#expect(EngineCommand.ucinewgame.rawValue == "ucinewgame")
4337
}
4438

45-
func testPositionCommand() {
46-
XCTAssertEqual(
39+
@Test func positionCommand() {
40+
#expect(
4741
EngineCommand.position(
4842
.startpos,
4943
moves: ["e2e4", "c7c6"]
50-
).rawValue,
51-
"position startpos moves e2e4 c7c6"
44+
).rawValue == "position startpos moves e2e4 c7c6"
5245
)
5346

54-
XCTAssertEqual(
47+
#expect(
5548
EngineCommand.position(
5649
.fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
57-
).rawValue,
58-
"position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
50+
).rawValue == "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
5951
)
6052

61-
XCTAssertEqual(
53+
#expect(
6254
EngineCommand.position(
6355
.fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"),
6456
moves: ["e2e4", "c7c6"]
65-
).rawValue,
66-
"position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 moves e2e4 c7c6"
57+
).rawValue == "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 moves e2e4 c7c6"
6758
)
6859
}
6960

70-
func testGoCommand() {
61+
@Test func goCommand() {
7162
let go = EngineCommand.go(
7263
searchmoves: ["e2e4", "c7c6"],
7364
ponder: true,
@@ -83,22 +74,22 @@ class EngineCommandTests: XCTestCase {
8374
infinite: false
8475
)
8576

86-
XCTAssertEqual(go.rawValue, "go searchmoves e2e4 c7c6 ponder wtime 5 btime 5 winc 2 binc 2 movestogo 10 depth 15 nodes 100 mate 2 movetime 3")
77+
#expect(go.rawValue == "go searchmoves e2e4 c7c6 ponder wtime 5 btime 5 winc 2 binc 2 movestogo 10 depth 15 nodes 100 mate 2 movetime 3")
8778

8879
let goInfinite = EngineCommand.go(infinite: true)
89-
XCTAssertEqual(goInfinite.rawValue, "go infinite")
80+
#expect(goInfinite.rawValue == "go infinite")
9081
}
9182

92-
func testStopCommand() {
93-
XCTAssertEqual(EngineCommand.stop.rawValue, "stop")
83+
@Test func stopCommand() {
84+
#expect(EngineCommand.stop.rawValue == "stop")
9485
}
9586

96-
func testPonderhitCommand() {
97-
XCTAssertEqual(EngineCommand.ponderhit.rawValue, "ponderhit")
87+
@Test func ponderhitCommand() {
88+
#expect(EngineCommand.ponderhit.rawValue == "ponderhit")
9889
}
9990

100-
func testQuitCommand() {
101-
XCTAssertEqual(EngineCommand.quit.rawValue, "quit")
91+
@Test func quitCommand() {
92+
#expect(EngineCommand.quit.rawValue == "quit")
10293
}
10394

10495
}

Tests/ChessKitEngineTests/EngineResponseInfoTests.swift

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,44 @@
33
// ChessKitEngineTests
44
//
55

6-
import XCTest
76
@testable import ChessKitEngine
7+
import Testing
88

9-
class EngineResponseInfoTests: XCTestCase {
9+
struct EngineResponseInfoTests {
1010

11-
func testStringConversion() {
12-
let input = EngineResponse.Info(
11+
@Test func subscripts() {
12+
#expect(sampleInput["depth"] as? Int == sampleInput.depth)
13+
#expect(sampleInput["seldepth"] as? Int == sampleInput.seldepth)
14+
#expect(sampleInput["time"] as? Int == sampleInput.time)
15+
#expect(sampleInput["nodes"] as? Int == sampleInput.nodes)
16+
#expect(sampleInput["pv"] as? [String] == sampleInput.pv)
17+
#expect(sampleInput["multipv"] as? Int == sampleInput.multipv)
18+
#expect(sampleInput["score"] as? EngineResponse.Info.Score == sampleInput.score)
19+
#expect(sampleInput["currmove"] as? String == sampleInput.currmove)
20+
#expect(sampleInput["currmovenumber"] as? Int == sampleInput.currmovenumber)
21+
#expect(sampleInput["hashfull"] as? Double == sampleInput.hashfull)
22+
#expect(sampleInput["nps"] as? Int == sampleInput.nps)
23+
#expect(sampleInput["tbhits"] as? Int == sampleInput.tbhits)
24+
#expect(sampleInput["sbhits"] as? Int == sampleInput.sbhits)
25+
#expect(sampleInput["cpuload"] as? Int == sampleInput.cpuload)
26+
#expect(sampleInput["string"] as? String == sampleInput.string)
27+
#expect(sampleInput["refutation"] as? [String] == sampleInput.refutation)
28+
#expect(sampleInput["currline"] as? EngineResponse.Info.CurrLine == sampleInput.currline)
29+
30+
}
31+
32+
@Test func stringConversion() {
33+
let output =
34+
" <depth> 1 <seldepth> 0 <time> 1 <nodes> 10 <pv> e2e4 e7e5 g1f3 <multipv> 2 <score> <cp> 8.37 <mate> -4 <upperbound> <currmove> e2e4 <currmovenumber> 3 <hashfull> 4.56 <nps> 8 <tbhits> 7 <sbhits> 8 <cpuload> 9 <string> This is a test string with real tokens inserted such as pv and nodes and score lowerbound. <refutation> c7c5 d2d4 <currline> 4 d2d4 g8f6 c2c4 e7e6"
35+
36+
#expect(String(describing: sampleInput) == output)
37+
}
38+
39+
}
40+
41+
private extension EngineResponseInfoTests {
42+
var sampleInput: EngineResponse.Info {
43+
EngineResponse.Info(
1344
depth: 1,
1445
seldepth: 0,
1546
time: 1,
@@ -35,11 +66,5 @@ class EngineResponseInfoTests: XCTestCase {
3566
moves: ["d2d4", "g8f6", "c2c4", "e7e6"]
3667
)
3768
)
38-
39-
let output =
40-
" <depth> 1 <seldepth> 0 <time> 1 <nodes> 10 <pv> e2e4 e7e5 g1f3 <multipv> 2 <score> <cp> 8.37 <mate> -4 <upperbound> <currmove> e2e4 <currmovenumber> 3 <hashfull> 4.56 <nps> 8 <tbhits> 7 <sbhits> 8 <cpuload> 9 <string> This is a test string with real tokens inserted such as pv and nodes and score lowerbound. <refutation> c7c5 d2d4 <currline> 4 d2d4 g8f6 c2c4 e7e6"
41-
42-
XCTAssertEqual(String(describing: input), output)
4369
}
44-
4570
}

0 commit comments

Comments
 (0)