1mod analyser;
2mod error;
3mod frames;
4mod id3v1;
5mod id3v2;
6mod utils;
7
8pub use analyser::Analyser;
9pub use error::{Error, Result};
10pub use frames::types::AudioFrame;
11pub use id3v1::ID3V1Tag;
12pub use id3v2::ID3V2Tag;
13
14#[cfg(test)]
15mod tests {
16 use crate::{
17 analyser::Analyser,
18 error::{Error, Result},
19 frames::utils::{compute_frame_index, get_frame_duration_from_header, get_offset_for_time},
20 };
21 use std::time::{Duration, Instant};
22 use tokio::{fs::File, io::BufReader, join};
23
24 async fn get_analyser(path: &str) -> Result<Analyser> {
25 let file = match File::open(path).await {
26 Ok(file) => file,
27 Err(_) => return Err(Error::Io),
28 };
29
30 let reader = BufReader::new(file);
31 let analyser = Analyser::new(reader);
32
33 Ok(analyser)
34 }
35
36 #[tokio::test]
37 async fn get_cbr320_audio_frames() {
38 let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
39 let audio_frames = analyser.get_audio_frames().await;
40 assert!(audio_frames.is_ok());
41 assert!(audio_frames.unwrap().len() > 0);
42 }
43
44 #[tokio::test]
45 async fn check_cbr320_xing_header() {
46 let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
47 let audio_frames = analyser.get_audio_frames().await.unwrap();
48 let xing = audio_frames
49 .iter()
50 .find(|frame| frame.xing_header.is_some())
51 .unwrap()
52 .xing_header
53 .as_ref()
54 .unwrap();
55
56 assert!(audio_frames.len().abs_diff(xing.frames.unwrap() as usize) < 10);
57 }
58
59 #[tokio::test]
60 async fn get_apev2_audio_frames() {
61 let analyser = get_analyser("test/APEv2.mp3").await.unwrap();
62 let audio_frames = analyser.get_audio_frames().await;
63 assert!(audio_frames.is_ok());
64 assert!(audio_frames.unwrap().len() > 0);
65 }
66
67 #[tokio::test]
68 async fn get_id3v1_audio_frames() {
69 let analyser = get_analyser("test/ID3v1.mp3").await.unwrap();
70 let audio_frames = analyser.get_audio_frames().await;
71 assert!(audio_frames.is_ok());
72 assert!(audio_frames.unwrap().len() > 0);
73 }
74
75 #[tokio::test]
76 async fn get_id3v2_audio_frames() {
77 let analyser = get_analyser("test/ID3v2.mp3").await.unwrap();
78 let audio_frames = analyser.get_audio_frames().await;
79 assert!(audio_frames.is_ok());
80 assert!(audio_frames.unwrap().len() > 0);
81 }
82
83 #[tokio::test]
84 async fn get_id3v2_with_bad_padding_audio_frames() {
85 let analyser = get_analyser("test/ID3v2WithBadPadding.mp3").await.unwrap();
86 let audio_frames = analyser.get_audio_frames().await;
87 assert!(audio_frames.is_ok());
88 assert!(audio_frames.unwrap().len() > 0);
89 }
90
91 #[tokio::test]
92 async fn get_id3v2_with_image_audio_frames() {
93 let analyser = get_analyser("test/ID3v2WithImage.mp3").await.unwrap();
94 let audio_frames = analyser.get_audio_frames().await;
95 assert!(audio_frames.is_ok());
96 assert!(audio_frames.unwrap().len() > 0);
97 }
98
99 #[tokio::test]
100 async fn get_sine_empty_id3_audio_frames() {
101 let analyser = get_analyser("test/SineEmptyID3.mp3").await.unwrap();
102 let audio_frames = analyser.get_audio_frames().await;
103 assert!(audio_frames.is_ok());
104 assert!(audio_frames.unwrap().len() > 0);
105 }
106
107 #[tokio::test]
108 async fn get_source_audio_frames() {
109 let analyser = get_analyser("test/source.mp3").await.unwrap();
110 let audio_frames = analyser.get_audio_frames().await;
111 assert!(audio_frames.is_ok());
112 assert!(audio_frames.unwrap().len() > 0);
113 }
114
115 #[tokio::test]
116 async fn get_truncated_audio_frames() {
117 let analyser = get_analyser("test/Truncated.mp3").await.unwrap();
118 let audio_frames = analyser.get_audio_frames().await;
119 assert!(audio_frames.is_ok());
120 assert!(audio_frames.unwrap().len() > 0);
121 }
122
123 #[tokio::test]
124 async fn get_vbr9_audio_frames() {
125 let analyser = get_analyser("test/VBR9.mp3").await.unwrap();
126 let audio_frames = analyser.get_audio_frames().await;
127 assert!(audio_frames.is_ok());
128 assert!(audio_frames.unwrap().len() > 0);
129 }
130
131 #[tokio::test]
132 async fn get_vbr0_audio_frames() {
133 let analyser = get_analyser("test/VBR0.mp3").await.unwrap();
134 let audio_frames = analyser.get_audio_frames().await;
135 assert!(audio_frames.is_ok());
136 assert!(audio_frames.unwrap().len() > 0);
137 }
138
139 #[tokio::test]
140 async fn compare_frame_count() {
141 let source_analyser = get_analyser("test/source.mp3").await.unwrap();
142 let id3v1_analyser = get_analyser("test/ID3v1.mp3").await.unwrap();
143 let id3v2_analyser = get_analyser("test/ID3v2.mp3").await.unwrap();
144 let cbr320_analyser = get_analyser("test/CBR320.mp3").await.unwrap();
145
146 let results = join!(
147 source_analyser.get_audio_frames(),
148 id3v1_analyser.get_audio_frames(),
149 id3v2_analyser.get_audio_frames(),
150 cbr320_analyser.get_audio_frames()
151 );
152
153 let source_audio_frames = results.0.unwrap();
154 let id3v1_audio_frames = results.1.unwrap();
155 let id3v2_audio_frames = results.2.unwrap();
156 let cbr320_audio_frames = results.3.unwrap();
157
158 assert!(source_audio_frames.len().abs_diff(id3v1_audio_frames.len()) < 5);
159 assert!(source_audio_frames.len().abs_diff(id3v2_audio_frames.len()) < 5);
160 assert!(
161 source_audio_frames
162 .len()
163 .abs_diff(cbr320_audio_frames.len())
164 < 5
165 );
166 }
167
168 #[tokio::test]
169 async fn get_xing_duration() {
170 let cbr320_analyser = get_analyser("test/CBR320.mp3").await.unwrap();
171 let source_analyser = get_analyser("test/source.mp3").await.unwrap();
172
173 let cbr320_start = Instant::now();
174 let cbr320_duration = cbr320_analyser.get_duration().await.unwrap();
175 let cbr320_elapsed_time = cbr320_start.elapsed();
176
177 let source_start = Instant::now();
178 let source_duration = source_analyser.get_duration().await.unwrap();
179 let source_elapsed_time = source_start.elapsed();
180
181 assert!(cbr320_elapsed_time < source_elapsed_time);
182 assert!(
183 cbr320_duration
184 .as_secs()
185 .abs_diff(source_duration.as_secs())
186 < 1
187 );
188 }
189
190 #[tokio::test]
191 async fn test_frame_index() {
192 let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
193 let audio_frames = analyser.get_audio_frames().await.unwrap();
194 let frame_index = compute_frame_index(&audio_frames);
195
196 let mut time_1 = Duration::from_secs(0);
197 let mut time_2 = Duration::from_secs(1);
198 let mut time_3 = Duration::from_secs(2);
199
200 let mut offset_1 = 0;
201 let mut offset_2 = 0;
202 let mut offset_3 = 0;
203
204 let mut time = Duration::from_secs(0);
205 let mut offset = 0;
206 for (index, frame) in audio_frames.iter().enumerate() {
207 if index == 0 {
208 time_1 = time;
209 offset_1 = offset;
210 }
211
212 if index == 10 {
213 time_2 = time;
214 offset_2 = offset;
215 }
216
217 if index == 65 {
218 time_3 = time;
219 offset_3 = offset;
220 }
221
222 time += get_frame_duration_from_header(&frame.header);
223 offset = frame.offset;
224
225 if index > 65 {
226 break;
227 }
228 }
229
230 let frame_offset_1 = get_offset_for_time(&frame_index, time_1);
231 let frame_offset_2 = get_offset_for_time(&frame_index, time_2);
232 let frame_offset_3 = get_offset_for_time(&frame_index, time_3);
233
234 assert!(frame_offset_1 == offset_1);
235 assert!(frame_offset_2 == offset_2);
236 assert!(frame_offset_3 == offset_3);
237 }
238}