import MusicAssistantCore import XCTest final class ArabicPronunciationProcessorTests: XCTestCase { private let processor = ArabicPronunciationProcessor() func testDisabledProcessingLeavesLyricsUntouched() { let lyrics = "\u{0645}\u{0651}\u{064E}\u{0631}\u{062D}\u{064E}\u{0628}\u{064B}\u{0627}" let result = processor.process( lyrics: lyrics, settings: ArabicPronunciationSettings(isEnabled: false) ) XCTAssertEqual(result.text, lyrics) XCTAssertEqual(result.notes, [.processingDisabled]) } func testProcessingNormalizesSuppliedDiacritics() { let lyrics = "\u{0645}\u{0651}\u{064E}\u{0631}\u{062D}\u{064E}\u{0628}\u{064B}\u{0627}" let result = processor.process( lyrics: lyrics, settings: ArabicPronunciationSettings( isEnabled: true, diacritizationPolicy: .pronunciationTargeted ) ) XCTAssertEqual( result.text, "\u{0645}\u{064E}\u{0651}\u{0631}\u{062D}\u{064E}\u{0628}\u{064B}\u{0627}" ) XCTAssertTrue(result.notes.isEmpty) } func testRemovingTanweenKeepsOtherDiacritics() { let result = processor.process( lyrics: "\u{0634}\u{064F}\u{0643}\u{0652}\u{0631}\u{064B}\u{0627}", settings: ArabicPronunciationSettings( isEnabled: true, tanweenPolicy: .removeWhenUnwanted ) ) XCTAssertEqual(result.text, "\u{0634}\u{064F}\u{0643}\u{0652}\u{0631}\u{0627}") } func testFullTashkeelDoesNotInventMissingMarks() { let lyrics = "\u{0645}\u{0631}\u{062D}\u{0628}\u{0627}" let result = processor.process( lyrics: lyrics, settings: ArabicPronunciationSettings( isEnabled: true, diacritizationPolicy: .fullTashkeel, tanweenPolicy: .addWhenPronunciationRequires ) ) XCTAssertEqual(result.text, lyrics) XCTAssertEqual( result.notes, [.fullTashkeelNeedsReview, .tanweenAdditionNeedsReview] ) } func testPreservedSpellingsKeepTheirTanweenWhileOtherTextIsProcessed() { let protectedSpelling = "\u{0647}\u{064F}\u{062F}\u{064B}\u{0649}" let otherText = "\u{0634}\u{064F}\u{0643}\u{0652}\u{0631}\u{064B}\u{0627}" let lyrics = "\(protectedSpelling) \(otherText) \(protectedSpelling)" let result = processor.process( lyrics: lyrics, settings: ArabicPronunciationSettings( isEnabled: true, diacritizationPolicy: .pronunciationTargeted, tanweenPolicy: .removeWhenUnwanted, preservedSpellings: [protectedSpelling] ) ) XCTAssertEqual( result.text, "\(protectedSpelling) \u{0634}\u{064F}\u{0643}\u{0652}\u{0631}\u{0627} \(protectedSpelling)" ) XCTAssertEqual(result.notes, [.preservedSpellingsProtected(count: 2)]) } func testPreservedSpellingsKeepTheirOriginalDiacriticOrder() { let preservedSpelling = "\u{0645}\u{0651}\u{064E}" let otherText = "\u{0628}\u{0651}\u{064E}" let result = processor.process( lyrics: "\(preservedSpelling) \(otherText)", settings: ArabicPronunciationSettings( isEnabled: true, diacritizationPolicy: .pronunciationTargeted, preservedSpellings: [preservedSpelling] ) ) XCTAssertEqual(result.text, "\(preservedSpelling) \u{0628}\u{064E}\u{0651}") XCTAssertEqual(result.notes, [.preservedSpellingsProtected(count: 1)]) } func testArabicDialectFixturesCoverExpectedProcessingBehavior() { XCTAssertEqual(ArabicLyricsFixtures.all.count, 4) XCTAssertEqual( Set(ArabicLyricsFixtures.all.map(\.dialectName)).count, ArabicLyricsFixtures.all.count ) for fixture in ArabicLyricsFixtures.all { let result = processor.process( lyrics: fixture.lyrics, settings: fixture.settings ) XCTAssertEqual( result.text.unicodeScalars.map(\.value), fixture.expectedLyrics.unicodeScalars.map(\.value), fixture.dialectName ) XCTAssertEqual(result.notes, fixture.expectedNotes, fixture.dialectName) } } }