Summary - Added fastparse_3:3.0.2 dependency to modules/io - Implemented FenParserFastParse as a second alternative FEN parser using FastParse, with the same public API as FenParser and FenParserCombinators - Parsers are built bottom-up using (using P[Any]) Scala 3 syntax with NoWhitespace.* to prevent implicit whitespace skipping; rank sum validation uses Pass/Fail inside .flatMap - Added FenParserFastParseTest mirroring FenParserCombinatorsTest to prove behavioural equivalence across all three implementations Test plan - All existing tests pass — FenParser, FenParserCombinators, and all other modules untouched - FenParserFastParseTest covers all cases: valid FEN, invalid color, invalid castling, invalid board shapes, en passant, rank overflow, round-trip via FenExporter - All parser logic branches genuinely covered — known scoverage gap documented in docs/unresolved.md (FastParse inline macro generates synthetic proxy methods that scoverage instruments but that never execute at runtime) Co-authored-by: LQ63 <lkhermann@web.de> Reviewed-on: #22 Reviewed-by: Janis <janis-e@gmx.de> Co-authored-by: Leon Hermann <lq@blackhole.local> Co-committed-by: Leon Hermann <lq@blackhole.local>
This commit was merged in pull request #22.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package de.nowchess.io.fen
|
||||
|
||||
import de.nowchess.api.board.*
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class FenParserFastParseTest extends AnyFunSuite with Matchers:
|
||||
|
||||
test("parseBoard parses canonical positions and supports round-trip"):
|
||||
val initial = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
|
||||
val empty = "8/8/8/8/8/8/8/8"
|
||||
val partial = "8/8/4k3/8/4K3/8/8/8"
|
||||
|
||||
FenParserFastParse.parseBoard(initial).map(_.pieceAt(Square(File.E, Rank.R2))) shouldBe Some(Some(Piece.WhitePawn))
|
||||
FenParserFastParse.parseBoard(initial).map(_.pieceAt(Square(File.E, Rank.R8))) shouldBe Some(Some(Piece.BlackKing))
|
||||
FenParserFastParse.parseBoard(empty).map(_.pieces.size) shouldBe Some(0)
|
||||
FenParserFastParse.parseBoard(partial).map(_.pieceAt(Square(File.E, Rank.R6))) shouldBe Some(Some(Piece.BlackKing))
|
||||
|
||||
FenParserFastParse.parseBoard(initial).map(FenExporter.boardToFen) shouldBe Some(initial)
|
||||
FenParserFastParse.parseBoard(empty).map(FenExporter.boardToFen) shouldBe Some(empty)
|
||||
|
||||
test("parseFen parses full state for common valid inputs"):
|
||||
FenParserFastParse.parseFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").fold(_ => fail(), ctx =>
|
||||
ctx.turn shouldBe Color.White
|
||||
ctx.castlingRights.whiteKingSide shouldBe true
|
||||
ctx.enPassantSquare shouldBe None
|
||||
ctx.halfMoveClock shouldBe 0
|
||||
)
|
||||
|
||||
FenParserFastParse.parseFen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1").fold(_ => fail(), ctx =>
|
||||
ctx.turn shouldBe Color.Black
|
||||
ctx.enPassantSquare shouldBe Some(Square(File.E, Rank.R3))
|
||||
)
|
||||
|
||||
FenParserFastParse.parseFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1").fold(_ => fail(), ctx =>
|
||||
ctx.castlingRights.whiteKingSide shouldBe false
|
||||
ctx.castlingRights.blackQueenSide shouldBe false
|
||||
)
|
||||
|
||||
test("parseFen rejects invalid color and castling tokens"):
|
||||
FenParserFastParse.parseFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR x KQkq - 0 1").isLeft shouldBe true
|
||||
FenParserFastParse.parseFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w XYZ - 0 1").isLeft shouldBe true
|
||||
|
||||
test("importGameContext returns Right for valid and Left for invalid FEN"):
|
||||
val fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
||||
FenParserFastParse.importGameContext(fen).isRight shouldBe true
|
||||
FenParserFastParse.importGameContext("invalid fen string").isLeft shouldBe true
|
||||
|
||||
test("parseBoard rejects malformed board shapes and invalid piece symbols"):
|
||||
FenParserFastParse.parseBoard("8/8/8/8/8/8/8") shouldBe None
|
||||
FenParserFastParse.parseBoard("9/8/8/8/8/8/8/8") shouldBe None
|
||||
FenParserFastParse.parseBoard("8p/8/8/8/8/8/8/8") shouldBe None
|
||||
FenParserFastParse.parseBoard("7/8/8/8/8/8/8/8") shouldBe None
|
||||
FenParserFastParse.parseBoard("8/8/8/8/8/8/8/7X") shouldBe None
|
||||
|
||||
test("parseBoard rejects ranks that overflow via multiple tokens"):
|
||||
FenParserFastParse.parseBoard("p8/8/8/8/8/8/8/8") shouldBe None
|
||||
FenParserFastParse.parseBoard("8pp/8/8/8/8/8/8/8") shouldBe None
|
||||
Reference in New Issue
Block a user