crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

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 }