AudioNoteRecorderTest.swift
138 lignes · 5618 octets
import XCTest @testable import Card2vcf /// Preuve de rougeur (exigence du plan `docs/plans/rattrapage-ios-et-tests.md`, Lot 2a) : /// En commentant la garde `guard !enregistrementEnCours else { throw ... }` dans /// `AudioNoteRecorder.demarrer()`, le test testDeuxDemarragesConsecutifsEchoue échoue avec : /// /// testDeuxDemarragesConsecutifsEchoue — XCTAssertThrowsError failed: did not throw an error /// /// Ce test détecte donc réellement l'absence de protection contre le double-démarrage. final class AudioNoteRecorderTest: XCTestCase { private var moteur: FakeMoteurAudioCapture! private var recorder: AudioNoteRecorder! private var tempDir: URL! override func setUp() { super.setUp() tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString) try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) moteur = FakeMoteurAudioCapture() recorder = AudioNoteRecorder(moteur: moteur, dureeMaxSecondes: 60) } override func tearDown() { super.tearDown() recorder.arreter() try? FileManager.default.removeItem(at: tempDir) } // MARK: - Démarrage / arrêt func testDeuxDemarragesConsecutifsEchoue() throws { let url1 = tempDir.appendingPathComponent("test1.wav") let url2 = tempDir.appendingPathComponent("test2.wav") try recorder.demarrer(fichierCible: url1) XCTAssertThrowsError(try recorder.demarrer(fichierCible: url2)) { error in XCTAssertEqual(error as? AudioNoteRecorderErreur, .enregistrementDejaEnCours) } } func testArreterSansDebutRetourneNil() { let resultat = recorder.arreter() XCTAssertNil(resultat, "arreter() sans demarrer() doit retourner nil") } func testEstEnCoursApresDepart() throws { let url = tempDir.appendingPathComponent("test.wav") XCTAssertFalse(recorder.estEnCours) try recorder.demarrer(fichierCible: url) XCTAssertTrue(recorder.estEnCours) recorder.arreter() XCTAssertFalse(recorder.estEnCours) } func testResultatContientChemin() throws { let url = tempDir.appendingPathComponent("note.wav") try recorder.demarrer(fichierCible: url) let resultat = recorder.arreter() XCTAssertEqual(url.path, resultat?.chemin) } func testDureeMsecPositive() throws { let url = tempDir.appendingPathComponent("duree.wav") try recorder.demarrer(fichierCible: url) let resultat = recorder.arreter() XCTAssertGreaterThanOrEqual(resultat?.dureeMsec ?? -1, 0) } // MARK: - Écriture WAV func testFichierWAVCreeSurDemarrer() throws { let url = tempDir.appendingPathComponent("sortie.wav") try recorder.demarrer(fichierCible: url) recorder.arreter() XCTAssertTrue(FileManager.default.fileExists(atPath: url.path)) let data = try Data(contentsOf: url) let riff = String(bytes: data[0..<4], encoding: .ascii) ?? "" XCTAssertEqual("RIFF", riff, "Le fichier produit doit être un WAV valide") } func testEchantillonsEcritsVersWAV() throws { let url = tempDir.appendingPathComponent("echantillons.wav") try recorder.demarrer(fichierCible: url) moteur.simulerEchantillons([100, 200, 300]) recorder.arreter() let data = try Data(contentsOf: url) // 44 octets d'en-tête + 3 échantillons × 2 octets = 50 XCTAssertEqual(50, data.count, "3 échantillons Int16 → 6 octets de données + 44 d'en-tête") } // MARK: - Transcripteur func testEchantillonsTransmisAuTranscripteur() throws { let url = tempDir.appendingPathComponent("transcription.wav") let transcripteur = FakeTranscripteurLocal() try recorder.demarrer(fichierCible: url, transcripteur: transcripteur) moteur.simulerEchantillons([10, 20, 30]) moteur.simulerEchantillons([-1, -2]) recorder.arreter() XCTAssertEqual(2, transcripteur.echantillonsRecus.count, "Deux appels simulerEchantillons → deux appels accepterEchantillons") XCTAssertEqual([10, 20, 30], transcripteur.echantillonsRecus[0]) XCTAssertEqual([-1, -2], transcripteur.echantillonsRecus[1]) } func testSansTranscripteurNePasPlanter() throws { let url = tempDir.appendingPathComponent("sans_transcripteur.wav") try recorder.demarrer(fichierCible: url, transcripteur: nil) moteur.simulerEchantillons([1, 2, 3]) let resultat = recorder.arreter() XCTAssertNotNil(resultat) } // MARK: - Arrêt automatique func testArretAutoApresDelai() throws { let recorder = AudioNoteRecorder(moteur: moteur, dureeMaxSecondes: 0.05) let url = tempDir.appendingPathComponent("auto.wav") try recorder.demarrer(fichierCible: url) XCTAssertTrue(recorder.estEnCours) RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.25)) XCTAssertFalse(recorder.estEnCours, "L'enregistrement doit s'arrêter automatiquement après dureeMaxSecondes") } // MARK: - Erreur moteur func testErreurMoteurPropagee() throws { moteur.prochainErreur = NSError(domain: "test", code: 42) let url = tempDir.appendingPathComponent("erreur.wav") XCTAssertThrowsError(try recorder.demarrer(fichierCible: url)) XCTAssertFalse(recorder.estEnCours, "estEnCours doit rester false si demarrer() échoue") } }
GitRust