XDAcceptTests.swift (3855B)
1 import Foundation 2 import Testing 3 4 @testable import Crossmate 5 6 @Suite("XD Accept metadata") 7 @MainActor 8 struct XDAcceptTests { 9 @Test("Missing CmVer defaults to one") 10 func missingCmVerDefaultsToOne() throws { 11 let xd = try XD.parse(""" 12 Title: Versionless 13 14 15 A 16 17 18 A1. Letter ~ A 19 D1. Letter ~ A 20 """) 21 22 #expect(xd.cmVersion == 1) 23 } 24 25 @Test("Explicit positive CmVer is parsed") 26 func explicitCmVerIsParsed() throws { 27 let xd = try XD.parse(""" 28 Title: Versioned 29 CmVer: 3 30 31 32 A 33 34 35 A1. Letter ~ A 36 D1. Letter ~ A 37 """) 38 39 #expect(xd.cmVersion == 3) 40 } 41 42 @Test("Clue metadata is parsed generically") 43 func clueMetadataParsesGenerically() throws { 44 let source = """ 45 Title: Metadata Test 46 47 48 ABC 49 50 51 A1. Gardener's concerns. ~ ABC 52 A1 ^Refs: A2 D4 53 A1 ^Note: first value 54 A1 ^Note: second value 55 """ 56 57 let xd = try XD.parse(source) 58 let clue = try #require(xd.acrossClues.first) 59 60 #expect(clue.text == "Gardener's concerns.") 61 #expect(clue.answer == "ABC") 62 #expect(clue.metadata["Refs"] == ["A2 D4"]) 63 #expect(clue.metadata["Note"] == ["first value", "second value"]) 64 } 65 66 @Test("Accept metadata parses escaped tokens onto matching rebus cells") 67 func acceptMetadataParsesEscapedTokens() throws { 68 let source = """ 69 Title: Accept Test 70 Rebus: 1=PHI 71 72 73 1A 74 75 76 A1. Greek letter represented in the puzzle. ~ PHI 77 A1 ^Accept: IO OI I/O NEW\\ YORK A\\|B BACK\\\\SLASH 78 """ 79 80 let puzzle = Puzzle(xd: try XD.parse(source)) 81 let acceptClue = try #require(try XD.parse(source).acrossClues.first) 82 let cell = puzzle.cells[0][0] 83 84 #expect(acceptClue.metadata["Accept"] == ["IO OI I/O NEW\\ YORK A\\|B BACK\\\\SLASH"]) 85 #expect(acceptClue.acceptedAnswers == ["IO", "OI", "I/O", "NEW YORK", "A|B", "BACK\\SLASH"]) 86 #expect(cell.accepts("PHI")) 87 #expect(cell.accepts("io")) 88 #expect(cell.accepts("NEW YORK")) 89 #expect(cell.accepts("A|B")) 90 #expect(cell.accepts("BACK\\SLASH")) 91 #expect(!cell.accepts("NYC")) 92 } 93 94 @Test("Accepted answers are case-insensitive and NFC-normalized") 95 func acceptedAnswersNormalizeForCompletion() throws { 96 let source = """ 97 Title: Normalize Test 98 Rebus: 1=PHI 99 100 101 1A 102 103 104 A1. Greek letter represented in the puzzle. ~ PHI 105 A1 ^Accept: CAF\\É 106 """ 107 108 let game = Game(puzzle: Puzzle(xd: try XD.parse(source))) 109 game.setLetter("cafe\u{301}", atRow: 0, atCol: 0, pencil: false) 110 111 #expect(game.completionState == .solved) 112 } 113 114 @Test("Whole-clue accepted answers project onto changed cells") 115 func acceptedClueAnswersProjectOntoCells() throws { 116 let source = """ 117 Title: Projection Test 118 Rebus: 1=Φ 119 120 121 P1NG 122 123 124 A1. Paddle sport starter. ~ PΦNG 125 A1 ^Accept: PPHING PI/ONG 126 """ 127 128 let puzzle = Puzzle(xd: try XD.parse(source)) 129 let cell = puzzle.cells[0][1] 130 131 #expect(cell.solution == "Φ") 132 #expect(cell.accepts("PHI")) 133 #expect(cell.accepts("I/O")) 134 #expect(!puzzle.cells[0][0].accepts("PHI")) 135 } 136 137 @Test("Check accepts alternate entries") 138 func checkAcceptsAlternateEntries() throws { 139 let source = """ 140 Title: Check Test 141 Rebus: 1=PHI 142 143 144 1A 145 146 147 A1. Greek letter represented in the puzzle. ~ PHI 148 A1 ^Accept: IO 149 """ 150 151 let game = Game(puzzle: Puzzle(xd: try XD.parse(source))) 152 game.setLetter("IO", atRow: 0, atCol: 0, pencil: false) 153 game.checkCells([game.puzzle.cells[0][0]]) 154 155 #expect(game.squares[0][0].mark == .none) 156 } 157 }