Coverage for tests/test_audio_utils.py: 100%
97 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
1#!/usr/bin/env python3
3import os
4import pytest
6from unittest import TestCase
8from media_utils import empty_audio_file
9from media_utils import get_audio_file_info
10from media_utils import get_audio_duration
11from file_utils import binary_to_base64
12from file_utils import base64_to_binary
13from media_utils import chunk_audio_base64
14from media_utils import base64_to_audio_file
15from file_utils import read_file_base64
16from media_utils import fit_audio_to_duration
17from media_utils import get_aligned_duration
20class TestAudioUtils(TestCase):
21 """Test cases for the empty_audio_file function."""
23 def test_empty(self) -> None:
24 """Test creating an empty audio file."""
25 # Create an empty audio file
26 audio_path = empty_audio_file(duration_seconds=0.5)
28 # Verify file was created
29 self.assertTrue(os.path.exists(audio_path))
30 self.assertTrue(audio_path.endswith('.wav'))
32 # Verify audio file info
33 audio_info = get_audio_file_info(audio_path)
34 self.assertEqual(audio_info['duration_seconds'], 0.5)
35 self.assertEqual(audio_info['samplerate'], 44100)
36 self.assertEqual(audio_info['channels'], 1)
38 with self.assertRaises(FileNotFoundError):
39 get_audio_file_info("nofile.wav")
40 with self.assertRaises(TypeError):
41 get_audio_file_info(["list", "of", "files"]) # type: ignore[arg-type]
43 # Clean up the created file
44 os.unlink(audio_path)
46 def test_chunk(self) -> None:
47 """Test chunking audio base64 data."""
48 audio_path = empty_audio_file(duration_seconds=3.2)
49 with open(audio_path, "rb") as f:
50 audio_bytes = f.read()
51 audio_base64 = binary_to_base64(audio_bytes)
52 audio_base64 = chunk_audio_base64(
53 audio_base64,
54 start_seconds=1.0,
55 end_seconds=2.0)
56 audio_duration = get_audio_duration(audio_base64)
57 self.assertAlmostEqual(audio_duration, 1.0, delta=0.1)
59 audio_bytes = base64_to_binary(audio_base64)
60 audio_duration = get_audio_duration(audio_bytes)
61 self.assertAlmostEqual(audio_duration, 1.0, delta=0.1)
63 with self.assertRaises(TypeError):
64 chunk_audio_base64(audio_bytes) # type: ignore[arg-type]
65 with self.assertRaises(ValueError):
66 chunk_audio_base64(audio_base64, start_seconds=-1, end_seconds=0.2)
67 with self.assertRaises(ValueError):
68 chunk_audio_base64(audio_base64, start_seconds=1.0, end_seconds=5.0)
70 def test_get_aligned_duration(self) -> None:
71 VAE = 4
72 FPS = 23
73 self.assertEqual(get_aligned_duration(0.0, fps=FPS, vae=VAE), 0.0)
74 self.assertAlmostEqual(get_aligned_duration(3.000, fps=FPS, vae=VAE), 3.000, delta=0.01)
75 self.assertAlmostEqual(get_aligned_duration(3.001, fps=FPS, vae=VAE), 3.174, delta=0.01)
76 self.assertAlmostEqual(get_aligned_duration(3.001, fps=FPS, vae=VAE), 3 + (VAE / FPS), delta=0.01)
77 self.assertAlmostEqual(get_aligned_duration(4.500, fps=FPS, vae=VAE), 4.565, delta=0.01)
78 self.assertAlmostEqual(get_aligned_duration(4.565, fps=FPS, vae=VAE), 4.565, delta=0.01)
79 self.assertAlmostEqual(get_aligned_duration(4.566, fps=FPS, vae=VAE), 4.739, delta=0.01)
82@pytest.mark.asyncio
83async def test_base64() -> None:
84 """Test converting audio file to base64 and back."""
85 audio_path = empty_audio_file(duration_seconds=0.8)
86 audio_base64 = await read_file_base64(audio_path)
87 audio_duration = get_audio_duration(audio_base64)
88 assert audio_duration == 0.8
90 audio_path_2 = await base64_to_audio_file(audio_base64)
92 with pytest.raises(TypeError):
93 await read_file_base64(12345) # type: ignore[arg-type]
94 with pytest.raises(TypeError):
95 await base64_to_audio_file(12345) # type: ignore[arg-type]
96 with pytest.raises(FileNotFoundError):
97 await read_file_base64("nonexisting.wav")
99 # Clean up files
100 os.unlink(audio_path)
101 os.unlink(audio_path_2)
104@pytest.mark.asyncio
105async def test_resize_wav() -> None:
106 audio_path = "tests/data/sample.wav"
107 audio_base64 = await read_file_base64(audio_path)
108 duration_secs = get_audio_duration(audio_base64)
109 assert duration_secs == 24.025
111 audio_shorter_path = fit_audio_to_duration(audio_path, 10.0)
112 audio_shorter_base64 = await read_file_base64(audio_shorter_path)
113 duration_shorter_secs = get_audio_duration(audio_shorter_base64)
114 assert duration_shorter_secs == 10.0
116 audio_longer_path = fit_audio_to_duration(audio_shorter_path, 10.6)
117 audio_longer_base64 = await read_file_base64(audio_longer_path)
118 duration_longer_secs = get_audio_duration(audio_longer_base64)
119 assert duration_longer_secs == 10.6
121 os.unlink(audio_shorter_path)
122 os.unlink(audio_longer_path)
124 with pytest.raises(TypeError):
125 get_audio_duration(None) # type: ignore[arg-type]
126 with pytest.raises(TypeError):
127 get_audio_duration([]) # type: ignore[arg-type]
128 with pytest.raises(TypeError):
129 fit_audio_to_duration(1, 2) # type: ignore[arg-type]
130 with pytest.raises(TypeError):
131 fit_audio_to_duration(None, 1) # type: ignore[arg-type]
132 with pytest.raises(FileNotFoundError):
133 fit_audio_to_duration("nonexisting.wav", 10.6)
134 with pytest.raises(ValueError):
135 fit_audio_to_duration(audio_path, -0.2)