Path: blob/master/src/util-tests/cue_parser_tests.cpp
4802 views
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#include "util/cue_parser.h"45#include "common/error.h"67#include <gtest/gtest.h>89// Test basic valid CUE sheet10TEST(CueParser, BasicValidCue)11{12const std::string cue = "FILE \"game.bin\" BINARY\n"13"TRACK 01 MODE2/2352\n"14"INDEX 01 00:00:00\n";1516CueParser::File cue_file;17Error error;1819ASSERT_TRUE(cue_file.Parse(cue, &error));2021const CueParser::Track* track = cue_file.GetTrack(1);22ASSERT_NE(track, nullptr);23EXPECT_EQ(track->number, 1);24EXPECT_EQ(track->mode, CueParser::TrackMode::Mode2Raw);25EXPECT_EQ(track->file, "game.bin");26EXPECT_EQ(track->file_format, CueParser::FileFormat::Binary);2728const CueParser::MSF* index1 = track->GetIndex(1);29ASSERT_NE(index1, nullptr);30EXPECT_EQ(index1->minute, 0);31EXPECT_EQ(index1->second, 0);32EXPECT_EQ(index1->frame, 0);33}3435// Test multiple tracks36TEST(CueParser, MultipleTracksCue)37{38const std::string cue = "FILE \"game.bin\" BINARY\n"39"TRACK 01 MODE1/2352\n"40"INDEX 01 00:00:00\n"41"TRACK 02 AUDIO\n"42"INDEX 00 02:30:65\n"43"INDEX 01 02:31:45\n"44"TRACK 03 MODE2/2336\n"45"INDEX 01 05:45:20\n";4647CueParser::File cue_file;48Error error;4950ASSERT_TRUE(cue_file.Parse(cue, &error));5152// Test track 153const CueParser::Track* track1 = cue_file.GetTrack(1);54ASSERT_NE(track1, nullptr);55EXPECT_EQ(track1->number, 1);56EXPECT_EQ(track1->mode, CueParser::TrackMode::Mode1Raw);5758// Test track 259const CueParser::Track* track2 = cue_file.GetTrack(2);60ASSERT_NE(track2, nullptr);61EXPECT_EQ(track2->number, 2);62EXPECT_EQ(track2->mode, CueParser::TrackMode::Audio);6364// Check track 2 indices65const CueParser::MSF* index0 = track2->GetIndex(0);66ASSERT_NE(index0, nullptr);67EXPECT_EQ(index0->minute, 2);68EXPECT_EQ(index0->second, 30);69EXPECT_EQ(index0->frame, 65);7071const CueParser::MSF* index1 = track2->GetIndex(1);72ASSERT_NE(index1, nullptr);73EXPECT_EQ(index1->minute, 2);74EXPECT_EQ(index1->second, 31);75EXPECT_EQ(index1->frame, 45);7677// Test track 378const CueParser::Track* track3 = cue_file.GetTrack(3);79ASSERT_NE(track3, nullptr);80EXPECT_EQ(track3->number, 3);81EXPECT_EQ(track3->mode, CueParser::TrackMode::Mode2);82}8384// Test different track modes85TEST(CueParser, DifferentTrackModes)86{87const std::string cue = "FILE \"audio.wav\" WAVE\n"88"TRACK 01 AUDIO\n"89"INDEX 01 00:00:00\n"90"FILE \"data.bin\" BINARY\n"91"TRACK 02 MODE1/2048\n"92"INDEX 01 00:00:00\n"93"TRACK 03 MODE1/2352\n"94"INDEX 01 15:34:50\n"95"TRACK 04 MODE2/2048\n"96"INDEX 01 30:10:00\n"97"TRACK 05 MODE2/2352\n"98"INDEX 01 45:00:00\n";99100CueParser::File cue_file;101Error error;102103ASSERT_TRUE(cue_file.Parse(cue, &error));104105// Verify track modes106const CueParser::Track* track1 = cue_file.GetTrack(1);107ASSERT_NE(track1, nullptr);108EXPECT_EQ(track1->mode, CueParser::TrackMode::Audio);109EXPECT_EQ(track1->file_format, CueParser::FileFormat::Wave);110111const CueParser::Track* track2 = cue_file.GetTrack(2);112ASSERT_NE(track2, nullptr);113EXPECT_EQ(track2->mode, CueParser::TrackMode::Mode1);114EXPECT_EQ(track2->file_format, CueParser::FileFormat::Binary);115116const CueParser::Track* track3 = cue_file.GetTrack(3);117ASSERT_NE(track3, nullptr);118EXPECT_EQ(track3->mode, CueParser::TrackMode::Mode1Raw);119120const CueParser::Track* track4 = cue_file.GetTrack(4);121ASSERT_NE(track4, nullptr);122EXPECT_EQ(track4->mode, CueParser::TrackMode::Mode2Form1);123124const CueParser::Track* track5 = cue_file.GetTrack(5);125ASSERT_NE(track5, nullptr);126EXPECT_EQ(track5->mode, CueParser::TrackMode::Mode2Raw);127}128129// Test track flags130TEST(CueParser, TrackFlags)131{132const std::string cue = "FILE \"game.bin\" BINARY\n"133"TRACK 01 AUDIO\n"134"FLAGS PRE DCP 4CH\n"135"INDEX 01 00:00:00\n"136"TRACK 02 AUDIO\n"137"FLAGS SCMS\n"138"INDEX 01 03:00:00\n";139140CueParser::File cue_file;141Error error;142143ASSERT_TRUE(cue_file.Parse(cue, &error));144145const CueParser::Track* track1 = cue_file.GetTrack(1);146ASSERT_NE(track1, nullptr);147EXPECT_TRUE(track1->HasFlag(CueParser::TrackFlag::PreEmphasis));148EXPECT_TRUE(track1->HasFlag(CueParser::TrackFlag::CopyPermitted));149EXPECT_TRUE(track1->HasFlag(CueParser::TrackFlag::FourChannelAudio));150EXPECT_FALSE(track1->HasFlag(CueParser::TrackFlag::SerialCopyManagement));151152const CueParser::Track* track2 = cue_file.GetTrack(2);153ASSERT_NE(track2, nullptr);154EXPECT_FALSE(track2->HasFlag(CueParser::TrackFlag::PreEmphasis));155EXPECT_FALSE(track2->HasFlag(CueParser::TrackFlag::CopyPermitted));156EXPECT_FALSE(track2->HasFlag(CueParser::TrackFlag::FourChannelAudio));157EXPECT_TRUE(track2->HasFlag(CueParser::TrackFlag::SerialCopyManagement));158}159160// Test PREGAP handling161TEST(CueParser, PregapHandling)162{163const std::string cue = "FILE \"game.bin\" BINARY\n"164"TRACK 01 AUDIO\n"165"INDEX 01 00:00:00\n"166"TRACK 02 AUDIO\n"167"PREGAP 00:02:00\n"168"INDEX 01 03:00:00\n";169170CueParser::File cue_file;171Error error;172173ASSERT_TRUE(cue_file.Parse(cue, &error));174175const CueParser::Track* track1 = cue_file.GetTrack(1);176ASSERT_NE(track1, nullptr);177EXPECT_FALSE(track1->zero_pregap.has_value());178179const CueParser::Track* track2 = cue_file.GetTrack(2);180ASSERT_NE(track2, nullptr);181ASSERT_TRUE(track2->zero_pregap.has_value());182EXPECT_EQ(track2->zero_pregap->minute, 0);183EXPECT_EQ(track2->zero_pregap->second, 2);184EXPECT_EQ(track2->zero_pregap->frame, 0);185}186187// Test track lengths are correctly calculated188TEST(CueParser, TrackLengthCalculation)189{190const std::string cue = "FILE \"game.bin\" BINARY\n"191"TRACK 01 AUDIO\n"192"INDEX 01 00:00:00\n"193"TRACK 02 AUDIO\n"194"INDEX 00 02:30:00\n"195"INDEX 01 02:32:00\n"196"TRACK 03 AUDIO\n"197"INDEX 01 05:45:20\n";198199CueParser::File cue_file;200Error error;201202ASSERT_TRUE(cue_file.Parse(cue, &error));203204const CueParser::Track* track1 = cue_file.GetTrack(1);205ASSERT_NE(track1, nullptr);206ASSERT_TRUE(track1->length.has_value());207// Track 1 length should be calculated to end at track 2's index 0208EXPECT_EQ(track1->length->minute, 2);209EXPECT_EQ(track1->length->second, 30);210EXPECT_EQ(track1->length->frame, 0);211212const CueParser::Track* track2 = cue_file.GetTrack(2);213ASSERT_NE(track2, nullptr);214ASSERT_TRUE(track2->length.has_value());215// Track 2 length should be calculated to end at track 3's index 1216EXPECT_EQ(track2->length->minute, 3);217EXPECT_EQ(track2->length->second, 13);218EXPECT_EQ(track2->length->frame, 20);219}220221// Test missing FILE command222TEST(CueParser, MissingFileCommand)223{224const std::string cue = "TRACK 01 AUDIO\n"225"INDEX 01 00:00:00\n";226227CueParser::File cue_file;228Error error;229ASSERT_FALSE(cue_file.Parse(cue, &error));230}231232// Test invalid track number233TEST(CueParser, InvalidTrackNumber)234{235const std::string cue = "FILE \"game.bin\" BINARY\n"236"TRACK 100 AUDIO\n"237"INDEX 01 00:00:00\n";238239CueParser::File cue_file;240Error error;241ASSERT_FALSE(cue_file.Parse(cue, &error));242}243244// Test missing index 1245TEST(CueParser, MissingIndex1)246{247const std::string cue = "FILE \"game.bin\" BINARY\n"248"TRACK 01 AUDIO\n"249"INDEX 00 00:00:00\n";250251CueParser::File cue_file;252Error error;253ASSERT_FALSE(cue_file.Parse(cue, &error));254}255256// Test invalid index number257TEST(CueParser, InvalidIndexNumber)258{259const std::string cue = "FILE \"game.bin\" BINARY\n"260"TRACK 01 AUDIO\n"261"INDEX 100 00:00:00\n";262263CueParser::File cue_file;264Error error;265ASSERT_FALSE(cue_file.Parse(cue, &error));266}267268// Test invalid MSF format269TEST(CueParser, InvalidMSFFormat)270{271const std::string cue = "FILE \"game.bin\" BINARY\n"272"TRACK 01 AUDIO\n"273"INDEX 01 00:99:00\n";274275CueParser::File cue_file;276Error error;277ASSERT_FALSE(cue_file.Parse(cue, &error));278}279280// Test duplicate index281TEST(CueParser, DuplicateIndex)282{283const std::string cue = "FILE \"game.bin\" BINARY\n"284"TRACK 01 AUDIO\n"285"INDEX 01 00:00:00\n"286"INDEX 01 00:01:00\n";287288CueParser::File cue_file;289Error error;290ASSERT_FALSE(cue_file.Parse(cue, &error));291}292293// Test reverse index order294TEST(CueParser, ReverseIndexOrder)295{296const std::string cue = "FILE \"game.bin\" BINARY\n"297"TRACK 01 AUDIO\n"298"INDEX 02 00:01:00\n"299"INDEX 01 00:02:00\n";300301CueParser::File cue_file;302Error error;303ASSERT_FALSE(cue_file.Parse(cue, &error));304}305306// Test handling of comments and whitespace307TEST(CueParser, CommentsAndWhitespace)308{309const std::string cue = "REM This is a comment\n"310" FILE \"game.bin\" BINARY \n"311"\n"312"REM Another comment\n"313" TRACK 01 MODE2/2352 \n"314" INDEX 01 00:00:00 \n";315316CueParser::File cue_file;317Error error;318319ASSERT_TRUE(cue_file.Parse(cue, &error));320321const CueParser::Track* track = cue_file.GetTrack(1);322ASSERT_NE(track, nullptr);323EXPECT_EQ(track->mode, CueParser::TrackMode::Mode2Raw);324}325326// Test handling of multiple files327TEST(CueParser, MultipleFiles)328{329const std::string cue = "FILE \"track1.bin\" BINARY\n"330"TRACK 01 MODE1/2352\n"331"INDEX 01 00:00:00\n"332"FILE \"track2.wav\" WAVE\n"333"TRACK 02 AUDIO\n"334"INDEX 01 00:00:00\n";335336CueParser::File cue_file;337Error error;338339ASSERT_TRUE(cue_file.Parse(cue, &error));340341const CueParser::Track* track1 = cue_file.GetTrack(1);342ASSERT_NE(track1, nullptr);343EXPECT_EQ(track1->file, "track1.bin");344EXPECT_EQ(track1->file_format, CueParser::FileFormat::Binary);345346const CueParser::Track* track2 = cue_file.GetTrack(2);347ASSERT_NE(track2, nullptr);348EXPECT_EQ(track2->file, "track2.wav");349EXPECT_EQ(track2->file_format, CueParser::FileFormat::Wave);350}351352// Test ignoring unsupported metadata353TEST(CueParser, IgnoreUnsupportedMetadata)354{355const std::string cue = "CATALOG 1234567890123\n"356"TITLE \"Game Title\"\n"357"PERFORMER \"Developer\"\n"358"FILE \"game.bin\" BINARY\n"359"TRACK 01 MODE2/2352\n"360"TITLE \"Track Title\"\n"361"INDEX 01 00:00:00\n";362363CueParser::File cue_file;364Error error;365366ASSERT_TRUE(cue_file.Parse(cue, &error));367368const CueParser::Track* track = cue_file.GetTrack(1);369ASSERT_NE(track, nullptr);370EXPECT_EQ(track->number, 1);371}372373374