Ctrl K
ComunidadGuías…

test_validation_clip_usb_reel.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Issue #49 : un VRAI clip USB de Sync Module dans la suite de tests.

Le défaut de #49 (chaque clip USB rejeté en silence par la validation de
ffprobe, depuis la 0.12.29) a échappé à la CI parce qu'elle n'avait que des clips
fabriqués par FFmpeg, et des tests qui simulaient la réponse de ffprobe. Ce
clip, enregistré sur une vraie clé USB, déclenche la ligne
« missing picture in access unit with size 9 » : la vraie validation doit
l'accepter, avec l'outil que la machine fournit (ffprobe, sinon ffmpeg).

Sautés sans ffprobe ni ffmpeg ; la CI Linux (job « chaîne vidéo ») installe
FFmpeg, donc y tourne."""

from __future__ import annotations

import hashlib
import subprocess
import tempfile
import unittest
from pathlib import Path

import merge_daily

CLIP = Path(__file__).parent / "fixtures" / "clip_usb_reel_neutre.mp4"
# Empreinte du clip tel que publié : un clip retouché, ou converti par une fin
# de ligne, ne prouverait plus rien.
SHA256 = "0089741365600fa231020176f3d761ce07e8f56a9dfbfde422a2ad204afd8aee"


class ClipUsbReel(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        if not CLIP.is_file():
            raise unittest.SkipTest("clip de test absent")
        if merge_daily._outil_validation_media() is None:
            raise unittest.SkipTest("ni ffprobe ni ffmpeg disponibles")
        cls.genre, cls.exe = merge_daily._outil_validation_media()

    def test_le_fichier_publie_est_intact(self):
        self.assertEqual(hashlib.sha256(CLIP.read_bytes()).hexdigest(), SHA256,
                         "le clip de test a ete modifie")

    def test_c_est_bien_un_clip_usb_a_un_seul_bloc_de_donnees(self):
        # Structure des clips USB : ftyp, moov, un seul mdat (pas fragmenté comme
        # un enregistrement du direct).
        donnees = CLIP.read_bytes()
        genres, pos = [], 0
        while pos + 8 <= len(donnees):
            taille = int.from_bytes(donnees[pos:pos + 4], "big")
            genres.append(donnees[pos + 4:pos + 8].decode("latin-1"))
            if taille < 8:
                break
            pos += taille
        self.assertEqual(genres, ["ftyp", "moov", "mdat"])
        self.assertEqual(donnees[8:12], b"isom")

    def test_la_vraie_validation_accepte_le_clip(self):
        self.assertTrue(merge_daily.valid_mp4_complet(CLIP), merge_daily.raison_refus_mp4(CLIP))

    def test_la_sortie_de_ffprobe_ne_laisse_rien_de_refusable(self):
        if self.genre != "ffprobe":
            self.skipTest("la sonde de cette machine est ffmpeg")
        resultat = subprocess.run(
            [self.exe, "-v", "error", "-select_streams", "v:0", "-count_packets",
             "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", str(CLIP)],
            capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
        self.assertEqual(resultat.returncode, 0)
        self.assertGreater(int(resultat.stdout.split()[0]), 0)
        # Selon la version de FFmpeg, la ligne de #49 est émise ou non ; dans
        # tous les cas, le filtre n'en doit rien garder. L'ancienne règle
        # (« toute ligne d'erreur = refus ») rejetait ce clip dès qu'elle l'était.
        self.assertEqual(merge_daily._erreurs_ffprobe(resultat.stderr), [])

    def test_des_copies_tronquees_du_vrai_clip_sont_refusees(self):
        donnees = CLIP.read_bytes()
        with tempfile.TemporaryDirectory(prefix="blink_clip_usb_") as dossier:
            for part in (0.5, 0.9, 0.999):
                copie = Path(dossier) / f"coupe_{int(part * 1000)}.mp4"
                copie.write_bytes(donnees[: int(len(donnees) * part)])
                with self.subTest(part=part):
                    self.assertFalse(merge_daily.valid_mp4_complet(copie))


if __name__ == "__main__":
    unittest.main()