EcritureWavTest.swift
138 lignes · 6022 octets
import XCTest @testable import Card2vcf /// Preuve de rougeur (exigence du plan `docs/plans/rattrapage-ios-et-tests.md`, Lot 2a) : /// En neutralisant l'écriture de l'en-tête (en commentant `handle.seek(toFileOffset: 0)` + /// `handle.write(entete)` dans `EcritureWav.fermer()`), les tests échouent avec : /// /// testEnteteRIFFValide — XCTAssertEqual failed: ("RIFF") is not equal to (" ") /// testSampleRateEt16Bits — XCTAssertEqual failed: ("16000") is not equal to ("0") /// /// (Les 44 premiers octets restent à zéro ; les octets nuls sont rendus comme espaces /// par la décodification ASCII, le sampleRate lu sur 4 octets nuls vaut 0.) final class EcritureWavTest: XCTestCase { private var tempUrl: URL! override func setUp() { super.setUp() tempUrl = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString) .appendingPathExtension("wav") } override func tearDown() { super.tearDown() try? FileManager.default.removeItem(at: tempUrl) } // MARK: - En-tête RIFF func testEnteteRIFFValide() throws { let ecrivain = try EcritureWav(url: tempUrl) try ecrivain.fermer() let data = try Data(contentsOf: tempUrl) XCTAssertGreaterThanOrEqual(data.count, 44, "Fichier doit faire au moins 44 octets") let riff = String(bytes: data[0..<4], encoding: .ascii) ?? "" XCTAssertEqual("RIFF", riff, "Les 4 premiers octets doivent être 'RIFF'") let wave = String(bytes: data[8..<12], encoding: .ascii) ?? "" XCTAssertEqual("WAVE", wave, "Les octets 8-11 doivent être 'WAVE'") } func testChunkFmtPresent() throws { let ecrivain = try EcritureWav(url: tempUrl) try ecrivain.fermer() let data = try Data(contentsOf: tempUrl) let fmt = String(bytes: data[12..<16], encoding: .ascii) ?? "" XCTAssertEqual("fmt ", fmt, "Les octets 12-15 doivent être 'fmt '") let fmtSize = data[16...19].withUnsafeBytes { $0.load(as: Int32.self).littleEndian } XCTAssertEqual(16, fmtSize, "taille du chunk fmt doit être 16 (PCM)") let audioFormat = data[20...21].withUnsafeBytes { $0.load(as: Int16.self).littleEndian } XCTAssertEqual(1, audioFormat, "audioFormat doit être 1 (PCM linéaire)") } func testSampleRateEt16Bits() throws { let ecrivain = try EcritureWav(url: tempUrl) try ecrivain.fermer() let data = try Data(contentsOf: tempUrl) // sampleRate aux octets 24-27 let sampleRate = data[24...27].withUnsafeBytes { $0.load(as: Int32.self).littleEndian } XCTAssertEqual(16_000, sampleRate, "sampleRate doit être 16 000 Hz") // canaux aux octets 22-23 let canaux = data[22...23].withUnsafeBytes { $0.load(as: Int16.self).littleEndian } XCTAssertEqual(1, canaux, "doit être mono (1 canal)") // bitsParEchantillon aux octets 34-35 let bits = data[34...35].withUnsafeBytes { $0.load(as: Int16.self).littleEndian } XCTAssertEqual(16, bits, "doit être 16 bits par échantillon") } // MARK: - Taille cohérente avec les données func testTailleBloc_data_CohérenteAvecEchantillons() throws { let ecrivain = try EcritureWav(url: tempUrl) let echantillons: [Int16] = [0, 100, -100, 32767, -32768] ecrivain.ecrireEchantillons(echantillons) try ecrivain.fermer() let data = try Data(contentsOf: tempUrl) // tag "data" aux octets 36-39 let dataTag = String(bytes: data[36..<40], encoding: .ascii) ?? "" XCTAssertEqual("data", dataTag) // taille du bloc data aux octets 40-43 let tailleData = data[40...43].withUnsafeBytes { $0.load(as: Int32.self).littleEndian } XCTAssertEqual(Int32(echantillons.count * 2), tailleData, "taille data = nb échantillons × 2 octets") // taille totale du fichier XCTAssertEqual(44 + echantillons.count * 2, data.count) } func testRIFFSizeCohérente() throws { let echantillons = [Int16](repeating: 42, count: 100) let ecrivain = try EcritureWav(url: tempUrl) ecrivain.ecrireEchantillons(echantillons) try ecrivain.fermer() let data = try Data(contentsOf: tempUrl) // riffSize aux octets 4-7 = taille totale - 8 let riffSize = data[4...7].withUnsafeBytes { $0.load(as: Int32.self).littleEndian } let attendu = Int32(36 + echantillons.count * 2) XCTAssertEqual(attendu, riffSize, "riffSize doit valoir totalOctets - 8") } func testByteRateEtBlockAlign() throws { // byteRate = sampleRate × canaux × (bits/8) = 16000 × 1 × 2 = 32000 // blockAlign = canaux × (bits/8) = 1 × 2 = 2 let ecrivain = try EcritureWav(url: tempUrl) try ecrivain.fermer() let data = try Data(contentsOf: tempUrl) let byteRate = data[28...31].withUnsafeBytes { $0.load(as: Int32.self).littleEndian } XCTAssertEqual(32_000, byteRate) let blockAlign = data[32...33].withUnsafeBytes { $0.load(as: Int16.self).littleEndian } XCTAssertEqual(2, blockAlign) } // MARK: - Cas limite func testFichierVideSansEchantillons() throws { let ecrivain = try EcritureWav(url: tempUrl) let octets = try ecrivain.fermer() XCTAssertEqual(0, octets) let data = try Data(contentsOf: tempUrl) XCTAssertEqual(44, data.count, "Fichier vide = 44 octets d'en-tête uniquement") } func testEchantillonsPCMEncodésLittleEndian() throws { let ecrivain = try EcritureWav(url: tempUrl) // 0x0100 en little-endian s'écrit [0x00, 0x01] ecrivain.ecrireEchantillons([0x0100]) try ecrivain.fermer() let data = try Data(contentsOf: tempUrl) XCTAssertEqual(0x00, data[44], "octet bas d'abord (little-endian)") XCTAssertEqual(0x01, data[45], "octet haut ensuite") } }
GitRust