@@ -84,6 +84,43 @@ struct ConstrainedGenerationSessionTests {
8484 #expect( allowedTokenIDs ( session: & session) . isEmpty)
8585 }
8686
87+ @Test func fillBitmaskIntoPointerMatchesNextTokenBitmask( ) throws {
88+ var session = try createTestSession ( )
89+
90+ // Get bitmask via the array-returning method
91+ let arrayBitmask = session. nextTokenBitmask ( )
92+ #expect( arrayBitmask != nil )
93+
94+ // Reset and get bitmask via the pointer method
95+ session. reset ( )
96+ let bitmaskSize = ( session. vocabularySize + 31 ) / 32
97+ var pointerBitmask = [ Int32] ( repeating: 0 , count: bitmaskSize)
98+ let result = pointerBitmask. withUnsafeMutableBufferPointer { buf in
99+ session. fillBitmask ( into: buf. baseAddress!)
100+ }
101+ #expect( result == . constrained)
102+
103+ // They should be identical
104+ #expect( arrayBitmask!. count == pointerBitmask. count)
105+ for i in 0 ..< arrayBitmask!. count {
106+ #expect(
107+ arrayBitmask![ i] == pointerBitmask [ i] ,
108+ " Mismatch at word \( i) : array= \( arrayBitmask![ i] ) pointer= \( pointerBitmask [ i] ) " )
109+ }
110+ }
111+
112+ @Test func fillBitmaskReturnsTerminatedWhenDone( ) throws {
113+ var session = try createTestSession ( )
114+ driveToCompletion ( session: & session)
115+
116+ let bitmaskSize = ( session. vocabularySize + 31 ) / 32
117+ var buffer = [ Int32] ( repeating: 0 , count: bitmaskSize)
118+ let result = buffer. withUnsafeMutableBufferPointer { buf in
119+ session. fillBitmask ( into: buf. baseAddress!)
120+ }
121+ #expect( result == . terminated)
122+ }
123+
87124 // MARK: - Token Acceptance Tests
88125
89126 @Test func acceptToken( ) throws {
@@ -109,6 +146,64 @@ struct ConstrainedGenerationSessionTests {
109146 )
110147 }
111148
149+ // MARK: - Rollback Tests
150+
151+ @Test func rollbackRestoresPriorState( ) throws {
152+ var session = try createTestSession ( )
153+
154+ let initialAllowed = allowedTokenIDs ( session: & session)
155+
156+ // Accept "{" then rollback — should return to initial state
157+ _ = session. acceptToken ( TestConstants . openBraceToken)
158+ let afterAccept = allowedTokenIDs ( session: & session)
159+ #expect( afterAccept != initialAllowed)
160+
161+ let success = session. rollback ( 1 )
162+ #expect( success, " rollback(1) should succeed " )
163+
164+ let afterRollback = allowedTokenIDs ( session: & session)
165+ #expect( afterRollback == initialAllowed, " After rollback, allowed tokens should match initial state " )
166+ }
167+
168+ @Test func rollbackMultipleTokens( ) throws {
169+ var session = try createTestSession ( )
170+
171+ let initialAllowed = allowedTokenIDs ( session: & session)
172+
173+ // Accept "{" then "\"" then rollback both
174+ _ = session. acceptToken ( TestConstants . openBraceToken) // "{"
175+ _ = session. acceptToken ( TestConstants . quoteToken) // "\""
176+
177+ let success = session. rollback ( 2 )
178+ #expect( success)
179+
180+ let afterRollback = allowedTokenIDs ( session: & session)
181+ #expect( afterRollback == initialAllowed)
182+ }
183+
184+ // MARK: - Jump Forward Tests
185+
186+ @Test func findJumpForwardStringReturnsNilAtChoice( ) throws {
187+ var session = try createTestSession ( )
188+
189+ // At the start, multiple tokens are valid ("{") — no deterministic jump
190+ let jump = session. findJumpForwardString ( )
191+ // The grammar may or may not have a deterministic prefix at the very start.
192+ // After "{", the next required token is "\"" (to start a key), but there could
193+ // be whitespace options. Just verify the API returns without crashing.
194+ _ = jump // no assertion on value — grammar-dependent
195+ }
196+
197+ @Test func findJumpForwardStringDoesNotMutateState( ) throws {
198+ var session = try createTestSession ( )
199+
200+ let beforeAllowed = allowedTokenIDs ( session: & session)
201+ _ = session. findJumpForwardString ( )
202+ let afterAllowed = allowedTokenIDs ( session: & session)
203+
204+ #expect( beforeAllowed == afterAllowed, " findJumpForwardString should not change grammar state " )
205+ }
206+
112207 // MARK: - Reset Tests
113208
114209 @Test func reset( ) throws {
0 commit comments