use cbvault::pgn::PgnWriter;
use cbvault_fixtures::TempDb;
use cbvault_fixtures::classic::{self, Builder, Tok};
use cbvault_format::cbh::Headers;
use cbvault_format::cbh::moves::GameMoves;
use cbvault_format::file::DbFile;
fn fixture(name: &str) -> TempDb {
let board = gigachess::Board::startpos();
let lines: [&[&str]; 4] = [
&["e2e4", "e7e5", "d1h5", "b8c6", "f1c4", "g8f6", "h5f7"],
&["e2e4", "e7e5", "d1h5", "g8f6", "h5e5"],
&["e2e4", "e7e5", "g1f3", "b8c6", "f1c4", "f8c5", "O-O", "g8f6", "d1e2", "O-O"],
&["e2e4", "e7e6", "e4e5", "d7d5", "e5d6"],
];
let mut b = Builder::new();
for line in lines {
let mut toks: Vec<Tok<'_>> = line.iter().map(|m| Tok::Mv(m)).collect();
toks.push(Tok::End);
b.game(&classic::move_record(0, None, None, &classic::encode(&board, &toks, 0, false)));
}
b.write(name)
}
fn export(db: &TempDb) -> Vec<String> {
let headers = Headers::open(&db.base()).expect("headers");
let entities = cbvault_format::cbh::Entities::open(&db.base()).expect("namebases");
let cbg_path = db.path(".cbg");
let cbg = DbFile::open(cbg_path.clone()).expect(".cbg");
let mut out = Vec::new();
for id in 1..=headers.records() {
let header = headers.record(id).expect("a record");
let at = u64::from(header.moves_offset());
let mut head = [0u8; 4];
cbg.read_into(at, &mut head).expect("a record head");
let size = u32::from_be_bytes([0, head[1], head[2], head[3]]) as usize;
let record = cbg.read(at, size).expect("a record");
let game = GameMoves::parse(&cbg_path, &record).expect("a record");
let mut text = Vec::new();
PgnWriter::new().write_game(&mut text, &header, &entities, &game, None).expect("PGN");
out.push(String::from_utf8(text).expect("PGN is text"));
}
out
}
#[test]
fn check_and_mate_suffixes_survive_the_split() {
let db = fixture("san-split");
let games = export(&db);
assert_eq!(games.len(), 4, "one game per shape");
let movetext = |g: &str| {
let after = g.split("\n\n").nth(1).unwrap_or("").trim();
match after.rsplit_once(' ') {
Some((moves, result)) if result == "1-0" || result == "0-1" || result == "1/2-1/2" => moves.to_owned(),
_ => after.to_owned(),
}
};
assert!(movetext(&games[0]).ends_with("Qxf7#"), "{}", movetext(&games[0]));
assert!(movetext(&games[1]).ends_with("Qxe5+"), "{}", movetext(&games[1]));
let castle = movetext(&games[2]);
assert!(castle.contains("O-O"), "{castle}");
assert!(castle.matches("O-O").count() >= 2, "{castle}");
assert!(!castle.ends_with('+') && !castle.ends_with('#'), "{castle}");
assert!(movetext(&games[3]).ends_with("exd6"), "{}", movetext(&games[3]));
}
#[test]
fn a_pass_carries_no_suffix_and_the_tree_stays_well_formed() {
let board = gigachess::Board::startpos();
let toks = [Tok::Mv("e2e4"), Tok::Mv("e7e5"), Tok::Mv("d1h5"), Tok::Mv("g8f6"), Tok::Mv("--"), Tok::End];
let mut b = Builder::new();
b.game(&classic::move_record(0, None, None, &classic::encode(&board, &toks, 0, false)));
let db = b.write("san-split-null");
let games = export(&db);
let movetext = games[0].trim_end().lines().last().unwrap_or_default().to_owned();
assert!(movetext.contains("--"), "{movetext}");
assert!(!movetext.contains("--+") && !movetext.contains("--#"), "{movetext}");
}